我在UNIONOracle数据库上进行了两次查询.他们俩都有一个WHERE条款.是否有性能有区别,如果我这样做了WHERE之后,UNION荷兰国际集团的查询相比,在执行UNION后WHERE的条款?
例如:
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约束,这可能是有可能的,但显然有局限性:)
注意:虽然多年前我的建议是正确的,但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,所以你需要自己测试一下.
只是一个警告
如果你试过
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,使其成为一个非常不同的查询.如果以这种方式对列进行别名,则可能会让人感到困惑.
您需要查看解释计划,但除非COL_A上有INDEX或PARTITION,否则您正在查看两个表上的FULL TABLE SCAN.
考虑到这一点,您的第一个示例是抛出一些数据,就像完全表扫描一样.该结果由UNION排序,然后删除重复数据.这为您提供了结果集.
在第二个示例中,您将提取两个表的全部内容.结果可能会更大.所以UNION正在排序更多的数据,然后丢弃重复的东西.然后应用过滤器为您提供您所追求的结果集.
作为一般规则,越早过滤数据,数据集越小,获得结果的速度就越快.与往常一样,您的milage可能会有所不同.