2009-05-01 8 views
4

MySQLのファイルに関する情報を含む非常に大きなテーブル(〜100Millionレコード)があります。情報の1つは、各ファイルの修正日です。MySQL集計クエリの最適化

指定した日付範囲に収まるファイルの数をカウントするクエリを作成する必要があります。

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 

を、このクエリは永遠に実行されます。私は、毎回HugeFileTableを5回実行するようにMySQLに依頼しているので、毎回DATEDIFF()計算を各ファイルで実行するためだと思います。

私が代わりにやりたいのは、レコードでHugeFileTableレコードを1回だけ調べ、ファイルごとに適切なrange_nameの合計をインクリメントすることです。私はそれを行う方法を理解することはできません....

誰もこれで助けることができますか?

ありがとうございました。

EDIT: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 
+0

使用しているMySQLのバージョンは? –

+0

DESCRIBEをクエリの前で実行して、出力が得られるので、ここに改善が必要なものがあるかどうかを確認できます – tristanbailey

答えて

4

まず、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を使用すると、クエリで一時テーブルが作成されるため、高価になる可能性があります。あなたはあまりそれについて行うことはできません。

しかし、少なくともこのクエリは、元のクエリで持っていたデカルト積を排除します。


更新:ここで相関サブクエリを使用していますが、私は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; 

EXPLAIN計画は、(少なくとも行の些細な量で、私は私のテストテーブルを持っている)は、一時テーブルまたはfilesortレコードを示しています

+----+--------------------+-------+-------+------------------+--------------------------+ 
| id | select_type  | table | type | key    | Extra     | 
+----+--------------------+-------+-------+------------------+--------------------------+ 
| 1 | PRIMARY   | r  | ALL | NULL    |       | 
| 2 | DEPENDENT SUBQUERY | t  | index | file_last_access | Using where; Using index | 
+----+--------------------+-------+-------+------------------+--------------------------+ 

データセットにこのクエリを試してみてどうかを確認しますそれはより良い実行します。

+0

ありがとう、病気を試して、コメントを更新してください。 – Zenshai

+0

私はインデックスなしでクエリを実行しました(実際には必要ない場合は作成したくありませんでした)、9182秒(2.5時間)で完了しました。これは実際にはかなり受け入れられています。 1週間に1回程度。だから、ありがとうございました。 – Zenshai

+0

さて、それが受け入れられる限り、あなたの呼び出しです。私は比較のためにインデックスでそれを試してみることをお勧めします。それほど速くない場合は、そのテスト後にいつでもインデックスを削除できます。 –

1

にするために頼まれたdescibeだ、それを確認することによって開始しますfile_last_accessは、テーブルHugeFileTableインデックスです。

私はこれが良い\可能であるかどうかわからないんだけど、(日付Bに日付からファイルを)最初の日付の限界を計算してみてください、そして> =と< =でいくつかのクエリを使用しています。理論的には少なくともパフォーマンスを改善します。それはあなたのSQLで二回行ごとに、この機能を実行するよう

t.file_last_access >= StartDate AND t.file_last_access <= EndDate 
+0

ご回答ありがとうございます。私はこれが性能を非常に向上させるとは思っていませんが、それは1日の比較を取りますが、すべての減速がどこから来ているのかは分かりません。 また、私はその列をインデックスにすることはできませんが、どうにか役立つのか分かりません。 – Zenshai

+0

@zenshai 索引(Bツリー)を使用すると、mysqlが必要な範囲外のfile_last_access値に対して本質的に早期にベールできるようになり、クエリの処理速度が向上します。 インデックスがないと、O(N)比較のテーブルスキャンが実行されます.Nはテーブル内の行数です。インデックスでは、O(M)を実行します。ここで、Mは一致する行の数であり、M <= Nです。 – mattkemp

+0

@mattkemp:ああ、あなたは何を言っているのか分かりません。アジズ問題は、これらの巨大なテーブルにインデックスを作成するには時間がかかり、多くのスペースを消費することです。最後のアクセス日だけでなく、変更された日付と作成日も同じクエリを実行する必要があるため、あまりにも。 – Zenshai

0

あなたは()CURDATEを除去することにより、小さな改善を得ることができ、クエリで日付を入れて:

比較は何かのようになります。