MySQL集約クエリの最適化
-
03-07-2019 - |
質問
MySQLには、ファイルに関する情報を含む非常に大きなテーブル(最大1億件のレコード)があります。情報の1つは、各ファイルの変更日です。
指定した日付範囲に収まるファイルの数をカウントするクエリを作成する必要があります。そのために、これらの範囲を指定する小さなテーブルを作成し(すべて日単位)、次のようにします。
DateRanges
range_id range_name range_start range_end
1 0-90 0 90
2 91-180 91 180
3 181-365 181 365
4 366-1095 366 1095
5 1096+ 1096 999999999
次のようなクエリを作成しました:
SELECT r.range_name, sum(IF((DATEDIFF(CURDATE(),t.file_last_access) > r.range_start and DATEDIFF(CURDATE(),t.file_last_access) < r.range_end),1,0)) as FileCount
FROM `DateRanges` r, `HugeFileTable` t
GROUP BY r.range_name
ただし、予想どおり、このクエリの実行には永遠に時間がかかります。これは、各ファイルでDATEDIFF()計算を実行するたびに、HugeFileTableを5回実行するようにMySQLに要求しているためだと思います。
代わりにやりたいことは、HugeFileTableレコードをレコードごとに1回だけ通過し、各ファイルについて、適切なrange_nameの合計をカウントすることです。その方法がわかりません。...
誰でもこれを手助けできますか?
ありがとう。
編集:MySQLバージョン:5.0.45、テーブルはMyISAMです
EDIT2 :コメントで求められた説明を次に示します
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE r ALL NULL NULL NULL NULL 5 Using temporary; Using filesort
1 SIMPLE t ALL NULL NULL NULL NULL 96506321
解決
最初に、HugeFileTable.file_last_access
にインデックスを作成します。
次に、次のクエリを試してください:
SELECT r.range_name, COUNT(t.file_last_access) as FileCount
FROM `DateRanges` r
JOIN `HugeFileTable` t
ON (t.file_last_access BETWEEN
CURDATE() + INTERVAL r.range_start DAY AND
CURDATE() + INTERVAL r.range_end DAY)
GROUP BY r.range_name;
MySQL 5.0.75でこのクエリを試したときに得たEXPLAIN
プランです(簡潔にするために編集しました):
+-------+-------+------------------+----------------------------------------------+
| table | type | key | Extra |
+-------+-------+------------------+----------------------------------------------+
| t | index | file_last_access | Using index; Using temporary; Using filesort |
| r | ALL | NULL | Using where |
+-------+-------+------------------+----------------------------------------------+
まだ十分なパフォーマンスが得られません。 GROUP BY
を使用すると、クエリに一時テーブルが発生します。それについてあなたができることはあまりありません。
ただし、少なくともこのクエリでは、元のクエリにあったデカルト積が削除されます。
更新:相関サブクエリを使用する別のクエリを次に示しますが、<=>を削除しました。
SELECT r.range_name,
(SELECT COUNT(*)
FROM `HugeFileTable` t
WHERE t.file_last_access BETWEEN
CURDATE() - INTERVAL r.range_end DAY AND
CURDATE() - INTERVAL r.range_start DAY
) as FileCount
FROM `DateRanges` r;
<=>プランには、一時テーブルまたはファイルソートが表示されません(少なくとも、テストテーブルにある些細な行):
+----+--------------------+-------+-------+------------------+--------------------------+
| id | select_type | table | type | key | Extra |
+----+--------------------+-------+-------+------------------+--------------------------+
| 1 | PRIMARY | r | ALL | NULL | |
| 2 | DEPENDENT SUBQUERY | t | index | file_last_access | Using where; Using index |
+----+--------------------+-------+-------+------------------+--------------------------+
データセットでこのクエリを実行し、パフォーマンスが向上するかどうかを確認します。
他のヒント
さて、file_last_access
がテーブルHugeFileTable
のインデックスであることを確認することから始めます。
これが可能かどうかはわかりませんが、最初に日付の制限(日付 A から日付 B までのファイル)を計算してから、 <!> gt; =および<!> lt; =を使用したクエリ。少なくとも理論的には、パフォーマンスが向上します。
比較は次のようになります:
t.file_last_access >= StartDate AND t.file_last_access <= EndDate
CURDATE()を削除し、SQLの各行に対してこの関数を2回実行するため、クエリに日付を入れることで、少し改善できます。