在T-SQL中使用DATEDIFF

Jos*_*ola 6 t-sql datediff

我在SQL语句中使用DATEDIFF.我正在选择它,我也需要在WHERE子句中使用它.这句话不起作用......

SELECT DATEDIFF(ss, BegTime, EndTime) AS InitialSave
FROM MyTable
WHERE InitialSave <= 10
Run Code Online (Sandbox Code Playgroud)

它给出了消息:无效的列名"InitialSave"

但这句话很好......

SELECT DATEDIFF(ss, BegTime, EndTime) AS InitialSave
FROM MyTable
WHERE DATEDIFF(ss, BegTime, EndTime) <= 10
Run Code Online (Sandbox Code Playgroud)

我的程序员说这是低效的(似乎我在调用函数两次).

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

Eri*_*ikE 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'
Run Code Online (Sandbox Code Playgroud)

正如我所说,这不会避免在单个表上进行扫描,但是,在这样的情况下,它可能会产生巨大的差异:

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
Run Code Online (Sandbox Code Playgroud)

EndTime现在,两种情况都是在比较的一方.假设BeginTime表中的行数少得多,并且EndTime表的列上有索引EndTime,这将比使用的任何内容都要好得多DateDiff(second, B.BeginTime, E.EndTime).现在是优化搜索,这意味着有一个有效的"搜索参数" -这样的引擎扫描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'
Run Code Online (Sandbox Code Playgroud)

现在,最大舍入误差仅为一秒,而不是两个(实际上是一个floor()操作).请注意,您只能减去datetime数据类型 - 减去要转换为datetimedatetime或其他方法以获得更好的精度(大量的DateAdd,DateDiff可能还有其他垃圾,或者可能使用更高的精度时间)单位和分割).

在计算较大的单位(如小时,天或月)时,此原则尤为重要.一DateDiff1 month可能有62个相隔几天(想想2013年7月1日- 2013年8月31日)!


Dan*_*ler 5

您不能访问where语句中select语句中定义的列,因为它们只有在执行where之后才会生成.

但是你可以这样做

select InitialSave from 
(SELECT DATEDIFF(ss, BegTime, EndTime) AS InitialSave
FROM MyTable) aTable
WHERE InitialSave <= 10
Run Code Online (Sandbox Code Playgroud)

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

  • 如果需要,可以在第一行使用select*,而不是选择InitialSave,Col2,Col3等. (2认同)