1

rating30 万行弱のテーブルと SQL クエリがあります。

  SELECT rt1.product_id as id1, rt2.product_id as id2, sum(1), sum(rt1.rate-rt2.rate) as sum 
FROM rating as rt1 
JOIN rating as rt2 ON rt1.user_id = rt2.user_id AND rt1.product_id != rt2.product_id 
group by rt1.product_id, rt2.product_id
LIMIT 1

問題は..本当に遅いです。で実行するのに 36 秒かかりますがlimit 1、無制限に実行する必要があります。私が理解したように、一部が原因で速度が低下しましたGROUP BY。テーブルrt1またはrt2に関係なく、1列でグループ化している間は正常に機能します。インデックスも試してみました.user_id、product_id、rate、および(user_id、product_id)のインデックスをすでに作成しています。

EXPLAIN私にもあまり教えてくれません。

 id     select_type     table   type    possible_keys   key     key_len     ref     rows    Extra
1   SIMPLE  rt1     ALL     PRIMARY,user_id,user_product    NULL    NULL    NULL    289700  Using temporary; Using filesort
1   SIMPLE  rt2     ref     PRIMARY,user_id,user_product    user_id     4   mgrshop.rt1.user_id     30  Using where

これを一度だけ実行してデータを生成する必要があるため、最適な時間を達成することは重要ではありませんが、合理的です。

何か案は?

編集。

完全なテーブル スキーマ

CREATE TABLE IF NOT EXISTS `rating` (
  `user_id` int(11) NOT NULL,
  `product_id` int(11) NOT NULL,
  `rate` int(11) NOT NULL,
  PRIMARY KEY (`user_id`,`product_id`),
  KEY `user_id` (`user_id`),
  KEY `product_id` (`product_id`),
  KEY `user_product` (`user_id`,`product_id`),
  KEY `rate` (`rate`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
4

4 に答える 4

0

Declan_K がクロス結合の結果セットについて言及したことに加えて、それはあなたが気付く前に 100k 行になる可能性があります。

rt1.product_id < rt2.product_id

それ以外の

rt1.product_id != rt2.product_id

理由... これらは同じテーブル/レコードであるため、RT1.product_ID に対して 1 回循環するだけで済みます。最高値よりも低いため、比較の一部としてすでに最高値を取得しています. 現状では、(1 人のユーザーに対して) 5 つの製品 (1-5) がある場合、次の結果が得られます。

(1,2)  (1,3)  (1,4)  (1,5)
(2,1)  (2,3)  (2,4)  (2,5)
(3,1)  (3,2)  (3,4)  (3,5)
(4,1)  (4,2)  (4,3)  (4,5)
(5,1)  (5,2)  (5,3)  (5,4)

LESS than に変更することで、1,2 対 2,1 1,3 対 3,1 などの重複を排除できます。

(1,2)  (1,3)  (1,4)  (1,5)
       (2,3)  (2,4)  (2,5)
              (3,4)  (3,5)
                     (4,5)

少し小さい結果セットで、これは 1 人あたり 5 つの製品のみです。

于 2013-05-09T19:28:43.737 に答える
0

私の解決策は最も簡単ではありませんが、少し説明してクエリ時間を短縮する必要があります。

MySQL に参加すると、一時テーブルが作成されます。その一時テーブルに入れられる行が多いほど、ディスクに移動する可能性が高くなります。ディスクが遅い。新しい一時テーブルにはインデックスがありません。インデックスなしのクエリは遅くなります。

EXPLAIN ステートメントの最初の行は、クエリが最初に結合し、一連の行を作成し、それを一時テーブルに貼り付け、製品 ID でグループ化することを示しています。列は空で、keyキーを使用できないことを示しています。

私の解決策は、別のテーブルを作成することです。この他のテーブルは、JOIN の関連するすべての列で構成されます。バックグラウンドでテーブルを更新するには、バッチ ジョブが必要です。これにより、データが少し古くなりますが、はるかに高速に実行されます。

CREATE TABLE `rate_tmp` (
  userid ...,
  id1 ...,
  id2 ...,
  rate1 ...,
  rate2 ...,
  PRIMARY KEY (id1, id2, userid)
)

主キーの順序は非常に重要です。クエリは次のようになります。

SELECT userid, id1, id2, sum(1), sum(rate1-rate2) as sum
from rate_tmp
group by id1, id2;

その時点で非常に高速に実行されるはずです。これは、テーブルがまだディスクに永続化されている間、MySQL がクエリ時にデータをディスクに書き込む必要がないためです。また、さらに重要なことに、一時テーブルにある事前定義されたインデックスを使用できます。

于 2013-05-09T20:15:55.103 に答える