将可选参数优化传递到查询中
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 毫秒的额外时间,这是一个缺点。所以我会在这里坚持所有答案的组合。以上是关于将可选参数优化传递到查询中的主要内容,如果未能解决你的问题,请参考以下文章