我在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
我的程序员说这是低效的(好像我在调用函数两次)。
所以有两个问题。为什么第一个陈述不起作用?使用第二个语句来执行它是否效率低下?
答案 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时间单位的更好精度,您需要以下查询将0
与EndTime - BegTime
进行比较:
SELECT DateDiff(second, 0, EndTime - BegTime) AS InitialSave
FROM MyTable
WHERE EndTime <= BegTime + '00:00:10'
现在最大舍入误差仅为一秒,而不是两个(实际上是一个floor()操作)。请注意,您只能减去datetime
数据类型 - 要减去必须转换为date
的{{1}}或time
值,或使用其他方法获取更高的精度(大量的datetime
,DateAdd
和可能的其他垃圾,或者可能使用更高精度的时间单位和分割。)
在计算较大的单位(如小时,天或月)时,此原则尤为重要。 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。