比较日期查询的性能

Posted

技术标签:

【中文标题】比较日期查询的性能【英文标题】:Comparing performance of query for year in date 【发布时间】:2017-07-26 21:26:15 【问题描述】:

以下三个查询在性能方面的比较如何?我正在尝试获取 year=2017 的所有记录:

使用EXTRACT:

SELECT count(*), completed_by_id FROM table 
WHERE EXTRACT(YEAR FROM completed_on)=2017 
GROUP BY completed_by_id
# Took 11.8s

使用YEAR

SELECT count(*), completed_by_id FROM table 
WHERE YEAR(completed_on)=2017 
GROUP BY completed_by_id
# Took 5.15s

使用LIKE 'YEAR%'

SELECT count(*), completed_by_id FROM table 
WHERE completed_on LIKE '2017%' 
GROUP BY completed_by_id
# Took 6.61s

注意:在我自己的测试中,我发现YEAR() 是最快的,LIKE 是第二快的,EXTRACT() 是最慢的。

表中有大约 5M 行,completed_on 是已被索引的DATETIME 字段。

【问题讨论】:

您在completed_on 字段上确实有索引,对吧? @PM77-1 是的,有一个索引。 我们在谈论多少条记录? @PM77-1 ~ 500 万 completed_on 是什么列类型? 【参考方案1】:

您尚未描述您的表或索引,因此所有关于查询性能的建议都是猜测。

如果您的completed_on 列是DATETIMEDATETIMESTAMP 类型并且它已被索引,则此查询将大大优于您显示的所有查询,并保持随着表的增长,它的性能。

SELECT count(*), completed_by_id
  FROM table 
 WHERE completed_on >= '2017-01-01'
   AND completed_on <  '2017-01-01' + INTERVAL 1 YEAR
 GROUP BY completed_by_id

为什么?它可以对索引进行范围扫描,而不是对每一行的值调用nonsargable 函数。

注意在日期范围的开头使用&gt;=,在结尾使用&lt;。我们希望包括从 2017 年元旦的第一刻到 但不包括 2018 年元旦的第一刻的所有行。BETWEEN 不能这样做,因为它使用 @ 987654330@ 而不是 &lt; 在其范围的末尾。

如果有索引,BETWEEN 和我展示的语法都使用范围扫描,并且执行大致相同。

为获得加速此查询的最佳结果,请在 (completed_on, completed_by_id) 上使用复合索引。

【讨论】:

BETWEEN 会完全一样吗? 不,BETWEEN 产生不正确的结果。查看我的编辑。 我的意思是正确使用 BETWEEN - 12 月 31 日为最大值。 @O.Jones 上述查询比使用YEAR() 执行得更好。它提供了大约 2% 的加速。 如果该列是 DATE 值,您的建议将起作用。但如果列是TIMESTAMPDATETIME,则需要BETWEEN '2017-01-01' AND 2017-12-31 23:59`。如果时间具有亚秒级分辨率,即使这样也不正确。【参考方案2】:

如果您将 completed_on 存储为 DATE 或 DATETIME,您可以使用:

SELECT count(*) as cnt, LEFT(completed_on, 4) AS year
FROM table 
GROUP BY year
HAVING year=2017

【讨论】:

以上是关于比较日期查询的性能的主要内容,如果未能解决你的问题,请参考以下文章

日期比较 BETWEEN vs < >

与数字数据类型进行日期比较时性能下降

在 SQL 中查询比较日期

将选定日期与表列或 MySQL 查询生成的日期数组进行比较

休眠查询日期仅比较月份

在 SQL 查询中比较日期