好吧-我被要求详细说明解决方案(我还看到“写好答案的技巧”)-让我尝试解释一下-尽管我只能解释效果-为了了解背景,必须自己动手进入提到的文章。
我遇到了每三分钟对某些数据进行快照的问题。(非常简化的)查询是
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())
只要我用常量调用它,这也很完美,例如
SELECT *
FROM GetSnapshot(180)
现在我想进一步参数化,因为 180 秒不适合所有目的。现在问题开始了:
DECLARE @v int
SET @v = 180
SELECT *
FROM GetSnapshot(@v)
运行近10 秒,而直接调用 180 需要毫秒
我还必须提到,简单的表格也有同样的效果——我调用函数的事实并没有影响结果。无论我尝试什么 - 十秒。
现在在完全绝望之前,我用标题中的问题求助于 stackoverflow 中的专家。我对编程语言中的参数传递了解很多——但在 SQL 中却一无所知。在 PL 中,如果您按值传递,编译器会生成代码以在运行时制作实际值的本地副本,并将其像常量一样传递给被调用函数 - 而按引用传递则“按原样”传递语言构造,然后被调用的过程可以一次又一次地“调用”这个参数 - 无论是变量还是函数调用或其他任何东西。因此,我的印象是,SQL 编译器通过值调用常量和通过引用调用变量——这需要被调用过程进行多次评估,并解释几万条记录的结果集的长时间运行。
在提到的文章中,Erland 或多或少地以我的方式解释了它:
(开始报价)
- 常量就是常量,当查询包含常量时,SQL Server 可以完全信任地使用常量的值,甚至可以使用这样的捷径根本不访问表,如果它可以从约束中推断出没有行会被退回。
- 对于参数,SQL Server 不知道运行时值,但它会在编译查询时“嗅探”输入值。
- 对于局部变量,SQL Server 根本不知道运行时值,并应用标准假设。(哪些假设取决于操作员以及可以从唯一索引的存在中推断出什么。)
(结束报价)
然后他进一步详细说明了参数嗅探和执行计划,我并不羞于承认我什么都不懂 (-:) - 但总而言之,它类似于编程语言的按值/被引用概念。
现在如何强制 SQL Server “按值调用”?
幸运的是,David 对Demystifying SQL Server 的提示:SQL Server Parameter Sniffing文章给出了我称赞的解决方案:使用 sp_executesql 包装器打包完整的“按引用”调用 - 在这种情况下,外部调用仍将是“按参考”,但由于参数解析是在包装级别完成的,因此内部调用可以“按值”完成,我们又回到了毫秒级的响应时间。
像这样使用它来制作技巧:
exec sp_executesql
N'SELECT * FROM GetSnapshot(@v)',
N'@v int',
@v=180
就是这样 - 抱歉回复晚了,但我最近几天很忙...... Meiki