Why is a T-SQL variable comparison slower than GETDATE() function-based comparison?
我有一个 T-SQL 语句,我正在对一个包含许多行的表运行。我看到一些奇怪的行为。将 DateTime 列与预先计算的值进行比较比将每一行与基于 GETDATE() 函数的计算进行比较要慢。
以下 SQL 耗时 8 秒:
1 2 3 4 5 6 7 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 |
这个交替奇怪地立即返回:
1 2 3 4 5 6 | 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 尝试根据 where 子句确定如何最好地使用索引,但在这种情况下它做得不是很好。
请看下面的例子:
慢版本:
1 2 3 4 5 | declare @dNow DateTime Select @dNow=GetDate() Select * From response_master_Incident rmi Where rmi.response_date between DateAdd(hh,-2,@dNow) AND @dNow |
快速版本:
1 2 3 | Select * From response_master_Incident rmi Where rmi.response_date between DateAdd(hh,-2,GetDate()) AND GetDate() |
"快速"版本的运行速度比慢速版本快 10 倍左右。 Response_Date 字段已编入索引并且是 DateTime 类型。
解决方案是告诉 Sql Server 如何最好地优化查询。如下修改示例以包含 OPTIMIZE 选项导致它使用与"快速版本"相同的执行计划。这里的 OPTMIZE 选项明确告诉 sql server 将本地 @dNow 变量视为日期(好像将其声明为 DateTime 是不够的:s)
在执行此操作时应小心,因为在更复杂的 WHERE 子句中,您最终可能会使查询的性能比 Sql Server 自己的优化更差。
1 2 3 4 5 6 7 8 9 10 11 12 | 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')); |
执行计划必须不同,因为 SQL Server 在执行时创建执行计划时不会计算变量的值。因此,它使用了可以存储在表中的所有不同日期的平均统计数据。
另一方面,函数 getdate 是在执行时间中评估的,因此执行计划是使用该特定日期的统计数据创建的,这当然比以前的更现实。
如果你创建一个以