3

私は何年も前に構築したチャット サイトのクエリを持っていますが、大量のトラフィックが原因で、クエリの設計が不十分であることがわかりました。ここに、長いクエリ ログの例を示します。

SELECT  DISTINCT user.id 
FROM    user 
        STRAIGHT_JOIN user_pics 
            ON user.id=user_pics.uid      
        STRAIGHT_JOIN user_account 
            ON user_account.user_id=user.id 
WHERE   registered = 1  AND 
        user.id<>0 AND 
        user.id<>23847 AND 
        user.id<>12392... (IT HAS LITERALLY 1000 OF THESE)
        AND user_pics.main=1 AND 
        user_pics.approved=1 AND 
        user_pics.deleted<>1 AND 
        gender LIKE '%female%' AND 
        country LIKE '%United Kingdom%' AND 
        city LIKE '%birmingham%' AND 
        sexorientation     LIKE '%Straight%' 
ORDER   BY updatedate DESC 
LIMIT   20;

クエリの実行には約 15 秒かかります。参照するすべての列にもインデックスを付けました。1000 個の "AND user.id<>0" マークを一時テーブルへのルックアップに置き換えると、クエリが改善されます。行って変更を加える前に尋ねると思いました。コードの有用な変更を推奨できる場合は、非常に感謝しています。

編集: "user.id<>23847" マークは、単純な選択によって php で作成され、次に foreach 配列ループによって大きな SQL クエリに追加されます。

EDIT 2:「not in」を使用することで、クエリが13秒から0.3秒に短縮されました。

4

6 に答える 6

5

EXPLAIN
http://dev.mysql.com/doc/refman/5.0/en/explain.html
を使用してみてください。

user.id NOT IN(23847 ,0 , 23847 ,...) これは、NOT IN() を使用して置き換えることができます

于 2013-02-26T09:26:02.427 に答える
1

はい、使用する場合は、

user.id NOT IN (SELECT id FROM idExemptTable)

これは、各 ID を個別にチェックするよりもはるかに高速です。

INステートメントに関する詳細を説明する別の回答へのリンクを含めました:-

SQL: SELECT IN はより高速でベスト プラクティスですか?

国名の確認にワイルドカード一致と LIKE を使用している理由もわかりません。

于 2013-02-26T09:23:05.063 に答える
1

遅い not-equals-to を使用する代わりに、トリックを使用します: 選択しないものを選択し、 を使用して元のテーブルに結合し、LEFT JOINフィルタリングを介して残りのみを取得します。

SELECT DISTINCT user.id 
FROM user 
STRAIGHT_JOIN user_pics ON user.id=user_pics.uid
STRAIGHT_JOIN user_account ON user_account.user_id=user.id 
LEFT OUTER JOIN 
 (SELECT u.id from user u where u.id in (0,23847, 12397 ... ... ...)) as notToBeIncluded ON user.id=notToBeIncluded.id -- the users that are to be excluded
WHERE registered=1  
AND notToBeIncluded.id IS NULL --this is the important part.
AND user_pics.main=1 
AND user_pics.approved=1 
AND user_pics.deleted<>1 
AND gender LIKE '%female%' 
AND country LIKE '%United Kingdom%' 
AND city LIKE '%birmingham%' 
AND sexorientation     LIKE '%Straight%' 
ORDER BY updatedate DESC LIMIT 20;

編集私はなんてばかです...あなたが持っている不要なIDは別のクエリの結果であるとさえ言いました! この場合、PHP で結果を取得しないでください。クエリで直接使用してください。そうすれば確実に速くなります。

そう:

  • その結果を処理する foreach とともに他のクエリを削除します
  • メインクエリを書き直す

(マークダウンで正しくフォーマットするには、ここに文字列が必要です)

SELECT DISTINCT user.id 
FROM user 
STRAIGHT_JOIN user_pics ON user.id=user_pics.uid
STRAIGHT_JOIN user_account ON user_account.user_id=user.id 
LEFT OUTER JOIN 
 (SELECT u.id from user <[ your other query here ]> ) as notToBeIncluded ON user.id=notToBeIncluded.id -- the users that are to be excluded
WHERE registered=1  
AND notToBeIncluded.id IS NULL --this is the important part.
--( conditions removed for brewity)
ORDER BY updatedate DESC LIMIT 20;

その他の推奨事項:

  • 性別や向きなどの列に varchar を使用しても役に立ちません。いくつかの助けになる数値を使用してください。
  • また、インデックスを確認してください。誰かが実行計画の使用を提案しました。これは、特定のクエリのパフォーマンスのボトルネックを見つけようとする場合の究極の方法です。
于 2013-02-26T09:27:10.523 に答える
0

ppeterkasソリューションのマイナーなバリエーション。必要のないユーザーが一時テーブルを使用することを前提としています。また、LIKEをストレートイコールに変更します(フィールドに値の組み合わせが格納されているかどうかに応じて、フラグまたはビット文字列を使用することをお勧めします)。

SELECT  DISTINCT user.id 
FROM    user 
        STRAIGHT_JOIN user_pics ON user.id=user_pics.uid      
        STRAIGHT_JOIN user_account ON user_account.user_id=user.id 
        LEFT OUTER JOIN tmp_users_to_ignore ON user.id = tmp_users_to_ignore.id
WHERE   registered = 1   
AND     tmp_users_to_ignore.id IS NULL 
AND     user_pics.main=1  
AND     user_pics.approved=1  
AND     user_pics.deleted<>1  
AND     gender = 'female'  
AND     country = 'United Kingdom'  
AND     city = 'birmingham'  
AND     sexorientation     = 'Straight' 
ORDER   BY updatedate DESC 
LIMIT   20;
于 2013-02-26T09:48:50.980 に答える
0

そうか :

編集:「user.id <> 23847」マークは、単純な選択とforeach配列ループによってphpで作成され、より大きなSQLクエリに追加されます。

では、なぜサブクエリを作成しないのでしょうか。

最初のクエリがであると想像してみましょうSELECT * FROM user WHERE yourcondition。すべてのデータを返す必要がありますか?いいえの場合は、2番目のクエリで実行してください。

SELECT DISTINCT user.id 
FROM user 
STRAIGHT_JOIN user_pics ON user.id=user_pics.uid
STRAIGHT_JOIN user_account ON user_account.user_id=user.id 
WHERE registered=1  
AND user.id NOT IN(SELECT id FROM user WHERE yourcondition)
AND user_pics.main=1 
AND user_pics.approved=1 
AND user_pics.deleted<>1 
AND gender ='female' 
AND country LIKE '%United Kingdom%' 

AND city LIKE'%birmingham%' AND sexorientation LIKE'%Straight%' ORDER BY updatedate DESC LIMIT 20;

于 2013-02-26T09:49:31.510 に答える
0

ユーザー テーブルに追加のフィールドを追加し、インデックスを作成します。のすべてのユーザーの値を 1 に設定します。

user.id<>0 AND 
user.id<>23847 AND 
user.id<>12392...

他のすべてのユーザーの場合は 0 になります。

次に、上記のクエリでこのフィールドでフィルター処理します。

于 2013-02-26T09:24:26.490 に答える