2011-09-04 51 views
4

我的是SQL Server 2008上这样的查询:SQL Server是否在选择查询中优化DATEADD计算?

DECLARE @START_DATE DATETIME 
SET @START_DATE = GETDATE() 

SELECT * FROM MY_TABLE 
WHERE TRANSACTION_DATE_TIME > DATEADD(MINUTE, -1440, @START_DATE) 

在你上面看到的选择查询,并SqlServer的优化查询,以不一次又一次计算DATEADD结果。或者,我自己有责任将DATEADD结果存储在临时变量中?

回答

8

令人惊讶的是,我发现使用GETDATE()内联似乎比执行这种类型的事先计算。

DECLARE @sd1 DATETIME, @sd2 DATETIME; 
SET @sd1 = GETDATE(); 

SELECT * FROM dbo.table 
WHERE datetime_column > DATEADD(MINUTE, -1440, @sd1) 

SELECT * FROM dbo.table 
WHERE datetime_column > DATEADD(MINUTE, -1440, GETDATE()) 

SET @sd2 = DATEADD(MINUTE, -1440, @sd1); 

SELECT * FROM dbo.table 
WHERE datetime_column > @sd2; 

如果您检查那些计划,中间的查询将永远出来用最低的成本(但并不总是最低的经过时间)。当然,这可能取决于您的索引和数据,并且您不应基于一个查询做出任何假设,即相同的优先优化将适用于另一个查询。我的直觉就是不要在线执行任何计算,而应该使用上面的@sd2变体......但是我已经知道,我不能总是相信自己的直觉,我不能根据我经历的行为做出一般假设在特定情况下。

+1

+1令人惊讶。只是看着它,我认为这是因为SQL Server不会执行变量嗅探,所以总会认为'> @ variable'将匹配30%的行(并且计划中显示的成本仅基于此估计) 。至少在编译计划时,DATEADD(MINUTE,-1440,getdate())版似乎更加准确。 –

+1

GetDate()查询的一个潜在问题/边缘情况是它在编译时被评估,选择性可能会随着时间的推移而发生巨大变化[无需触发重新编译](http://stackoverflow.com/a/9905880/73226) –

3

它只会执行一次。你可以通过检查执行计划(“Compute Scalar” - > Estimated Number of execution == 1)来仔细检查它。

10

被认为是runtime constants的SQL Server功能只被评估一次。 GETDATE()就是这样一个函数,而DATEADD(..., constant, GETDATE())也是一个运行时常量。通过在查询中留下实际的函数调用,您可以让优化器查看实际使用的值(而不是变量值嗅探),然后它可以相应地调整其基数估计,可能会提出更好的计划。

另请阅读:Troubleshooting Poor Query Performance: Constant Folding and Expression Evaluation During Cardinality Estimation

@马丁·史密斯

您可以运行此查询:

set nocount on; 
declare @known int; 
select @known = count(*) from sysobjects; 
declare @cnt int = @known; 
while @cnt = @known 
    select @cnt = count(*) from sysobjects where getdate()=getdate() 
select @cnt, @known; 

在我的壳22秒后,它击中了边界的情况下,循环退出。重要的是,循环退出@cnt。人们会认为,如果getdate()是每行评估,那么我们会得到@cnt与正确的@known计数不同,但不是0.循环存在时@cnt为零的事实显示每个getdate()被评估一次,然后每行WHERE过滤使用相同的常量值(无匹配)。我知道有一个正面的例子并不能证明一个定理,但我认为这个例子已经足够决定了。

+0

第一个链接似乎意味着'getdate()'仅对每个** query **进行一次评估,但是这不会无限循环'SET NOCOUNT ON; WHILE EXISTS(SELECT 1 FROM sysobjects WHERE GETDATE()= GETDATE())PRINT'Yes'' –

+0

@Martin:我想我知道发生了什么。检查计划XML显示你的查询有*两个* const表达式,每个表达式都被计算一次(在我的showplan XML中,它们被称为“ConstExpr1080”和“ConstExpr1081”)。这些值总是不同的,因为它们是在不同的时刻评估的,但由于日期时间类型(3ms)的精确度低,它们实际上会是很多次相同的值。偶尔评估第一个getdate和第二个getdate恰好落在舍入边界的不同侧,然后循环停止。 –

+0

因此,缓存似乎是按照函数引用发生的,而不是在查询中的所有地方都重复使用结果的函数调用,然后如Conor的帖子所示。或者您是否认为我误解了他所说的内容,而这不适用于这种情况? –