我有一张非常大的桌子(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"
答案 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
(具体来说,KEY
是BeginDate_index
)。
但请注意,不保证使用索引,例如如果您针对更广泛的日期条件执行相同的查询,则可以使用不同的计划(例如,如果您为> 20140101
运行相同的小提琴,则不再使用BeginDate_index
,因为它确实如此不提供sufficient selectivity)。
编辑,重新:对完整性的评论
由于BeginDate
是日期时间,因此文字20141101
也将转换为日期时间(一次)。来自the docs:
如果其中一个参数是TIMESTAMP或DATETIME列而另一个参数是常量,则在执行比较之前将常量转换为时间戳。
再次,是的,根据您的上一段,过滤器BeginDate >= 20141101
中的文字将转换为确切的日期时间20141101000000
(2014-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
以查看差异。