web-dev-qa-db-ja.com

UNION with WHERE句

Oracleデータベースで2つのクエリの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

2番目のケースでは、パフォーマンスに影響する両方のテーブルで全テーブルスキャンを実行すると考えています。あれは正しいですか?

41
MNIK

私の経験では、Oracleはsimple述語をプッシュするのが得意です。次のテストは、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制約が適切に設定されていれば可能かもしれませんが、明らかに制限があります:)

20
Ronnis

ただの注意

試したなら

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

次に、2番目のクエリでは、where句のcolAに実際にはtableBのcolXが含まれるため、まったく異なるクエリになります。列がこの方法でエイリアスされている場合、混乱する可能性があります。

9
Gary Myers

注:私のアドバイスは何年も前に真実でしたが、Oracleのオプティマイザーは改善されており、ここでの場所の位置はもはや重要ではありません。ただし、UNION ALL vs UNION 、およびポータブルSQLは、すべてのデータベースにはない可能性のある最適化に依存しないようにする必要があります。

簡単な答えは、WHEREの前にUNIONが必要であり、可能な場合はUNION ALLを使用することです。 UNION ALLを使用している場合は、EXPLAIN出力を確認します。Oracleは、WHERE条件が残っている場合に最適化するのに十分なほど賢い場合があります。

その理由は次のとおりです。 UNIONの定義は、2つのデータセットに重複がある場合、それらを削除する必要があることを示しています。したがって、その操作には暗黙的にGROUP BYがあり、これは遅くなる傾向があります。さらに悪いことに、Oracleのオプティマイザー(少なくとも3年前、および変更されたとは思わない)は、GROUP BY(暗黙的または明示的)を介して条件をプッシュしようとしません。そのため、Oracleは必要以上に大きなデータセットを作成し、グループ化して、フィルター処理を行う必要があります。したがって、可能な限り事前フィルタリングすることは公式には良いアイデアです。 (これは、偶然、WHERE句に条件を残すのではなく、可能な限りHAVINGに条件を入れることが重要な理由です。)

さらに、2つのデータセットの間に重複がないことがわかった場合は、UNION ALLを使用します。これは、データセットを連結するという点でUNIONに似ていますが、データの重複排除を試みません。これにより、費用のかかるグループ化操作が節約されます。私の経験では、この操作を利用できることは非常に一般的です。

UNION ALLには暗黙のGROUP BYがないため、Oracleのオプティマイザーが条件をプッシュする方法を知っている可能性があります。テストするためにOracleが座っているわけではないので、自分でテストする必要があります。

9
btilly

EXPLAIN PLANを確認する必要がありますが、COL_AにINDEXまたはPARTITIONがない場合は、両方の表のFULL TABLE SCANを確認しています。

これを念頭に置いて、最初の例は、FULL TABLE SCANを実行するときにデータの一部を破棄することです。その結果はUNIONによってソートされ、重複データはドロップされます。これにより、結果セットが得られます。

2番目の例では、両方のテーブルの内容全体をプルしています。その結果は大きくなる可能性があります。そのため、UNIONはより多くのデータをソートしてから、重複するものを削除しています。次に、フィルタを適用して、目的の結果セットを提供します。

一般的なルールとして、データをより早くフィルタ除去するほど、データセットは小さくなり、結果をより速く得ることができます。いつものように、あなたの走行距離は異なる場合があります。

7
EvilTeach

ColAにインデックスがあることを確認してから、両方を実行して時間を計ります。それはあなたに最高の答えを与えるでしょう。

2
rayman86

私はそれが多くのものに依存すると思う-EXPLAIN PLAN各オプティマイザーが選択するものを確認します。それ以外の場合-@raymanが示唆するように-両方を実行し、時間を計ります。

1
Randy
SELECT * FROM (SELECT colA, colB FROM tableA UNION SELECT colA, colB FROM tableB) as tableC WHERE tableC.colA > 1

2つのテーブルに同じフィールド名を含むユニオンを使用している場合、サブクエリにtableC(上記のクエリ)として名前を付ける必要があります。最後に、WHERE条件はWHERE tableC.colA > 1

0