SQL Server - 变量替换速度慢

时间:2013-12-13 10:20:28

标签: sql-server

我有一个表“my_table”,有2000万个条目。 id列被编入索引。如果我执行以下查询,则需要不到1个sek。

SELECT * FROM my_table WHERE id='asdf'

如果我执行相同的查询但在where子句中使用临时变量。

declare @ID nvarchar(4)
set @ID = N'asdf'
SELECT * FROM my_table WHERE id=@ID

需要~14 sek。

临时变量的执行计划:

select <- Clustered Index Scan

常数执行计划:

select <- Nested Loops <- Index Seek (Non clustered)
                       <- Key Lookup (Clustered)

第一个问题: 为什么第一个查询执行的时间要长得多? SQL Server Profiler告诉我,第一个查询执行~800000次读取(cpu 5258),而第二次查询执行25次读取(cpu 0)。

第二个问题: 如果我将OPTION(RECOMPILE)应用于第一个查询,执行将快速执行,执行计划就像第二个,但我不明白为什么。
变量是否针对搜索中的每个条目进行评估?

1 个答案:

答案 0 :(得分:1)

当SQL Server决定使用群集索引扫描或带有密钥查找的索引查找时,它会查看将返回的估计行数。

当您在查询中将值作为常量提供时,SQL Server可以使用该值并查看索引的统计信息,以大致查看该值将返回的行数,在您的情况下,值asdf将具有足够的选择性,因此搜索/查找将是获取数据的最快方式。

当您使用变量来保存值时,SQL Server(在构建查询计划时)看不到变量最终将在执行时具有的值。在这种情况下,估计的行数将是基于id上的索引的统计信息返回的某些平均行数。在您的情况下,估计的行数太高,SQL Server无法考虑查找/查找查询计划。

当您使用option (recompile)时,在创建查询计划时已知参数的值,因此SQL Server可以在估计行数时使用实际值。

如果您决定使用存储过程@id是一个参数,那么SQL Server能够在编译时使用@id的值(参数嗅探)并为您提供该值的最佳计划。请注意,存储过程的查询计划是高速缓存的,因此如果您稍后使用扫描速度更快的值调用该过程,它仍将使用搜索/查找执行查询。当然,如果第一次执行过程的参数值将返回许多行,则只要计划位于缓存中,存储过程的计划就会执行扫描并继续执行此操作。