这是更大选择的一部分,但我已将其删除至基本问题:
比较这两个SQL查询 - 第一个使用常量,第二个使用变量,两者都具有相同的值(假设为180)。具有常量显示结果的那个立即显示(例如在几毫秒内),具有该变量的那个需要几秒钟才能产生相同的结果。
渔获物在哪里?
查询1:
SELECT *
FROM table
WHERE field > 180
查询2:
DECLARE @V INT
SET @v = 180
SELECT *
FROM table
WHERE field > @v
答案 0 :(得分:2)
问题在于parameter sniffing。从提到的文章:
“当参数化查询使用缓存基数估计来做出查询计划决策时,会发生参数嗅探。当第一次执行具有非典型参数值时会出现问题。对于每次后续执行,优化器将假设估计值很好即使估计值可能偏离。例如,假设您有一个存储过程返回1到1000之间的所有id值。如果使用这么大范围的参数值执行存储过程,优化器将缓存这些非典型值值,间接导致优化器估计基数。问题是典型的执行可能只返回几行。这种“嗅探”可能导致查询扫描表反对寻找,因为优化器假定基数估计不准确。“< / em>的
答案 1 :(得分:0)
嗯 - 我被要求详细说明解决方案(我也看到"tips on writing great answers") - 让我试着解释一下 - 尽管我只能解释一下这个效果 - 为了理解背景,我必须要投入自己进入上述文章。
我遇到了每三分钟制作一些数据快照的问题。 (非常简化的)查询是
SELECT *
FROM table
WHERE TimeStamp > DateAdd(ss,-180,GetDate())
工作完美 - 把它放在一个功能中:
CREATE FUNCTION GetSnapshot (@ss int) RETURNS TABLE
AS
RETURN
SELECT *
FROM Table
WHERE TimeStamp > DateAdd(ss,-@ss,GetDate())
只要我用constanst称呼它,这也很有效。
SELECT *
FROM GetSnapshot(180)
现在我想进一步参数化,因为180秒不适合所有目的。 现在问题开始了:
DECLARE @v int
SET @v = 180
SELECT *
FROM GetSnapshot(@v)
几乎 10秒,而180直接调用需要几毫秒
我还要提到同样的效果与简单的表格相同 - 我调用函数的事实并没有影响结果。无论我怎么做 - 十秒钟。
现在在完全绝望之前,我转向了stackoverflow中的专家,标题中有问题。我对编程语言中的参数传递了解很多 - 但在SQL中却没有。在PL中,如果您通过值传递,编译器会生成代码以在运行时生成实际值的本地副本,并将其作为常量传递给被调用的函数 - 而按引用传递会按“原样”传递语言结构,而不是被调用的过程可以反复“调用”这个参数 - 无论是变量还是函数调用等等。因此,我的印象是SQL编译器通过值调用常量调用,并且通过引用调用变量 - 这需要被调用过程进行多次评估,并解释几万条记录的结果集上的长运行时间。
在mentioned article Erland或多或少地解释了我的做法:
(开始引用)
(结束语)
他进一步详细阐述了参数嗅探和执行计划,我并不羞于承认我什么都不懂( - :) - 但总而言之,它类似于编程语言的by value / be reference概念。 / p>
现在如何强制SQL Server“按值调用”?
幸运的是大卫对Demystifying SQL Server : SQL Server Parameter Sniffing文章的暗示给出了我赞扬的解决方案:使用sp_executesql包装器打包完整的“引用”调用 - 在这种情况下,外部调用仍然是“通过引用“但由于参数解析是在包装器级别完成的,因此内部调用可以通过值”完成“ 我们回到了几毫秒的响应时间。
像这样使用它来制作技巧:
exec sp_executesql
N'SELECT * FROM GetSnapshot(@v)',
N'@v int',
@v=180
这就是全部 - 对于迟到的回应感到抱歉但是我在最后几天非常忙碌...... 名机