UNION与WHERE子句
我在Oracle数据库上做了两个查询的UNION
。 他们都有一个WHERE
子句。 如果在UNION
执行查询之后执行WHERE
子句后执行UNION
,那么性能是否有所不同?
例如:
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
我相信在第二种情况下,它会对影响性能的两个表执行全表扫描。 那是对的吗?
以我的经验,Oracle非常善于推动简单的谓词。 以下testing是在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)中看到的那样,谓词在sorting(联合)之前被下推并应用。
我无法让优化器按下整个子查询,如
where a = (select max(a) from empty_table)
或join。 有适当的PK / FK限制,这可能是可能的,但显然有限制:)
只是谨慎
如果你尝试过
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,使其成为一个非常不同的查询。 如果列以这种方式被混淆,它会变得混乱。
注意:虽然我的build议多年前是正确的,但是Oracle的优化器已经得到改进,所以这里的位置绝对不再重要。 不过,首选UNION ALL
和UNION
总是成立,并且可移植SQL应避免取决于可能不在所有数据库中的优化。
简而言之,你需要UNION
之前的WHERE
,如果可能,你想使用UNION ALL
。 如果你正在使用UNION ALL
那么检查EXPLAIN输出,Oracle可能足够聪明,以便在之后留下来优化WHERE
条件。
原因如下。 UNION
的定义说,如果两个数据集中有重复的地方,他们必须被删除。 因此在该操作中有一个隐式的GROUP BY
,这往往是缓慢的。 更糟糕的是,Oracle的优化器(至less在3年前,我认为并没有改变)不会尝试通过GROUP BY
(隐式或显式)推送条件。 因此,Oracle必须构build比必要的更大的数据集,对它们进行分组,然后才能进行过滤。 因此,任何可能的预过滤都是一个好主意。 (顺便说一句,为什么只要有可能就把条件放在WHERE
而不是把它们留在HAVING
子句中是很重要的。)
此外,如果您碰巧知道两个数据集之间不会有重复,请使用UNION ALL
。 这就像UNION
一样连接数据集,但是并不试图对数据进行重复数据删除。 这节省了昂贵的分组操作。 根据我的经验,能够利用这一操作是相当普遍的。
由于UNION ALL
中没有隐式的GROUP BY
,因此Oracle的优化器可能知道如何通过它来推送条件。 我没有甲骨文坐在testing,所以你需要自己testing。
您需要查看解释计划,但除非在COL_A上有INDEX或PARTITION,否则您正在查看两个表上的FULL TABLE SCAN。
考虑到这一点,你的第一个例子是抛出一些数据,因为它做的全表扫描。 结果是由UNIONsorting,然后重复的数据被丢弃。 这给你你的结果集。
在第二个例子中,你正在拉两个表的全部内容。 这个结果可能会更大。 所以UNIONsorting更多的数据,然后删除重复的东西。 然后filter正在被应用,给你你后面的结果集。
作为一般规则,越早过滤数据,数据集越小,获得结果的速度越快。 一如既往,你的微软可能会有所不同。
我会确保你有一个ColA的索引,然后运行它们并计时。 那会给你最好的答案。
我认为这将取决于很多事情 – 运行EXPLAIN PLAN
,看看你的优化器select了什么。 否则 – 就像@rayman所暗示的 – 运行它们并计时。
SELECT * FROM (SELECT colA, colB FROM tableA UNION SELECT colA, colB FROM tableB) as tableC WHERE tableC.colA > 1
如果我们在两个表中使用包含相同字段名称的联合,那么我们需要为子查询命名为tableC(在上面的查询中)。 最后, WHERE
条件应该是WHERE tableC.colA > 1
SELECT * FROM (SELECT * FROM can UNION SELECT * FROM employee) as e WHERE e.id = 1;
SELECT colA, colB FROM tableA WHERE colA > 1 UNION SELECT colX, colA FROM tableB