在mysql的DATETIME索引列中使用SELECT查询中的DATE是否正确优化?

时间:2014-12-25 11:14:14

标签: mysql datetime

我有一张非常大的桌子(425,000多行)。

CREATE TABLE `DummyTab` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `Name` varchar(48) NOT NULL,
  `BeginDate` datetime DEFAULT NULL,
  `EndDate` datetime NOT NULL,
  ......
  ......
  KEY `BeginDate_index` (`dBegDate`),
  KEY `id` (`id`),
) ENGINE=MyISAM

选择基于" BeginDate"和此表中的其他标准

select * from DummyTab where Name like "%dummyname%" and BeginDate>= 20141101

现在在这种情况下,只提供日期时间以外的日期字段(尽管它将用作2014-11-01 00:00:00)。

问题是优化者是否正确地使用了日期时间指数,即使在这种情况下提供的日期也是如此?或者索引应该设置在"日期"要更有效地使用字段而不是" datetime"

1 个答案:

答案 0 :(得分:1)

是的,当使用仅限DATE的过滤器指定查询时,仍然可以使用BeginDate_index(同时在Name上应用其他条件,但也不会取消索引。)

如果您查看this SqlFiddle随机数据,并展开底部的Execution plan,您会看到以下内容:

ID   SELECT_TYPE  TABLE  TYPE     POSSIBLE_KEYS    KEY             KEY_LEN   REF    ROWS   FILTERED                EXTRA
1    SIMPLE       DummyTab range  BeginDate_index  BeginDate_index 6         17190  100    Using index condition;  Using where   

(具体来说,KEYBeginDate_index)。 但请注意,不保证使用索引,例如如果您针对更广泛的日期条件执行相同的查询,则可以使用不同的计划(例如,如果您为> 20140101运行相同的小提琴,则不再使用BeginDate_index,因为它确实如此不提供sufficient selectivity)。

编辑,重新:对完整性的评论
由于BeginDate是日期时间,因此文字20141101也将转换为日期时间(一次)。来自the docs

  

如果其中一个参数是TIMESTAMP或DATETIME列而另一个参数是常量,则在执行比较之前将常量转换为时间戳。

再次,是的,根据您的上一段,过滤器BeginDate >= 20141101中的文字将转换为确切的日期时间201411010000002014-11-01 00:00:00),任何符合条件的索引都将是被视为(但同样,从不保证)。

无法使用索引的常见问题是因为过滤器谓词 NOT sargable 是函数应用于过滤器中的列时,因为引擎需要评估查询中所有剩余行的函数。一些examples here

所以稍微改变你的例子,下面的查询做同样的事情,但第二个查询要慢得多。这个查询是可以理解的:

SELECT * FROM DummyTab
WHERE BeginDate < 20140101;  -- Good

这不是:

SELECT * FROM DummyTab
WHERE YEAR(BeginDate) < 2014; -- Bad

Updated SqlFiddle here - 请再次查看底部的Execution Plans以查看差异。