DATE字段上的INDEX和MONTH()YEAR()函数

时间:2016-10-26 01:04:54

标签: mysql

是否仍然正确日期/日期时间列上的索引未针对YEAR(col),MONTH(col)函数进行优化? Bill Karwin给出了一个非常确定的答案here,但由于这是十年前我想检查的。我原以为是因为日期栏被描述为

  

打包为YYYY×16×32 + MM×32 + DD

的三字节整数

它可以以这样一种方式编制索引:它可以像执行tight index scan时的多列索引一样进行优化。

这种优化仍然不存在吗?为什么不可能呢?

2 个答案:

答案 0 :(得分:6)

正确。一旦WHERE子句中的列包含函数,MySQL优化器就不会使用索引。简单的推理是,一旦你在列my_date上有一个索引,当你实际上在寻找不同的值时,数据库就没有意义在其中搜索,例如YEAR(my_date)的输出。

虽然好消息是在某些情况下有一个快速而简单的解决方案。

例如,可以使用不同的方法优化以下查询:

SELECT flight_company, count(*)
FROM flight_times
WHERE year(FlightDate) = 2017
group by flight_company

除了在WHERE子句中使用YEAR()函数之外,您可以使用范围条件替换函数调用,该范围条件将检查相同的内容:

SELECT flight_company, count(*) 
FROM flight_times
WHERE FlightDate between '2017-01-01'
and '2018-01-01'
GROUP BY flight_company

虽然有些功能不能简单地替换为范围条件,例如在YEAR的情况下。例如,如何用条件替换dayofweek()?可能更难。 因此,另一种方法是使用MySQL 5.7 Virtual (generated) columns。如果你采用这种方法,你可以在CREATE TABLE语句中创建这个虚拟列,以实际匹配dayofweek()的结果:

Flight_dayofweek tinyint(4)
 GENERATED ALWAYS AS (dayofweek(FlightDate
 VIRTUAL

作为EverSQL的联合创始人,我将谦虚地建议您使用EverSQL SQL Query Optimizer自动优化此类查询。

答案 1 :(得分:4)

问题不在于日期的表示。问题是查询的优化。在日期列上使用YEAR()MONTH()时,该列是参数的函数。

这意味着编译器将拥有大量有关该函数的信息,以便使用索引扫描或索引查找对其进行优化。如:

  • 在给定相同参数的情况下,函数必须返回相同的值。
  • 该函数只需要一个参数。
  • 该功能需要单调。
  • 该函数需要有一个反转(用于启动索引扫描)。

当然,这些都是可能的。实际上,挑战是将它们构建到函数定义和优化器中。没有人构建SQL优化器会想要为特定函数添加特殊情况(好吧,几乎没有人; SQL Server允许使用cast()来利用索引)。

所以,你的问题的答案是比尔的答案仍然有效。