注意:当我最初写这个答案时,我说其中一个列上的索引可以创建一个比其他答案执行得更好的查询(并提到了 Dan Fuller 的)。但是,我并没有 100% 正确地思考。事实是,如果没有计算列或索引(物化)视图,则需要进行全表扫描,因为要比较的两个日期列来自同一个表!
我相信下面的信息仍然有价值,即 1) 在正确的情况下提高性能的可能性,例如在不同表中的列之间进行比较时,以及 2) 促进 SQL 开发人员遵循最佳实践和重塑的习惯他们的思路是正确的。
使条件变得可行
我所指的最佳实践是将一列单独移动到比较运算符的一侧,如下所示:
SELECT InitialSave = DateDiff(second, T.BegTime, T.EndTime)
FROM dbo.MyTable T
WHERE T.EndTime <= T.BegTime + '00:00:10'
正如我所说,这不会避免对单个表进行扫描,但是,在这种情况下,它可能会产生巨大的影响:
SELECT InitialSave = DateDiff(second, T.BegTime, T.EndTime)
FROM
dbo.BeginTime B
INNER JOIN dbo.EndTime E
ON B.BeginTime <= E.EndTime
AND B.BeginTime + '00:00:10' > E.EndTime
EndTime
在这两种情况下,现在单独在比较的一侧。假设该BeginTime
表的行数要少得多,并且该EndTime
表在 column 上有一个索引EndTime
,这将比使用DateDiff(second, B.BeginTime, E.EndTime)
. 它现在是sargable,这意味着有一个有效的“搜索参数”——所以当引擎扫描表时BeginTime
,它可以搜索表EndTime
。需要仔细选择哪一列单独位于运算符的一侧 -BeginTime
通过做一些代数切换到AND B.BeginTime > E.EndTime - '00:00:10'
DateDiff 的精度
我还应该指出,DateDiff
它不会返回经过的时间,而是计算跨越的边界数。如果调用DateDiff
using seconds 返回1
,这可能意味着3 ms
经过的时间,或者可能意味着1997 ms
!这本质上是 +- 1 个时间单位的精度。为了获得更好的 +- 1/2 时间单位精度,您需要以下查询0
来比较EndTime - BegTime
:
SELECT DateDiff(second, 0, EndTime - BegTime) AS InitialSave
FROM MyTable
WHERE EndTime <= BegTime + '00:00:10'
现在,最大舍入误差总共只有一秒,而不是两秒(实际上是 floor() 操作)。请注意,您只能减去datetime
数据类型 - 减去一个date
或一个time
值,您必须转换为datetime
或使用其他方法来获得更好的精度(一大堆DateAdd
,DateDiff
可能还有其他垃圾,或者可能使用更高的精度时间单位和划分)。
在计算小时、天或月等较大单位时,这一原则尤其重要。A DateDiff
of1 month
可能相隔 62 天(想想 2013 年 7 月 1 日 - 2013 年 8 月 31 日)!