到目前为止,将datetime列转换为sargable,可以使用datetime列上的索引.
select * from T where cast(DateTimeCol as date) = '20130101';
您拥有的另一个选择是使用范围.
select * from T where DateTimeCol >= '20130101' and DateTimeCol < '20130102'
这些查询是同样好还是应该优先于另一个?
解决方法
sql Server调用内部函数GetRangeThroughConvert来获取范围的开始和结束.
有点令人惊讶的是,这与您的文字值不同.
创建一个每页有一行和每天1440行的表
CREATE TABLE T ( DateTimeCol DATETIME PRIMARY KEY,Filler CHAR(8000) DEFAULT 'X' ); WITH Nums(Num) AS (SELECT number FROM spt_values WHERE type = 'P' AND number BETWEEN 1 AND 1440),Dates(Date) AS (SELECT {d '2012-12-30'} UNION ALL SELECT {d '2012-12-31'} UNION ALL SELECT {d '2013-01-01'} UNION ALL SELECT {d '2013-01-02'} UNION ALL SELECT {d '2013-01-03'}) INSERT INTO T (DateTimeCol) SELECT DISTINCT DATEADD(MINUTE,Num,Date) FROM Nums,Dates
然后跑
SET STATISTICS IO ON; SET STATISTICS TIME ON; SELECT * FROM T WHERE DateTimeCol >= '20130101' AND DateTimeCol < '20130102' SELECT * FROM T WHERE CAST(DateTimeCol AS DATE) = '20130101';
第一个查询有1443个读取,第二个查询有2883个,因此它读取整个额外的一天,然后根据残差谓词丢弃它.
Seek Keys[1]: Start: DateTimeCol > Scalar Operator([Expr1006]),End: DateTimeCol < Scalar Operator([Expr1007])
因此,而不是> =’20130101’…< '20130102'它读取> ‘20121231’…< '20130102'然后丢弃所有2012-12-31行. 依赖于它的另一个缺点是基数估计可能不如传统范围查询那样准确.这可以在SQL Fiddle的修订版中看到.
表中的所有100行现在都与谓词匹配(在同一天,所有日期时间相隔1分钟).
第二个(范围)查询正确估计100将匹配并使用聚簇索引扫描. CAST(AS DATE)查询错误地估计只有一行匹配并生成带有键查找的计划.
统计数据不会完全被忽略.如果表中的所有行具有相同的日期时间并且它与谓词匹配(例如20130101 00:00:00或20130101 01:00:00),则计划显示聚集索引扫描,估计行数为31.6228.
100 ^ 0.75 = 31.6228
因此,在这种情况下,估计似乎是从the formula here得出的.
如果表中的所有行具有相同的日期时间且与谓词不匹配(例如20130102 01:00:00),则它将回退到估计的行数1和具有查找的计划.
对于表具有多个DISTINCT值的情况,估计的行似乎与查询正在查找20130101 00:00:00完全相同.
如果统计直方图恰好在2013-01-01 00:00:00.000处有一个步骤,则估计将基于EQ_ROWS(即不考虑该日期的其他时间).否则,如果没有步骤,它看起来好像是使用周围步骤中的AVG_RANGE_ROWS.
由于日期时间在许多系统中的精度约为3毫秒,因此实际重复值非常少,此数字为1.