4

我在 MySQL 中有一个非常大的表(约 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

然而,可以预见的是,这个查询需要永远运行。我认为这是因为我要求 MySQL 遍历 HugeFileTable 5 次,每次对每个文件执行 DATEDIFF() 计算。

相反,我想做的是只逐条记录一次 HugeFileTable 记录,并为每个文件增加相应 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   
4

3 回答 3

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;

这是EXPLAIN我在 MySQL 5.0.75 上尝试此查询时得到的计划(为简洁起见,已编辑):

+-------+-------+------------------+----------------------------------------------+
| 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计划没有显示临时表或文件排序(至少我的测试表中的行数很少):

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

在您的数据集上尝试此查询,看看它是否表现更好。

于 2009-05-02T01:56:40.620 回答
1

好吧,首先确保它file_last_access是table的索引HugeFileTable

我不确定这是否可能\更好,但首先尝试计算日期限制(从日期A到日期B的文件),然后使用带有 >= 和 <= 的查询。至少从理论上讲,它会提高性能。

比较将类似于:

 t.file_last_access >= StartDate AND t.file_last_access <= EndDate 
于 2009-05-01T18:17:12.830 回答
0

您可以通过删除 CURDATE() 并在查询中放置一个日期来获得一个小的改进,因为它将在您的 SQL 中为每一行运行此函数两​​次。

于 2009-05-01T22:43:44.327 回答