我有一个MySQL表,其中约有60万行(Engine:InnoDB)。 MySQL在运行Ubuntu 16.04 LTS的虚拟机中运行。如果相关,MySQL服务器版本为5.7.23。
WHERE子句中的列(open_time
和close_time
)均已建立索引,并且均为DATETIME列。
我将(体积)的总和作为一栏。
此查询立即返回(0.000秒):
SELECT *
FROM klines
WHERE (open_time between '2018-01-01 00:00:00' AND '2018-01-01 12:00:00')
;
这一次要花几乎一秒钟的时间(在10次尝试之间在0.640到0.703秒之间变化):
SELECT SUM(volume)
FROM klines
WHERE open_time >= '2018-01-01 00:00:00' AND close_time <= '2018-01-01 12:00:00'
;
请注意,两个查询都返回大约相同的行(第一行返回720,第二行返回721。第二个查询返回相同的720行,第一个返回,另加另一个)。
因此,如果我只想获取行,那么我将WHERE子句用于两列还是一列都没有关系。但是,如果我想获取列的总和,当我对两列使用WHERE子句时,查询的速度将大大降低。但是,如果我只使用一列,它会立即再次返回。
虽然我完全可以使用在两个open_time条件之间查询表的查询,但是我真的很好奇发生了什么。
那么,这背后的原因是什么?
答案 0 :(得分:1)
open_time between '2018-01-01 00:00:00'
AND '2018-01-01 12:00:00'
可以轻松地使用INDEX(open_time)
仅触摸有趣的行。但这不可能使索引突然停止:
open_time >= '2018-01-01 00:00:00'
AND close_time <= '2018-01-01 12:00:00'
INDEX(open_time)
可以使用,但是将扫描表的后半部分。类似地,INDEX(close_time)
将扫描表的前半部分。现在有两种方法都可以。
您可能还有一个其他地方都看不到的约束:
这些不能在标准SQL中指定,也没有任何索引公式可以利用这两个约束。
这里有两行会弄乱任何优化尝试:
INSERT INTO klines (open_time, close_time)
VALUES ('2018-01-01 06:00:00', '2037-12-31'),
('1971-01-01', '2018-01-01 06:00:00')
('2037-01-01', '1971-01-01')
有一些修复程序,但是它们要求要么假设不重叠,要么处理查询是很严格的方法;或玩水桶。