0

処理に時間がかかる MySQL クエリを最適化しようとしています。users テーブルと purchase テーブルの 2 つのテーブルがあるとします。両方のテーブルには、約 20,000 行あります。

mysql> 
SELECT NOW(),u.id
    FROM users u
    LEFT JOIN purchases p
        ON p.user_id = u.id
    WHERE
        p.website_id = 1234
    ORDER BY u.total_paid DESC
    LIMIT 10;
+---------------------+-------+
| NOW()               | id    |
+---------------------+-------+
*snip*
+---------------------+-------+
10 rows in set (0.06 sec)

超高速ではありませんが、かなり機敏です。に変更する以外に何も変更u.idしないu.*と、劇的に遅くなります:

mysql>
SELECT NOW(),u.*
    FROM users u
    LEFT JOIN purchases p
        ON p.user_id = u.id
    WHERE
        p.website_id = 1234
    ORDER BY u.total_paid DESC
    LIMIT 10;
+---------------------+-------+
*snip*
+---------------------+-------+
10 rows in set (0.37 sec)

「まあ、絶対に使用しないでください」と言う前にselect *、追加するフィールドが増えるほど、その時間までゆっくりと忍び寄ることを考慮してください。つまり、選択するフィールドの半分に名前を付けると、クエリが約 0.20 秒で実行され、フィールドにはフィールドがありません。 users テーブルが より大きいですvarchar(255)

ただし、比較的きびきびしたクエリから ID を取得すると、単純に次のようになります。

mysql>
SELECT *
    FROM users
    WHERE id IN (*snip*);
+---------------------+-------+
*snip*
+---------------------+-------+
10 rows in set (0.01 sec)

したがって、私の 2 つのクエリ: select u.idplusselect u.* where id inは、同様のクエリであると想定するものよりも高速です。一体何?

詳細情報: users テーブルには約 30 のフィールドがあります。繰り返しますが、どのフィールドもvarchar(255)

詳細情報:両方のクエリの EXPLAIN は次のとおりです。

*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: p
         type: ref
possible_keys: PRIMARY,user_id_index,website_id_index,website_user_id_index,website_created_index,website_type_created_index,website_type_index,purchase_user_id_type_index,user_id_website_id_index,website_id_user_id_index
          key: website_id_user_id_index
      key_len: 9
          ref: const
         rows: 9976
        Extra: Using where; Using index; Using temporary; Using filesort
*************************** 2. row ***************************
           id: 1
  select_type: SIMPLE
        table: u
         type: eq_ref
possible_keys: PRIMARY
          key: PRIMARY
      key_len: 8
          ref: database.p.user_id
         rows: 1
        Extra:

編集一時/ファイルソートを使用しているため、ユーザーから * を選択する必要があり、最終的な結果セットにどの行が含まれるかがわからない可能性がありますか? 些細な余分なデータのように見えるかもしれませんが、実際には、テーブルの大きなチャンクを選択することの違いですか? これが正しい場合、何か提案はありますか?

4

1 に答える 1

2

まず、質問/部分的な回答をお願いします。あなたは本当に何を求めていますか。purchases テーブルへの LEFT-JOIN がありますが、特定の "purchase" Web サイト ID に対する WHERE 句があります。これは本質的に、クエリを INNER JOIN に持ち込み、問題のサイトから購入したユーザーのみを返すことです。そうは言っても、クエリを次のように書き直したでしょう

select 
      NOW(),
      u.id 
   from 
      purchases p
         JOIN users u 
            ON p.user_id = u.id
   where 
      p.website_id = 1234 
   order by 
      u.total_paid desc 
   limit 
      10;

(Website_ID)にインデックスがあると仮定すると、これは最初に購入から始まり、ユーザーに参加しますが、Web サイト 1234 での購入のみです。これも、1 人のユーザーが同じサイトから複数回購入した場合に何が起こるかとして、誤った回答を与える可能性があります。そして、彼らはトップ バイヤーの 1 人でした...彼らの ID は何度も表示される可能性があります。これを防ぐために、サイトから DISTINCT ユーザーを事前にクエリし、ユーザーに参加します。(Website_ID, user_ID) の purchases テーブルにインデックスを作成し、次の操作を行います。

select 
      NOW(),
      u.id 
   from 
      ( select distinct p.user_id
           from purchases p
           where p.website_id = 1234 ) PQ
         JOIN users u 
            ON PQ.user_id = u.id
   order by 
      u.total_paid desc 
   limit 
      10;
于 2013-09-14T01:29:54.790 に答える