注意:当我最初写这个答案时,我说其中一个列上的索引可以创建一个执行比其他答案更好的查询(并提到 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 表在EndTime 列上有一个索引,那么这将比使用DateDiff(second, B.BeginTime, E.EndTime) 的任何东西都要好得多。它现在是sargable,这意味着有一个有效的“搜索参数”——所以当引擎扫描BeginTime 表时,它可以搜索 em> 进入EndTime 表。需要仔细选择哪一列单独位于运算符的一侧 - 值得试验一下,将 BeginTime 单独放置,通过代数切换到 AND B.BeginTime > E.EndTime - '00:00:10'
DateDiff 的精度
我还应该指出,DateDiff 不返回 elapsed 时间,而是计算跨越的 边界 的数量。如果使用秒对DateDiff 的调用返回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 或使用其他方法来获得更好的精度(一大堆 @ 987654344@、DateDiff 和可能的其他垃圾,或者可能使用更高精度的时间单位和除法)。
在计算小时、天或月等较大单位时,这一原则尤其重要。 DateDiff 和 1 month 可能相隔 62 天(想想 2013 年 7 月 1 日 - 2013 年 8 月 31 日)!