2

これは私の質問です:

SELECT B.RECORDID, A.ITEMCODE, A.ITEMNAME, A.STOCKINHAND, B.SALEPRICE 
FROM ITEMMASTER A, STOCKENTRY B 
WHERE A.ITEMID = B.ITEMID 
  AND RECORDID = (SELECT MAX(RECORDID) FROM STOCKENTRY 
                  WHERE ITEMID = A.ITEMID) 
  AND A.STOCKINHAND > 0 
  AND B.SALEPRICE > 0 
  AND B.INVOICEDATE IS NOT NULL 
ORDER BY A.ITEMNAME, B.INVOICEDATE;

テーブルB(StockEntry)には1つ以上のレコードが含まれる場合がありますが、テーブルA(ItemMaster)にはそのItemIDの行が1つしかないことは間違いありません。

WHERE句のサブクエリを削除すると、1つ以上の行が表示されます。WHERE句のサブクエリでmax(RecordID)を選択すると、クエリが遅くなるように感じます。RecordID、InvoiceDate、ItemIDにインデックスがありますが、それでもMySQLログはこのクエリがうまく機能していないことを示しています。なんらかの理由で列の順序を変更できません

このクエリを最適化するためのより良い方法はありますか?

4

5 に答える 5

6

外部クエリのすべての行に対して相関サブクエリを実行しているため、処理が遅くなる可能性があります。より効率的に実行される傾向がある2つのソリューションがあります。

1つは、サブクエリを使用する派生テーブルを使用することですが、派生テーブルを準備するためにサブクエリを1回だけ実行します。

SELECT B.RECORDID, A.ITEMCODE, A.ITEMNAME, A.STOCKINHAND, B.SALEPRICE 
FROM ITEMMASTER A
JOIN STOCKENTRY B ON A.ITEMID = B.ITEMID
JOIN (SELECT ITEMID, MAX(RECORDID) AS MAXRECORDID 
      FROM STOCKENTRY GROUP BY ITEMID) M
  ON (M.ITEMID, M.MAXRECORDID) = (B.ITEMID, B.RECORDID)
WHERE A.STOCKINHAND > 0 
  AND B.SALEPRICE > 0 
  AND B.INVOICEDATE IS NOT NULL 
ORDER BY A.ITEMNAME, B.INVOICEDATE;

もう1つの解決策は、除外結合を使用してBの行を検索し、同じitemidとより大きなrecordidを持つ他の行が存在しないようにすることです。正しいインデックス(たとえば、(ITEMID、RECORDID)の複合インデックス)を使用すると、これは非常にうまく機能するはずです。

SELECT B.RECORDID, A.ITEMCODE, A.ITEMNAME, A.STOCKINHAND, B.SALEPRICE 
FROM ITEMMASTER A
JOIN STOCKENTRY B ON A.ITEMID = B.ITEMID 
LEFT OUTER JOIN STOCKENTRY B2
  ON B.ITEMID = B2.ITEMID AND B.RECORDID < B2.RECORDID
WHERE B2.ITEMID IS NULL 
  AND A.STOCKINHAND > 0 
  AND B.SALEPRICE > 0 
  AND B.INVOICEDATE IS NOT NULL 
ORDER BY A.ITEMNAME, B.INVOICEDATE;

このタイプの問題は、StackOverflowで頻繁に発生します。他のケースを確認できるように、グループごとに最大のタグを質問に追加しました。


@RPKのコメントについて:

私はMySQLQBを自分で使用していません。そのアプリは何度も変更されており、使用方法についてアドバイスすることはできません。しかし、mysqlモニター(コマンドライン)では、EXPLAINPROFILINGの組み合わせを使用して統計を取得します。

ただし、インデックスを変更(または作成)しないことについてコメントしました。それはあなたの最適化の試みを妨げるでしょう。

于 2011-08-05T15:22:09.860 に答える
1

TOPデータベース固有です。MySQLの代替を使用することをお勧めしますORDER BY ... DESC LIMIT 1

このSO投稿LIMITには、データベース全体に概念を実装するためのさまざまな方法の概要が記載されています。

于 2011-08-05T15:22:25.973 に答える
1

TOP 1 ... ORDER BY .. DESC次のように、を使用してみてください。

SELECT B.RECORDID, A.ITEMCODE, A.ITEMNAME, A.STOCKINHAND, B.SALEPRICE 
FROM ITEMMASTER A, STOCKENTRY B 
WHERE A.ITEMID = B.ITEMID 
  AND RECORDID = (SELECT top 1 RECORDID FROM STOCKENTRY 
                  WHERE ITEMID = A.ITEMID
                  order by RECORDID desc) 
  AND A.STOCKINHAND > 0 
  AND B.SALEPRICE > 0 
  AND B.INVOICEDATE IS NOT NULL 
ORDER BY A.ITEMNAME, B.INVOICEDATE;
于 2011-08-05T15:14:42.147 に答える
1

私の提案はビューを作成することです

CREATE VIEW `STOCKENTRY_V` AS 
SELECT ITEMID,MAX(RECORDID) AS RECORDID
FROM STOCKENTRY
GROUP BY ITEMID;

また、2つのテーブルとビューで単純な結合を行うことができます。私はそれがどれほど速く実行されるかに興味があります。

SELECT B.RECORDID, A.ITEMCODE, A.ITEMNAME, A.STOCKINHAND, B.SALEPRICE 
FROM ITEMMASTER A, STOCKENTRY B, STOCKENTRY_V C
WHERE A.ITEMID = B.ITEMID AND AND B.ITEMID = C.ITEMID
  AND B.RECORDID = C.RECORDID
  AND A.STOCKINHAND > 0 
  AND B.SALEPRICE > 0 
  AND B.INVOICEDATE IS NOT NULL 
ORDER BY A.ITEMNAME, B.INVOICEDATE;
于 2011-08-05T15:37:40.930 に答える
1

クエリが頻繁に使用され、それでもパフォーマンスが問題になる場合は、アイテムの最後のレコードIDのテーブルを作成し、ITEMMASTERテーブルのトリガーを使用して最新の状態に保つことができます。

于 2011-08-05T15:56:02.147 に答える