带有WHERE子句的UNION

MNI*_*NIK 42 sql oracle union

我在UNIONOracle数据库上进行了两次查询.他们俩都有一个WHERE条款.是否有性能有区别,如果我这样做了WHERE之后,UNION荷兰国际集团的查询相比,在执行UNIONWHERE的条款?

例如:

SELECT colA, colB FROM tableA WHERE colA > 1
UNION
SELECT colA, colB FROM tableB WHERE colA > 1
Run Code Online (Sandbox Code Playgroud)

相比:

SELECT * 
  FROM (SELECT colA, colB FROM tableA
        UNION
        SELECT colA, colB FROM tableB) 
 WHERE colA > 1
Run Code Online (Sandbox Code Playgroud)

我相信在第二种情况下,它会对影响性能的两个表执行全表扫描.那是对的吗?

Ron*_*nis 20

根据我的经验,Oracle非常擅长推动简单的谓词.以下测试是在Oracle 11.2上进行的.我相当肯定它在10g的所有版本上都会产生相同的执行计划.

(请大家,如果您运行的是早期版本并尝试以下操作,请随时发表评论)

create table table1(a number, b number);
create table table2(a number, b number);

explain plan for
select *
  from (select a,b from table1
        union 
        select a,b from table2
       )
 where a > 1;

select * 
  from table(dbms_xplan.display(format=>'basic +predicate'));

PLAN_TABLE_OUTPUT
---------------------------------------
| Id  | Operation            | Name   |
---------------------------------------
|   0 | SELECT STATEMENT     |        |
|   1 |  VIEW                |        |
|   2 |   SORT UNIQUE        |        |
|   3 |    UNION-ALL         |        |
|*  4 |     TABLE ACCESS FULL| TABLE1 |
|*  5 |     TABLE ACCESS FULL| TABLE2 |
---------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------    
   4 - filter("A">1)
   5 - filter("A">1)
Run Code Online (Sandbox Code Playgroud)

正如您在步骤(4,5)中看到的那样,谓词被推下并在排序(联合)之前应用.

我无法让优化器按下整个子查询,例如

 where a = (select max(a) from empty_table)
Run Code Online (Sandbox Code Playgroud)

或加入.有适当的PK/FK约束,这可能是有可能的,但显然有局限性:)


bti*_*lly 9

注意:虽然多年前我的建议是正确的,但Oracle的优化器已经改进,因此这里的位置绝对不再重要.但是,首选UNION ALLvs UNION将永远是真的,并且可移植SQL应该避免依赖于可能不在所有数据库中的优化.

简短的回答,如果可能的话,你想要WHERE之前UNION和你想要使用UNION ALL.如果你正在使用UNION ALL那么检查EXPLAIN输出,Oracle可能足够聪明,WHERE如果它被遗留后优化条件.

原因如下.a的定义UNION表明,如果两个数据集中存在重复项,则必须将其删除.因此,GROUP BY在该操作中存在隐含的,其往往是缓慢的.更糟糕的是,Oracle的优化器(至少在3年前,我认为它没有改变)并不试图通过GROUP BY(隐式或显式)推动条件.因此,Oracle必须构建比必要的更大的数据集,对它们进行分组,然后才能进行过滤.因此,只要有可能,预过滤就是一个好主意.(顺便说一下,为什么在WHERE任何可能的情况下尽可能地设置条件而不是将它们留在HAVING条款中是很重要的.)

此外,如果您碰巧知道两个数据集之间不会有重复,那么请使用UNION ALL.这就像UNION它连接数据集,但它不会尝试重复数据删除.这节省了昂贵的分组操作.根据我的经验,能够利用这种操作是很常见的.

由于UNION ALL没有隐含GROUP BY的内容,Oracle的优化器可能知道如何通过它推动条件.我没有坐在那里测试Oracle,所以你需要自己测试一下.

  • @JonHeller 您在 2016 年回复了 2011 年写的一篇帖子,该帖子根据我几年前的经验明确标记为。很多事情都可以改变,简洁地说明什么比毯子更有帮助,“那是错误的”。 (3认同)

Gar*_*ers 9

只是一个警告

如果你试过

SELECT colA, colB FROM tableA WHERE colA > 1
UNION
SELECT colX, colA FROM tableB WHERE colA > 1
Run Code Online (Sandbox Code Playgroud)

相比:

SELECT * 
  FROM (SELECT colA, colB FROM tableA
        UNION
        SELECT colX, colA FROM tableB) 
 WHERE colA > 1
Run Code Online (Sandbox Code Playgroud)

然后在第二个查询中,where子句中的colA实际上将具有来自tableB的colX,使其成为一个非常不同的查询.如果以这种方式对列进行别名,则可能会让人感到困惑.


Evi*_*ach 7

您需要查看解释计划,但除非COL_A上有INDEX或PARTITION,否则您正在查看两个表上的FULL TABLE SCAN.

考虑到这一点,您的第一个示例是抛出一些数据,就像完全表扫描一样.该结果由UNION排序,然后删除重复数据.这为您提供了结果集.

在第二个示例中,您将提取两个表的全部内容.结果可能会更大.所以UNION正在排序更多的数据,然后丢弃重复的东西.然后应用过滤器为您提供您所追求的结果集.

作为一般规则,越早过滤数据,数据集越小,获得结果的速度就越快.与往常一样,您的milage可能会有所不同.


小智 2

我会确保你在 ColA 上有一个索引,然后运行它们并计时。那会给你最好的答案。

  • 我没有投反对票,但这可能是对普遍存在的“添加索引来解决任何性能问题”心态的反应。 (2认同)