执行计划索引用法

时间:2019-08-25 10:50:03

标签: sql sql-server indexing

我尝试在一些我经常使用的查询中优化索引的使用。最近,我了解到在where子句中使用函数和隐式转换不是最佳方法。令我惊讶的是,由于正确使用了索引,隐式转换会更快。

我有一个名为Records的表。聚集索引和主键位于Id列(int),非聚集索引位于Created列(datetime)。为了避免隐式转换,我创建了@dd变量。

declare @dd Datetime

set @dd = '2019-08-25'

--1st option
select  * from Records where Created > @dd
--2nd option
select  * from Records where Created > '2019-08-25'    
--3rd option
select  * from Records where Year(Created) = 2019 and MONTH(Created) = 8 and DAY(Created) = 25

不幸的是,第一个选项使用索引扫描(列ID)。第二个选项使用索引查找(创建的列)和键查找,这很好,但我想知道为什么第一个选项不做相同的事情。我添加了第3个选项只是为了看到区别,它的行为就像第一个选项。

我发现query execution plan : missing index导致了有关此行为的博客文章,但没有解释为什么会发生。 我可以看到估计的行数有所不同。当我将日期设置为'2019-06-25'时,所有三个选项都使用类似的计划进行索引扫描。

那么当我可以预期行数会很低时,使用隐式转换是否是经验法则?我在这里很困惑。

1 个答案:

答案 0 :(得分:0)

因为T-SQL没有日期时间文字的语法,所以需要日期时间文字的隐式转换。这不是性能问题。

在由于data type precedence而导致表达式无法精简的情况下(例如,将int文字与varchar列进行比较),隐式转换是一个值得关注的问题。将YEAR之类的函数应用于列也会导致表达式不可保留,因为必须在比较之前对函数结果求值。

查询中的问题是您使用的是变量而不是参数。您应该获得与文字相同的性能:

select * from Records where Created > @dd OPTION(RECOMPILE);

或参数化查询(假设缓存的计划尚不存在):

EXEC sp_executesql N'select * from Records where Created > @dd'
    , N'@dd datetime'
    , @dd = '2019-08-25';

在带文字,参数和OPTION(RECOMPLE)的变量的情况下,SQL Server在初始编译期间使用统计直方图(如果存在)以更好地估计可能返回的行数。对于变量,SQL Server使用平均密度统计信息来估计行数。结果可能会使用不同的计划,尤其是在数据分布偏斜的情况下。