【问题标题】:Using DATEDIFF in T-SQL在 T-SQL 中使用 DATEDIFF
【发布时间】:2009-05-15 18:38:54
【问题描述】:

我在 SQL 语句中使用 DATEDIFF。我正在选择它,我也需要在 WHERE 子句中使用它。这个说法不成立……

SELECT DATEDIFF(ss, BegTime, EndTime) AS InitialSave
FROM MyTable
WHERE InitialSave <= 10

它给出消息:无效的列名“InitialSave”

但是这个说法很好用……

SELECT DATEDIFF(ss, BegTime, EndTime) AS InitialSave
FROM MyTable
WHERE DATEDIFF(ss, BegTime, EndTime) <= 10

我的程序员说这是低效的(好像我调用了两次函数)。

所以有两个问题。为什么第一个语句不起作用?使用第二条语句是不是效率低下?

【问题讨论】:

    标签: tsql datediff


    【解决方案1】:

    注意:当我最初写这个答案时,我说其中一个列上的索引可以创建一个执行比其他答案更好的查询(并提到 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 &gt; E.EndTime - '00:00:10'

    DateDiff 的精度

    我还应该指出,DateDiff 不返回 elapsed 时间,而是计算跨越的 边界 的数量。如果使用秒对DateDiff 的调用返回1,这可能意味着3 ms 已用时间,或者可能意味着1997 ms!这本质上是 +- 1 个时间单位的精度。为了获得更好的 +- 1/2 时间单位精度,您需要以下查询比较 0EndTime - BegTime

    SELECT DateDiff(second, 0, EndTime - BegTime) AS InitialSave
    FROM MyTable
    WHERE EndTime <= BegTime + '00:00:10'
    

    现在最大舍入误差总共只有一秒,而不是两秒(实际上是 floor() 操作)。请注意,您只能减去 datetime 数据类型——要减去 datetime 值,您必须转换为 datetime 或使用其他方法来获得更好的精度(一大堆 @ 987654344@、DateDiff 和可能的其他垃圾,或者可能使用更高精度的时间单位和除法)。

    在计算小时、天或月等较大单位时,这一原则尤其重要。 DateDiff1 month 可能相隔 62 天(想想 2013 年 7 月 1 日 - 2013 年 8 月 31 日)!

    【讨论】:

    • 这太傻了。当我发表评论时,我并没有装出傲慢的态度,而是实际上试图对那些可能不知道我所描述的事情的实际影响的人真正有帮助。请重读我写的内容:我可能只是有点太精确了,但实际上并没有侮辱?
    • 哦...在我的 better-performing 查询中出现 -1,因为您误解了一个词是...在此处填写空白,但这不是免费的。嘿,伙计,我试图分享我能分享的东西。
    【解决方案2】:

    您无法在 where 语句中访问 select 语句中定义的列,因为它们直到 where 执行后才会生成。

    你可以这样做

    select InitialSave from 
    (SELECT DATEDIFF(ss, BegTime, EndTime) AS InitialSave
    FROM MyTable) aTable
    WHERE InitialSave <= 10
    

    作为旁注 - 这基本上将 DATEDIFF 移动到 where 语句中,就其首次定义的位置而言。在 where 语句中的列上使用函数会导致索引的使用效率不高,应尽可能避免使用,但是如果您必须使用 datediff,那么您必须这样做!

    【讨论】:

    • 因此,如果我选择除此之外的 20 列,我将需要在两个语句(内部和外部)中选择它们,对吗?有没有更好的办法?
    • 如果你愿意,你可以只在第一行使用 select *,而不是选择 InitialSave、Col2、Col3 等。
    • 哦,呵呵!谢谢! 拍自己的头
    【解决方案3】:

    除了让它“工作”之外,您还需要使用索引

    使用带有索引的计算列,或带有索引的视图,否则您将进行表扫描。当你得到足够多的行时,你会感觉到扫描慢的PAIN

    计算列和索引:

    ALTER TABLE MyTable ADD
        ComputedDate  AS DATEDIFF(ss,BegTime, EndTime)
    GO
    CREATE NONCLUSTERED INDEX IX_MyTable_ComputedDate  ON MyTable 
        (
        ComputedDate
        ) WITH( STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
    GO
    

    创建视图和索引:

    CREATE VIEW YourNewView
    AS
    SELECT
        KeyValues
            ,DATEDIFF(ss, BegTime, EndTime) AS InitialSave
        FROM MyTable
    GO
    CREATE CLUSTERED INDEX IX_YourNewView
        ON YourNewView(InitialSave)
    GO
    

    【讨论】:

    • 在视图上创建索引并不总是灵丹妙药。我认为您会发现 EndTime 列上的索引将执行得足够好,并且不会产生更新开销。它确实归结为更新/选择模式。
    • 这是一个非常好的观点,使用计算列确实取决于数据和使用列的频率。如果该列正在用于的表可能很大(例如,数百万行+)并且所讨论的列是: 1. 将在应用程序的一般运行中极其频繁地使用 2. 将不经常访问的是需要快速访问的行政级别的员工。然后应该使用计算列。否则,最好不要使用它,也不要减慢插入/更新速度。
    【解决方案4】:

    您必须使用函数而不是列别名 - 它与 count(*) 等 PITA 相同。

    【讨论】:

    • 它是否足够聪明,可以多次进行相同的大量计算?我很怀疑。
    【解决方案5】:

    作为替代,您可以使用computed columns

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-07-09
      • 2012-12-24
      • 1970-01-01
      相关资源
      最近更新 更多