我尝试在一些我经常使用的查询中优化索引的使用。最近,我了解到在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'
时,所有三个选项都使用类似的计划进行索引扫描。
那么当我可以预期行数会很低时,使用隐式转换是否是经验法则?我在这里很困惑。
答案 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使用平均密度统计信息来估计行数。结果可能会使用不同的计划,尤其是在数据分布偏斜的情况下。