如何按值传递sql函数参数

时间:2012-07-01 10:27:10

标签: sql sql-server sql-server-2005 parameter-passing

这是更大选择的一部分,但我已将其删除至基本问题:

比较这两个SQL查询 - 第一个使用常量,第二个使用变量,两者都具有相同的值(假设为180)。具有常量显示结果的那个立即显示(例如在几毫秒内),具有该变量的那个需要几秒钟才能产生相同的结果。

渔获物在哪里?

查询1:

SELECT *
FROM table
WHERE field > 180

查询2:

DECLARE @V INT
SET @v = 180
SELECT *
FROM table
WHERE field > @v

2 个答案:

答案 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或多或少地解释了我的做法:

(开始引用)

  • 常量是常量,当查询包含常量时,SQL Server可以使用具有完全信任的常量的值,甚至可以使用这样的快捷方式来完全不访问表,如果它可以从约束中推断出不会返回任何行。
  • 对于参数,SQL Server不知道运行时值,但在编译查询时“嗅探”输入值。
  • 对于局部变量,SQL Server根本不知道运行时值,并应用标准假设。 (假设取决于运算符以及可以从唯一索引的存在推断出什么。)

(结束语)

他进一步详细阐述了参数嗅探和执行计划,我并不羞于承认我什么都不懂( - :) - 但总而言之,它类似于编程语言的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

这就是全部 - 对于迟到的回应感到抱歉但是我在最后几天非常忙碌...... 名机