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 ALLUNION总是成立,并且可移植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