WHERE 子句中的优化似乎不适用于 UNION

Posted

技术标签:

【中文标题】WHERE 子句中的优化似乎不适用于 UNION【英文标题】:Optimization in WHERE clause seems to be not working with UNION 【发布时间】:2020-10-30 15:30:37 【问题描述】:

我尝试了以下格式的 SQL 脚本。 (有多个连接可生成 set1、set2 和 set3,但为简单起见将其删除)。

SELECT * FROM (
    SELECT * FROM Set1
    WHERE someConditions
    UNION
    SELECT * FROM Set2
    WHERE someConditions
    UNION 
    SELECT * FROM Set3
    WHERE @include = 1 AND otherConditions..
) as t
ORDER BY ...

人们会期望当@include = 0 时,最后一个UNION 的整体将被忽略。这也是我在查找有条件地执行UNION 时看到的。

但对我来说不是这样;即使@include = 0,查询也需要很长时间才能执行。如果我注释掉最后一部分,查询将执行得更快。

SELECT * FROM (
    SELECT * FROM Set1
    WHERE someConditions
    UNION
    SELECT * FROM Set2
    WHERE someConditions
    --UNION 
    --SELECT * FROM Set3
    --WHERE @include = 1 AND otherConditions..
) as t
ORDER BY ...

为什么编译器无法识别它?有没有办法在这种情况下优化查询?

【问题讨论】:

也许试试option (recompile)? 或者使用动态 SQL 并创建一个只包含您需要的部分的查询。另外,您真的需要UNION,还是UNION ALL 仍然会产生正确的结果? UNION 是一个成本更高的运算符,只有在您确实需要时才应使用它。 只是一种预感,但您可以尝试用“Union All”替换所有“Union”,然后将外部“select”转换为“select distinct”。查询引擎可能更有可能以这种方式分别处理三个查询组件。 option (recompile)UNION ALL 对我没有任何影响。可能不得不使用动态 SQL 作为最后的手段。 @eshirvana 不;最后为整个查询添加。我们不能为内部查询添加recompile,可以吗?这会导致语法错误。 【参考方案1】:

对我来说,OPTION(RECOMPILE) 在类似的测试设置上运行良好 - 在没有提示的情况下重用糟糕的计划(假设之前使用 @include = 1 运行),但在使用 RECOMPILE 时创建一个新的、更好的计划。

如果这对您不起作用,您可以尝试使用 IF 拆分成两个不同的语句,例如:

BEGIN
IF @include = 1 
BEGIN

    SELECT * FROM Set1
    WHERE someConditions
    UNION
    SELECT * FROM Set2
    WHERE someConditions
    UNION 
    SELECT * FROM Set3
    WHERE otherConditions..

END

ELSE 
BEGIN
    SELECT * FROM Set1
    WHERE someConditions
    UNION
    SELECT * FROM Set2
    WHERE someConditions
END
END

【讨论】:

出于某种原因,重新编译对我没有帮助。但我喜欢这种拆分的想法,即使它可能不利于维护。

以上是关于WHERE 子句中的优化似乎不适用于 UNION的主要内容,如果未能解决你的问题,请参考以下文章

ADD_DATE() 不适用于 mysql 中的 where 子句

SQL 优化 where 子句中的条件

优化where子句中的SQL子查询

Laravel UNION ALL 不适用于 where 语句

WHERE 子句中的 SQL 查询子选择优化 (SQL Server)

PreparedStatement 不适用于 Java 中的 Sybase IQ