将可选参数优化传递到查询中

Posted

技术标签:

【中文标题】将可选参数优化传递到查询中【英文标题】:Optimal passing of optional parameters into a query 【发布时间】:2015-04-26 13:24:45 【问题描述】:

我有一个带有可选参数的参数化查询。 连接了多个表。 WHERE 子句的一部分如下所示:

and ((x.a = @arg1) OR (@arg1 IS NULL))
and ((y.b = @arg2) OR (@arg2 IS NULL))
and ((z.c = @arg3) OR (@arg3 IS NULL))

所以,想法是:一个参数可以用于应用过滤器,或者,如果参数为 NULL,则不应用过滤器。

但是,我发现执行计划对这段代码不好。 实际设置参数时,最好写成

and x.a = @arg1

而不是

and ((x.a= @arg1) OR (@arg1 IS NULL))

实际上,我总共有 8 个表连接在一起。在这两个语句中,所有 8 个表都已连接,并且在所有这些表上应用了相同的索引查找/扫描。但是join顺序不同,导致执行速度不同。

有没有办法重写上面的语句,使得执行计划可以优化?可能有一些查询提示?

或者没有办法编写动态SQL?我想避免后者,因为

动态 SQL 难以阅读, SSMS 不显示依赖项, 将参数传递给动态 SQL 很糟糕

【问题讨论】:

从性能角度来看,动态 SQL 是该任务的最佳解决方案。尽管您对依赖关系是正确的,但如果遵循良好的编码实践,应该不难阅读。不确定我是否了解传递参数的困难。 参数传递到 DynSQL 并不是很困难,我只是不喜欢你需要通过为每个“外部”参数创建一个“内部”参数来将每个参数“隧道”到 DynSQL 中的方式,并且然后将整个事情传递给 exec 函数。但是很好。 ;) 您可以在传递给 sp_executesql 的声明字符串中声明所有 inside 参数,并无条件映射 inside/outside 参数值。性能的关键是有条件地构建 WHERE 子句,以便只指定搜索实际需要的谓词。 【参考方案1】:

尝试使用COALESCE(@arg1, x.a) 而不是((x.a = @arg1) OR (@arg1 IS NULL))

and x.a = COALESCE(@arg1, x.a)
and y.b = COALESCE(@arg2, xyb)
and z.c = COALESCE(@arg3, z.c)

【讨论】:

好主意,但结果相同:( 那我猜x.a = CASE WHEN @arg1 IS NULL THEN x.a ELSE @arg1 END 也无济于事,但值得一试。你能发布执行计划吗?无论你做什么,动态 sql 都应该被视为最后的手段。 是的,CASE WHEN 结果相同。实际语句连接了8张表,所以执行计划比较大。会尝试以某种方式发布它。 在这两种情况下,所有 8 个表都已连接,并且在所有这些表上应用了完全相同的索引查找/扫描。但是连接顺序不同,导致执行速度不同。 优化一个你一无所知的查询非常困难。 perhaps this link will help.【参考方案2】:

不幸的是,优化器倾向于坚持任何适合第一次调用的计划。您最好的选择是为每个组合(或至少是主要组合)编写一个存储过程,或者尝试OPTION(RECOMPILE),这将花费时间来评估实际参数,但需要一些成本。

要回答您关于“有没有办法重写上述语句,以使执行计划可以最佳工作?”的问题,答案可能不是。有太多的变化来提出一个单一的计划,其中有很多可能不会发生的事情(你的空变量)。

【讨论】:

是的,OPTION (RECOMPILE) 很好!与COALESCE 变体[@ZoharPeled] 结合使用,它会产生与手写变体相同的执行计划。只有重新编译时间花费了大约 70 毫秒的额外时间,这是一个缺点。所以我会在这里坚持所有答案的组合。

以上是关于将可选参数优化传递到查询中的主要内容,如果未能解决你的问题,请参考以下文章

python如何将可选参数或关键字参数从一个函数传递到另一个函数?

如何将可选参数传递给 C++ 中的方法?

将可选路径参数传递给 ant

s-s-rS 和 PostgreSQL:如何传递可选参数?

检查内部方法是不是传递了一些可选参数

getopt 不将可选参数解析为参数