在T-SQL中使用DATEDIFF

时间:2009-05-15 18:38:55

标签: tsql datediff

我在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

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

所以有两个问题。为什么第一个陈述不起作用?使用第二个语句来执行它是否效率低下?

5 个答案:

答案 0 :(得分:7)

注意:当我最初写这个答案时,我说其中一个列的索引可以创建一个比其他答案更好的查询(并提到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表时,它可以搜索进入EndTime表。需要仔细选择哪一列本身位于运算符的一侧 - 通过执行某些代数切换到BeginTime

,将AND B.BeginTime > E.EndTime - '00:00:10'单独置于试验中是值得的。

DateDiff的精确度

我还应该指出DateDiff不会返回已过去的时间,而是计算边界的数量。如果使用秒的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数据类型 - 要减去必须转换为date的{​​{1}}或time值,或使用其他方法获取更高的精度(大量的datetimeDateAdd和可能的其他垃圾,或者可能使用更高精度的时间单位和分割。)

在计算较大的单位(如小时,天或月)时,此原则尤为重要。 DateDiff的{​​{1}}可能相隔62天(2013年7月1日 - 2013年8月31日)!

答案 1 :(得分:5)

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

你可以这样做

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

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

答案 2 :(得分:3)

除了使其“有效”之外,您还需要使用索引

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

计算列&amp;指数:

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

创建一个视图&amp;指数:

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

答案 3 :(得分:2)

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

答案 4 :(得分:1)

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