我试图加快一个慢速报表查询,该查询从内部查询中获取日期列表,并使用外部查询中的子查询根据每个日期聚合各种数据(例如下面的例子)。
这些子查询表有DATETIME列,所以我使用BETWEEN来避免使用DATE()调用,这将禁止使用任何索引。
但是,如果内部查询返回多个日期,那么DATETIME字段上的索引似乎会被忽略。
MySQL版本:5.6.16
要重新创建(简化):
- 创建一个包含日期时间列的表
CREATE TABLE testtable (
id INT UNSIGNED NOT NULL AUTO_INCREMENT,
createddate DATETIME NULL,
value INTEGER DEFAULT 0,
PRIMARY KEY (id));
- 为日期时间添加索引
ALTER TABLE testtable
ADD INDEX dateIndex (createddate ASC);
- 插入一些数据
insert into testtable(createddate,value)
values ('2014-08-08 12:45:56',5),('2014-08-09 10:22:12',32),('2014-08-09 06:36:31',465);
- 出错的地方:根据内部查询中的日期(多个日期)对值求和
explain
select
(select sum(value)
from testtable
where createddate between temp.tdate and date_add(temp.tdate,interval 1 day)) as value from
(select '2014-08-08' as tdate union select '2014-08-09' as tdate) temp;
- EXPLAIN结果(仅适用于相关表格)
- #id,select_type,table,type,possible_keys,key,key_len,ref,rows,Extra - ' 2','相关提示',' testtable','所有',' dateIndex' ,NULL ,NULL,NULL,' 3',' 为每条记录检查范围(索引图:0x2)'
它可以看到索引但不使用它;它需要检查范围,但我必须使用BETWEEN,因为我将DATETIME与日期进行比较(使用 WHERE DATE(createddate)= temp.tdate 将不允许使用索引)
- 但是如果我在内部查询/表中只有一行
,则使用索引 explain
select
(select sum(value)
from testtable
where createddate between temp.tdate and date_add(temp.tdate,interval 1 day)) as value
from (select '2014-08-08' as tdate) temp;
- EXPLAIN结果 - #id,select_type,table,type,possible_keys,key,key_len,ref,rows,Extra - ' 2','依赖子信息' testtable','范围',' dateIndex ',' dateIndex ',' 6',NULL,' 1','使用索引条件'
我当前的解决方案选项是将DATETIME拆分为单独的DATE和TIME列,以便我可以使用WHERE createddate = temp.tdate
而不是BETWEEN:在这种情况下,索引使用正常,但需要进行大量其他更改在主要代码中。
我熟悉基本的索引使用,但这个让我的小脑袋迷糊了。任何指针赞赏!
答案 0 :(得分:0)
问题是查询优化器愿意做一次索引范围查询,但是它不是多次执行索引范围查询,而是认为通过索引表完全通过一次并检查每一行是否更有效完整的日期范围 -
这是查询优化器的常用启发式方法,主要是因为优化器无法判断子查询供应中的每个日期范围是否都不会回馈索引中的大部分或全部数据表
有时您可以通过确保您的表统计信息是最新的来使优化器变得更聪明,但有时优化器不会受到影响。
您可能会看到是否可以重新构建查询以使其更适合于优化程序,可能是:
指定单个日期范围,然后按范围内的每个日期进行分组。
重复每个子范围的整个查询并取结果的并集