我有一个简单的表,在DateTime
列上有索引。
有人可以解释一下这两个查询中的哪一个使用索引吗?
CREATE TABLE exams
(
name VARCHAR(50),
grade INT,
date DATETIME
);
CREATE INDEX date_idx ON exams(date);
SELECT *
FROM exams
WHERE date = '2018-01-01'; -- doesn't use index?
SELECT *
FROM exams
WHERE MONTH(date) = 1; -- uses index?
答案 0 :(得分:6)
SQL引擎有多种解决方法。让我们在表中插入一些示例数据:
DROP TABLE if exists exams;
CREATE TABLE exams(
name varchar(50),
grade INT,
date datetime
);
INSERT exams
SELECT TOP (5000) CONCAT('name', row_number() over(order by t1.number))
,6
,'2019-07-01'
FROM master..spt_values t1
CROSS JOIN master..spt_values t2
INSERT exams
SELECT TOP (5) CONCAT('name', row_number() over(order by t1.number))
,6
,'2019-07-02'
FROM master..spt_values t1
CROSS JOIN master..spt_values t2
CREATE INDEX date_idx ON exams(date);
如您所见,我们已经插入:
2019-07-01
的5000行2019-07-02
的5行现在让我们执行以下查询:
SELECT * FROM exams WHERE date= '2019-07-01';
SELECT * FROM exams WHERE date= '2019-07-02';
SELECT * FROM exams WHERE MONTH(date)=1;
并检查执行计划:
在第一个查询中,引擎(由于统计信息)知道几乎所有数据都将被读取,因此它在堆上执行table scan
。
在第二个查询中,引擎看到将只返回很少的记录,因此不需要读取所有数据-它使用索引并执行index seek
。
在最后一种情况下,无法使用索引,因为查询不是sargable。
因此,引擎将决定是否使用索引,以及是否执行查找或扫描。您唯一可以做的就是确保索引被覆盖,统计信息已更新且查询可保留。
答案 1 :(得分:4)
好吧,我相信事实是相反的:
第一个查询使用索引,而第二个则不。
为什么第二个查询不使用索引?因为索引列包装在防止SQL Server使用索引的函数中。
索引可以被认为是存储记录的一种方式。将函数应用于索引列可能会更改存储记录的顺序,因此使用函数时索引可能不再有效。