3

私のクエリの目的は、性別= fであり、campid=xxxxであるテーブルbにユーザー名が存在しないテーブルaからすべての行を取得することです。これが私が成功して使用しているクエリです:

SELECT `id` 
FROM pool 
  LEFT JOIN sent 
    ON  pool.username = sent.username 
    AND sent.campid = 'YA1LGfh9' 
WHERE sent.username IS NULL 
  AND pool.gender = 'f'

問題は、クエリの完了に9分以上かかり、プールテーブルに1,000万を超える行が含まれ、送信されたテーブルが最終的にそれよりも大きくなることです。ユーザー名や性別など、多くの列のインデックスを作成しました。ただし、MySQLはこのクエリに私のインデックスを使用することを拒否します。FORCEINDEXを使ってみました。プールからのインデックスとクエリのEXPLAINの出力は次のとおりです。

+-------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+
| Table | Non_unique | Key_name | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment |
+-------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+
| pool  |          0 | PRIMARY  |            1 | id          | A         |     9326880 |     NULL | NULL   |      | BTREE      |         |
| pool  |          1 | username |            1 | username    | A         |     9326880 |     NULL | NULL   |      | BTREE      |         |
| pool  |          1 | source   |            1 | source      | A         |           6 |     NULL | NULL   |      | BTREE      |         |
| pool  |          1 | gender   |            1 | gender      | A         |           9 |     NULL | NULL   |      | BTREE      |         |
| pool  |          1 | location |            1 | location    | A         |       59030 |     NULL | NULL   |      | BTREE      |         |
+-------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+
6 rows in set (0.00 sec)

mysql> explain SELECT `id` FROM pool FORCE INDEX (username) LEFT JOIN sent ON pool.username = sent.username AND sent.campid = 'YA1LGfh9' WHERE sent.username IS NULL AND pool.gender = 'f';
+----+-------------+-------+------+---------------+------+---------+------+---------+-------------------------+
| id | select_type | table | type | possible_keys | key  | key_len | ref  | rows    | Extra                   |
+----+-------------+-------+------+---------------+------+---------+------+---------+-------------------------+
|  1 | SIMPLE      | pool  | ALL  | NULL          | NULL | NULL    | NULL | 9326881 | Using where             |
|  1 | SIMPLE      | sent  | ALL  | NULL          | NULL | NULL    | NULL |     351 | Using where; Not exists |
+----+-------------+-------+------+---------------+------+---------+------+---------+-------------------------+
2 rows in set (0.00 sec)

また、送信されたテーブルのインデックスは次のとおりです。

+-------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+
| Table | Non_unique | Key_name | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment |
+-------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+
| sent  |          0 | PRIMARY  |            1 | primary_key | A         |         351 |     NULL | NULL   |      | BTREE      |         |
| sent  |          1 | username |            1 | username    | A         |         351 |     NULL | NULL   |      | BTREE      |         |
+-------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+
2 rows in set (0.00 sec)

インデックスが使用されていないことがわかります。そのため、クエリに非常に時間がかかります。クエリのやり直しを伴う解決策がある場合は、実装とテストの方法に混乱が生じないように、データ構造を使用してそれを行う方法の例を教えてください。ありがとうございました。

4

1 に答える 1

4

まず、キャンプを含むすべてのものの配置において、元のクエリは正しかった。プールから送信にLEFTJOINを使用し、前述のように「CAMP」などの必要な等式をWHERE句にプルすると、最終的にそれがINNER JOINに変換されるため、両側での入力が必要になります。そのままにしておきます。

送信されたテーブルにユーザー名のインデックスが既にありますが、次のようにします。

(CampID、UserName)の「送信済み」テーブルに複合(つまり、複数のキー)インデックスとしてインデックスを作成します。このようにして、左結合は両方のエントリに対して最適化されます。

「ビリヤード台」で、(gender、username、id)の3つのフィールドで複合インデックスを試してください。

これを行うことで、1,000万件以上のレコードを含むデータの実際のページをすべて調べる必要がないという利点を活用できます。インデックスには比較用の列があるため、実際のレコードを見つけて列を確認する必要はなく、インデックスの列を直接使用できます。

また、ニヤリと笑うために、キーワード「STRAIGHT_JOIN」を追加しました。これは、MySQLに、表示されているとおりにクエリを実行するように指示し、私のために考えようとはしません。多くの場合、これによりクエリのパフォーマンスが大幅に向上することがわかりました...役に立たなかったというフィードバックを受け取った人はほとんどいません。

SELECT STRAIGHT_JOIN
      p.id
   FROM 
      pool p
         LEFT JOIN sent s
            ON s.campid = 'YA1LGfh9' 
            AND p.username = s.username 
   WHERE 
          p.gender = 'f'
      AND s.username IS NULL 

とはいえ、プールに1,000万以上あり、単一のキャンプに5,000しかない場合は、1,000万以上のレコードからいくつのレコードを返すことになります。あなたはまだほとんどすべてのセットを返すでしょう。

于 2012-06-11T11:11:01.987 に答える