4

通常は 1 秒未満で実行されるクエリを使用していますが、完了するまでに 10 ~ 40 秒かかる場合があります。サブクエリがどのように機能するかについては、実際には完全に明確ではありません。faverprofileid ごとに 15 行が得られるという点で、それが機能することだけはわかっています。

遅いクエリをログに記録すると、5823244 行が検査されたことがわかります。これは、関連するテーブルのいずれにもそれほど多くの行がないため、奇妙です (お気に入りテーブルには 50,000 行が最も多くあります)。

誰かが私にいくつかの指針を提供できますか? サブクエリの問題であり、ファイルソートを使用する必要がありますか?

編集: Explain を実行すると、 users テーブルがインデックスを使用していないことが示されます (id が主キーであっても)。余分な下には、次のように書かれています:一時的な使用。ファイルソートの使用。

SELECT F.id,F.created,U.username,U.fullname,U.id,I.*   
FROM favorites AS F  
INNER JOIN users AS U ON F.faver_profile_id = U.id  
INNER JOIN items AS I ON F.notice_id = I.id  
WHERE faver_profile_id IN (360,379,95,315,278,1)  
AND F.removed = 0  
AND I.removed = 0   
AND F.collection_id is null   
AND I.nudity = 0  
AND (SELECT COUNT(*) FROM favorites WHERE faver_profile_id = F.faver_profile_id  
AND created > F.created AND removed = 0 AND collection_id is null) < 15 
ORDER BY F.faver_profile_id, F.created DESC;
4

5 に答える 5

6

多くの行が複数回検査されているため、検査された行の数は多くなっています。 これは、不適切に最適化されたクエリ プランが原因で、インデックス ルックアップを実行する必要があるときにテーブル スキャンが発生するためです。この場合、検査される行数は指数関数的です。つまり、複数のテーブルの合計行数の積に匹敵する大きさのオーダーです。

SELECT F.id,F.created,U.username,U.fullname,U.id,I.*
FROM favorites AS F FORCE INDEX (faver_profile_id_key)
INNER JOIN users AS U FORCE INDEX FOR JOIN (PRIMARY) ON F.faver_profile_id = U.id
INNER JOIN items AS I FORCE INDEX FOR JOIN (PRIMARY) ON F.notice_id = I.id
WHERE faver_profile_id IN (360,379,95,315,278,1)
AND F.removed = 0
AND I.removed = 0
AND F.collection_id is null
AND I.nudity = 0
AND (SELECT COUNT(*) FROM favorites FORCE INDEX (faver_profile_id_key) WHERE faver_profile_id = F.faver_profile_id
AND created > F.created AND removed = 0 AND collection_id is null) < 15
ORDER BY F.faver_profile_id, F.created DESC;

で提案されているように、ネストされたサブクエリの代わりにGROUP BY faver_profile_id/を使用するようにクエリを変更することもできます。元のクエリと のクエリの両方のパフォーマンスは、たとえばヒントを使用して適切に最適化されている場合に匹敵するはずです (クエリはネストされたインデックス ルックアップを使用しますが、のクエリはハッシュベースの戦略を使用します)。HAVING count > 15SELECT COUNT(*)vartecvartecvartec

于 2009-02-26T08:17:21.077 に答える
5

私はと思うしGROUP BYHAVINGそれはより速いはずです。それはあなたが望むものですか?

SELECT F.id,F.created,U.username,U.fullname,U.id, I.field1, I.field2, count(*) as CNT
FROM favorites AS F  
INNER JOIN users AS U ON F.faver_profile_id = U.id  
INNER JOIN items AS I ON F.notice_id = I.id  
WHERE faver_profile_id IN (360,379,95,315,278,1)  
AND F.removed = 0  
AND I.removed = 0   
AND F.collection_id is null   
AND I.nudity = 0  
GROUP BY F.id,F.created,U.username,U.fullname,U.id,I.field1, I.field2
HAVING CNT < 15
ORDER BY F.faver_profile_id, F.created DESC;

必要なフィールドがわからないitemsので、プレースホルダーを配置しました。

于 2009-02-26T08:21:26.247 に答える
0

そのクエリの結果は、ページ化されたリストとして表示されることを意図していると思います。その場合、おそらく、より単純な「結合されていないクエリ」を実行し、各行に対して 2 番目のクエリを実行して、表示されている 15、20、または 30 個の要素のみを読み取ることを検討できます。JOIN は重い操作ではありませんでしたか? これにより、クエリが簡素化され、結合されたテーブルが大きくなっても遅くなりません。

私が間違っているか教えてください。

于 2009-02-26T09:44:53.067 に答える
0

各 ID でループを実行し、count(*) サブクエリの代わりに limit を使用できます。

foreach $id in [123,456,789]:
    SELECT
     F.id,
     F.created,
     U.username,
     U.fullname,
     U.id,
     I.*
    FROM
     favorites AS F INNER JOIN
     users AS U ON F.faver_profile_id = U.id INNER JOIN
     items AS I ON F.notice_id = I.id
    WHERE
     F.faver_profile_id = {$id} AND
     I.removed = 0 AND
     I.nudity = 0 AND
     F.removed = 0 AND
     F.collection_id is null
    ORDER BY
     F.faver_profile_id,
     F.created DESC
    LIMIT
     15;
于 2009-02-26T08:42:44.383 に答える