2012-03-28 167 views
2

第一个查询是:有人可以解释两个查询之间的区别吗?

declare @myDate datetime = DATEADD(D,-2000,getdate()) 
SELECT * FROM [myTable] 
where CreatedDate >= @myDate 

第二个查询是:

SELECT * FROM [myTable] 
where CreatedDate >= DATEADD(D,-2000,getdate()) 

我想到的是,第一查询可能是因为速度更快,“DATEADD”函数计算一次。但实际上这个查询是相等的(2秒,30000行)

+0

它不计算DATEADD每一行的位置。 在选择甚至开始提取数据之前计算。 – 2012-03-28 10:38:45

+0

相似的问题在这里:http://stackoverflow.com/questions/7301058/does-sql-server-optimize-dateadd-calculation-in-select-query – 2012-03-28 10:45:53

回答

3

getdate()runtime constant function,每个函数引用这就是为什么

SELECT GETDATE() 
FROM SomeBigTable 

将返回相同的结果为所有行无论查询需要多长时间运行只计算一次。

然而,两者之间存在差异。由于第一个使用变量,并且在将变量分配给SQL Server之前编译计划(在没有重新编译的情况下),假定将返回30%的行。这种猜测可能会导致它使用与第二个查询不同的计划。

直接在过滤器中使用GETDATE()时需要注意的一点是,它在编译时评估GETDATE(),此后可能在没有查询或数据更改的情况下显着改变选择性以触发重新编译。在下面的示例中,对于1000行表,使用变量的查询会导致计划带有估计的300行和全表扫描,而嵌入函数调用的查询会估计1行并执行书签查找。这在第一次运行时是准确的,但是在第二次运行中,由于时间流逝,现在所有行都有资格,并且最终会执行1,000次这种随机查找。

USE tempdb; 

CREATE TABLE [myTable] 
(
CreatedDate datetime, 
Filler char(8000) NULL 
) 

CREATE NONCLUSTERED INDEX ix ON [myTable](CreatedDate) 

INSERT INTO [myTable](CreatedDate) 
/*Insert 1 row that initially qualifies*/ 
SELECT DATEADD(D,-2001,getdate()) 
UNION ALL 
/*And 999 rows that don't initially qualify*/ 
SELECT TOP 999 DATEADD(minute,1, DATEADD(D,-2000,getdate())) 
FROM master..spt_values 

EXEC(' 
DECLARE @myDate DATETIME = DATEADD(D,-2000,getdate()) 
SELECT * 
FROM [myTable] 
WHERE CreatedDate <= @myDate 
') 

EXEC(' 
SELECT * 
FROM [myTable] 
WHERE CreatedDate <= DATEADD(D,-2000,getdate()) 
') 

RAISERROR ('Delay',0,1) WITH NOWAIT 

WAITFOR DELAY '00:01:01' 

EXEC(' 
DECLARE @myDate DATETIME = DATEADD(D,-2000,getdate()) 
SELECT * 
FROM [myTable] 
WHERE CreatedDate <= @myDate 
') 

EXEC(' 
SELECT * 
FROM [myTable] 
WHERE CreatedDate <= DATEADD(D,-2000,getdate()) 
') 

DROP TABLE [myTable] 
+0

这是否意味着第一个变种有点长时间执行? – Yara 2012-03-28 10:49:06

+0

@Yara - 取决于选择性。如果查询是高度选择性的,那么第二个查询可能会考虑一个带有关键查找的计划,这在30%的假设下是不太可能的。 – 2012-03-28 10:51:16

+0

事实上,如果你的谓词使用少于(即''Where CreatedDate <= DATEADD(D,-2000,getdate())'),那么事情会更有趣,因为受影响的估计行将在编译时确定,但随着时间的推移该查询将变得更少选择性和不太理想。假设没有数据更改,则不会触发统计信息重新编译来纠正此问题。 – 2012-03-28 11:09:02

1

SQL不会重新计算每一行的DATEADD。无论哪种方式,它都会计算一次,然后对您的表格中的行进行比较。两种不同的方式,一种(可能是不必要的)比另一种更冗长,但最终他们会得到相同的结果。

0

SQL服务器优化器正在帮助使后者查询更优化。

看一看query plan

相关问题