在 T-SQL 中使用 DATEDIFF

发布于 2024-07-20 15:06:49 字数 492 浏览 2 评论 0原文

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

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

那么两个问题。 为什么第一个语句不起作用? 使用第二个语句执行此操作是否效率低下?

I am using DATEDIFF in an SQL statement. I am selecting it, and I need to use it in WHERE clause as well. This statement does not work...

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

It gives the message: Invalid column name "InitialSave"

But this statement works fine...

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

The programmer in me says that this is inefficient (seems like I am calling the function twice).

So two questions. Why doesn't the first statement work? Is it inefficient to do it using the second statement?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

望笑 2024-07-27 15:06:50

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

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

计算列和 索引:

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

beyond making it "work", you need to use an index

use a computed column with an index, or a view with an index, otherwise you will table scan. when you get enough rows, you will feel the PAIN of the slow scan!

computed column & index:

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 a view & index:

CREATE VIEW YourNewView
AS
SELECT
    KeyValues
        ,DATEDIFF(ss, BegTime, EndTime) AS InitialSave
    FROM MyTable
GO
CREATE CLUSTERED INDEX IX_YourNewView
    ON YourNewView(InitialSave)
GO
已下线请稍等 2024-07-27 15:06:50

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

You have to use the function instead of the column alias - it is the same with count(*), etc. PITA.

三生一梦 2024-07-27 15:06:50

作为替代方案,您可以使用计算列

As an alternate, you can use computed columns.

懒猫 2024-07-27 15:06:49

注意:当我最初写这个答案时,我说过其中一列上的索引可以创建一个比其他答案性能更好的查询(并提到了 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 表中。 需要仔细选择哪一列本身位于运算符的一侧 - 通过进行一些代数操作以切换到 AND B.BeginTime >,将 BeginTime 单独放置,这可能是值得尝试的; E.EndTime - '00:00:10'

DateDiff 的精度

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

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

现在最大舍入误差仅为 1第二个总数,而不是两个(实际上是一个 Floor() 操作)。 请注意,您只能减去 datetime 数据类型 - 要减去 datetime 值,您必须将其转换为 datetime 或使用其他方法来获得更好的精度(大量 DateAddDateDiff 以及可能的其他垃圾,或者可能使用更高精度的时间单位和划分)。

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

Note: When I originally wrote this answer I said that an index on one of the columns could create a query that performs better than other answers (and mentioned Dan Fuller's). However, I was not thinking 100% correctly. The fact is, without a computed column or indexed (materialized) view, a full table scan is going to be required, because the two date columns being compared are from the same table!

I believe there is still value in the information below, namely 1) the possibility of improved performance in the right situation, as when the comparison is between columns from different tables, and 2) promoting the habit in SQL developers of following best practice and reshaping their thinking in the right direction.

Making Conditions Sargable

The best practice I'm referring to is one of moving one column to be alone on one side of the comparison operator, like so:

SELECT InitialSave = DateDiff(second, T.BegTime, T.EndTime)
FROM dbo.MyTable T
WHERE T.EndTime <= T.BegTime + '00:00:10'

As I said, this will not avoid a scan on a single table, however, in a situation like this it could make a huge difference:

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 is in both conditions now alone on one side of the comparison. Assuming that the BeginTime table has many fewer rows, and the EndTime table has an index on column EndTime, this will perform far, far better than anything using DateDiff(second, B.BeginTime, E.EndTime). It is now sargable, which means there is a valid "search argument"--so as the engine scans the BeginTime table, it can seek into the EndTime table. Careful selection of which column is by itself on one side of the operator is required--it can be worth experimenting by putting BeginTime by itself by doing some algebra to switch to AND B.BeginTime > E.EndTime - '00:00:10'

Precision of DateDiff

I should also point out that DateDiff does not return elapsed time, but instead counts the number of boundaries crossed. If a call to DateDiff using seconds returns 1, this could mean 3 ms elapsed time, or it could mean 1997 ms! This is essentially a precision of +- 1 time units. For the better precision of +- 1/2 time unit, you would want the following query comparing 0 to EndTime - BegTime:

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

This now has a maximum rounding error of only one second total, not two (in effect, a floor() operation). Note that you can only subtract the datetime data type--to subtract a date or a time value you would have to convert to datetime or use other methods to get the better precision (a whole lot of DateAdd, DateDiff and possibly other junk, or perhaps using a higher precision time unit and dividing).

This principle is especially important when counting larger units such as hours, days, or months. A DateDiff of 1 month could be 62 days apart (think July 1, 2013 - Aug 31 2013)!

余生再见 2024-07-27 15:06:49

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

但是,您可以执行此操作

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

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

You can't access columns defined in the select statement in the where statement, because they're not generated until after the where has executed.

You can do this however

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

As a sidenote - this essentially moves the DATEDIFF into the where statement in terms of where it's first defined. Using functions on columns in where statements causes indexes to not be used as efficiently and should be avoided if possible, however if you've got to use datediff then you've got to do it!

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文