我在Oracle数据库上进行了两次UNION
次查询。它们都有一个WHERE
子句。如果我WHERE
UNION
UNION
与执行WHERE
SELECT colA, colB FROM tableA WHERE colA > 1
UNION
SELECT colA, colB FROM tableB WHERE colA > 1
子句之后的SELECT *
FROM (SELECT colA, colB FROM tableA
UNION
SELECT colA, colB FROM tableB)
WHERE colA > 1
相比,性能是否存在差异?
例如:
{{1}}
与之相比:
{{1}}
我相信在第二种情况下,它会对影响性能的两个表执行全表扫描。这是对的吗?
答案 0 :(得分:19)
根据我的经验,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)
正如您在步骤(4,5)中看到的那样,谓词被推下并在排序(联合)之前应用。
我无法让优化器按下整个子查询,例如
where a = (select max(a) from empty_table)
或加入。如果有适当的PK / FK约束,可能会有,但显然存在局限性:)
答案 1 :(得分:9)
注意:虽然多年前我的建议是正确的,但Oracle的优化器已经改进,因此这里的位置绝对不再重要。但是,首选UNION ALL
vs UNION
始终是正确的,并且可移植SQL应避免依赖可能不在所有数据库中的优化。
简短回答,您希望在WHERE
之前使用UNION
,并且如果可能的话,您想要使用UNION ALL
。如果您正在使用UNION ALL
然后检查EXPLAIN输出,那么Oracle可能足够聪明,可以优化WHERE
条件,如果它被遗留下来。
原因如下。 UNION
的定义表明,如果两个数据集中存在重复项,则必须将其删除。因此,该操作中存在隐式GROUP BY
,这往往很慢。更糟糕的是,Oracle的优化器(至少从3年前开始,我认为它没有改变)并没有试图通过GROUP BY
(隐式或显式)来推动条件。因此,Oracle必须构建比必要更大的数据集,对它们进行分组,然后才能进行过滤。因此,只要有可能,预过滤就是一个好主意。 (顺便说一下,为什么尽可能将条件放在WHERE
而不是将它们放在HAVING
子句中是很重要的。)
此外,如果您碰巧知道两个数据集之间不会有重复,那么请使用UNION ALL
。这就像UNION
一样,它连接数据集,但它不会尝试重复数据删除。这节省了昂贵的分组操作。根据我的经验,能够利用这一操作是很常见的。
由于UNION ALL
中没有隐式GROUP BY
,因此Oracle的优化器可能知道如何通过它推送条件。我没有坐在那里测试Oracle,所以你需要自己测试一下。
答案 2 :(得分:9)
小心点
如果你试过
SELECT colA, colB FROM tableA WHERE colA > 1
UNION
SELECT colX, colA FROM tableB WHERE colA > 1
与之相比:
SELECT *
FROM (SELECT colA, colB FROM tableA
UNION
SELECT colX, colA FROM tableB)
WHERE colA > 1
然后在第二个查询中,where子句中的colA实际上将具有来自tableB的colX,使其成为一个非常不同的查询。如果列以这种方式存在别名,则可能会让人感到困惑。
答案 3 :(得分:7)
您需要查看解释计划,但除非COL_A上有INDEX或PARTITION,否则您正在查看两个表上的FULL TABLE SCAN。
考虑到这一点,你的第一个例子是抛出一些数据,就像它完成FULL TABLE SCAN一样。该结果由UNION排序,然后删除重复数据。这为您提供了结果集。
在第二个示例中,您将提取两个表的全部内容。结果可能会更大。所以UNION正在排序更多数据,然后丢弃重复的东西。然后应用过滤器为您提供您所追求的结果集。
作为一般规则,越早过滤数据,数据集越小,获得结果的速度就越快。与往常一样,您的milage可能会有所不同。
答案 4 :(得分:1)
我会确保你有一个关于ColA的索引,然后运行它们并计时。这会给你最好的答案。
答案 5 :(得分:1)
我认为这将取决于许多事情 - 在每个事件上运行EXPLAIN PLAN
以查看优化程序选择的内容。否则 - 正如@rayman建议的那样 - 同时运行它们并计时。
答案 6 :(得分:0)
SELECT * FROM (SELECT colA, colB FROM tableA UNION SELECT colA, colB FROM tableB) as tableC WHERE tableC.colA > 1
如果我们在2个表中使用包含相同字段名称的union,那么我们需要将子查询的名称命名为tableC(在上面的查询中)。最后,WHERE
条件应为WHERE tableC.colA > 1
答案 7 :(得分:-4)
SELECT colA, colB FROM tableA WHERE colA > 1
UNION
SELECT colX, colA FROM tableB
答案 8 :(得分:-5)
SELECT *
FROM (SELECT * FROM can
UNION
SELECT * FROM employee) as e
WHERE e.id = 1;