Sql server 为什么T-SQL变量比较比基于GETDATE()函数的比较慢?

Sql server 为什么T-SQL变量比较比基于GETDATE()函数的比较慢?,sql-server,tsql,sql-server-2008,Sql Server,Tsql,Sql Server 2008,我有一个T-SQL语句,它是针对一个包含许多行的表运行的。我看到一些奇怪的行为。将DateTime列与预先计算的值进行比较要比将每一行与基于GETDATE()函数的计算进行比较慢 以下SQL需要8秒: SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED GO DECLARE @TimeZoneOffset int = -(DATEPART("HH", GETUTCDATE() - GETDATE())) DECLARE @LowerTime DATE

我有一个T-SQL语句,它是针对一个包含许多行的表运行的。我看到一些奇怪的行为。将DateTime列与预先计算的值进行比较要比将每一行与基于GETDATE()函数的计算进行比较慢

以下SQL需要8秒:

SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED
GO
DECLARE @TimeZoneOffset int = -(DATEPART("HH", GETUTCDATE() - GETDATE()))
DECLARE @LowerTime DATETIME = DATEADD("HH", ABS(@TimeZoneOffset), CONVERT(VARCHAR, GETDATE(), 101) + ' 17:00:00')
SELECT TOP 200 Id, EventDate, Message 
FROM Events WITH (NOLOCK)
WHERE EventDate > @LowerTime
GO
这个替代品奇怪地立即返回:

SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED
GO
SELECT TOP 200 Id, EventDate, Message 
FROM Events WITH (NOLOCK)
WHERE EventDate > GETDATE()-1
GO
为什么第二个查询要快得多


已编辑:我更新了SQL以准确反映我正在使用的其他设置执行计划必须不同,因为SQL Server在执行时创建执行计划时不会评估变量的值。因此,它使用可以存储在表中的所有不同日期的平均统计数据

另一方面,函数getdate是在执行时间中计算的,因此执行计划是使用该特定日期的统计信息创建的,当然,这比以前的日期更现实


如果使用
@LowerTime
作为参数创建存储过程,将获得更好的结果。

经过大量阅读和研究,我发现这里的问题是参数嗅探。SQLServer试图根据where子句确定如何最好地使用索引,但在本例中,它做得不是很好

请参见以下示例:

慢速版本:

declare @dNow DateTime  
Select @dNow=GetDate()  
Select *  
From response_master_Incident rmi  
Where rmi.response_date between DateAdd(hh,-2,@dNow) AND @dNow  
快速版本:

Select *  
From response_master_Incident rmi  
Where rmi.response_date between DateAdd(hh,-2,GetDate()) AND GetDate()  
“快速”版本的运行速度大约是慢速版本的10倍。Response_Date字段已编制索引,是DateTime类型

解决方案是告诉Sql Server如何最好地优化查询。将示例修改为包含OPTIMIZE选项,结果使用了与“Fast版本”相同的执行计划。这里的OPTMIZE选项明确告诉sql server将本地@dNow变量视为日期(好像将其声明为DateTime还不够:s)

但是,在执行此操作时应小心,因为在更复杂的WHERE子句中,最终可能会使查询的性能比Sql Server自身的优化更差

declare @dNow DateTime

SET @dNow=GetDate()

Select ID, response_date, call_back_phone 
from response_master_Incident rmi
where rmi.response_date between DateAdd(hh,-2,@dNow) AND @dNow

-- The optimizer does not know too much about the variable so assumes to should perform a clusterd index scann (on the clustered index ID) - this is slow

-- This hint tells the optimzer that the variable is indeed a datetime in this format (why it does not know that already who knows)
OPTION(OPTIMIZE FOR (@dNow = '99991231'));

你检查过执行计划了吗返回的记录数?EventDate列上是否有索引?您看过这两个查询的查询计划了吗?这里没有任何区别。我也看不出有什么理由。每个查询只计算一次GETDATE,因此一旦查询启动,它的行为与@LowerTime相同。在您的示例中,GETDATE和@LowerTime不是相同的值。它们都返回相同数量的行吗?
EventDate
列的类型是什么?没有ORDER BY的TOP不是正确的查询,因此不是正确的测试您是正确的,我没有在存储过程中运行此代码,而是在SSMS中以交互方式运行此代码。这听起来对我来说是可行的。+1这很有趣,你知道这一切对执行计划意味着什么吗?另外,这是否意味着getdate版本每次都在计算执行计划