2012-05-08 2 views
10

UNIONおよびUNION ALL問合せでは、特定の状況でOR接続述語を使用して同等の問合せより優れたパフォーマンスを得ることができます。私が知る限り、部分的にはUNIONの副選択を並行して実行することができるため、OR -connected述語の各部分に固有の独自の「サブプラン」を持つことができます。これはおそらく、 。ORはOR結合述語をUNION ALL操作に変換します。

OR接続述部の作成は、サブクエリの因数分解がUNION ALLソリューションに適用されていても、通常ははるかに読みやすく簡潔です。私の質問です:単一の、高価なOR接続された述語をUNION ALL操作に変換する必要があることをOracleに示す方法はありますか?そのようなヒント/メソッドがある場合は、どのような状況で適用できるか(例えば、述語などに含まれる列に制約が存在する必要があるなど)?例:

CREATE TABLE a AS 
    SELECT 1 x, 2 y FROM DUAL UNION ALL 
    SELECT 2 x, 1 y FROM DUAL; 

-- This query... 
SELECT * FROM a 
WHERE x = 1 OR y = 1 

-- Is sometimes outperformed by this one, for more complex table sources... 
-- Note: in my case, I can safely apply UNION ALL. I know the two predicates to 
-- be mutually exclusive. 
SELECT * FROM a 
WHERE x = 1 
UNION ALL 
SELECT * FROM a 
WHERE y = 1 

注、私は/*+ USE_CONCAT */ヒントを知ってる:

SELECT /*+ USE_CONCAT */ * FROM a 
WHERE x = 1 OR y = 1 

しかし、私が(実行計画には強制UNION ALL操作)必要ないものを生成していないようです。

------------------------------------------- 
| Id | Operation   | Name | E-Rows | 
------------------------------------------- 
| 0 | SELECT STATEMENT |  |  | 
|* 1 | TABLE ACCESS FULL| A |  2 | 
------------------------------------------- 

多分、このヒントにはいくつかの制限がありますか?私はOracle 11g2をこれに利用できるようにしました。

+0

'x = 1またはy = 1'の条件が(実際のテーブルの)何行返されますか(全行の%で)? "or"クエリで 'PARALLEL'ヒントを使うのはどうですか? –

+0

@a_horse_with_no_name:実際には、(実際の)条件は '(flag_function()= 1 and condition1)または(flag_function()= 0 and condition2)'の形式です。 2つのサブ条件は、PL/SQLの 'flag_function()'に応じてお互いを互いに排除します。私は、ORを使用するよりむしろ、 'UNION ALL'を使用するときにOracleがこれに対してより良い計画を作成することに気付きました。この場合、データの量がそれほど大きくないので、PARALLELはあまり役に立たないでしょうが、計画は複雑です...さらに、このクエリはユーザセッションで頻繁に実行されます。私は 'PARALLEL'ヒントを持つリソースをあまりにも多く使いたくないでしょう –

+1

これらのクエリは同等ではありません。 UNION ALLではなくUNIONを使用する必要があります。 xとyの両方が1に等しい行を持っていると、異なる結果が得られます。 – GriffeyDog

答えて

3

これは、OR述語で使用する列に存在するインデックスと関係していると思います。

私は11gR2で以下を使用してテストしました。

create table scott.test as 
select level l, 
     decode(mod(level,2), 1, 1, 2) x, 
     decode(mod(level,2), 1, 2, 1) y, 
     dbms_random.value(1, 3) z from dual 
connect by level < 1000; 
/

begin 
    dbms_stats.gather_table_stats('scott', 'test'); 
end; 
/

私はそのように、ヒントが動作していない表示されます(EXPLAIN PLAN FOR)、ヒキガエルに

select x, y, z from scott.test 
    where (floor(z) = 1 and x = 1) or (floor(z) = 2 and y = 1) 
    ; 

SELECT STATEMENT Optimizer Mode=ALL_ROWS  10   4         
    TABLE ACCESS FULL COS_DM.TEST 10  280  4 

select /*+ USE_CONCAT */ x, y, z from scott.test 
where (floor(z) = 1 and x = 1) or (floor(z) = 2 and y = 1) 
; 

SELECT STATEMENT Optimizer Mode=ALL_ROWS  10   4         
    TABLE ACCESS FULL COS_DM.TEST 10  280  4         


select x, y, z from test where (floor(z) = 1 and x = 1) 
union all 
select x, y, z from test where (floor(z) = 2 and y = 1) 
; 

SELECT STATEMENT Optimizer Mode=ALL_ROWS  10   8         
    UNION-ALL            
    TABLE ACCESS FULL COS_DM.TEST 5 140  4         
    TABLE ACCESS FULL COS_DM.TEST 5 140  4         

を次のクエリを説明しました。今のクエリを再実行

create index test_x on test (x, y); 

begin 
    dbms_stats.gather_table_stats('scott', 'test'); 
end; 
/

:私は、その後のx & yの列にインデックスを追加しました

select x, y, z from scott.test 
    where (floor(z) = 1 and x = 1) or (floor(z) = 2 and y = 1) 
    ; 

SELECT STATEMENT Optimizer Mode=ALL_ROWS  10   4         
    TABLE ACCESS FULL COS_DM.TEST 10  280  4 

select /*+ USE_CONCAT */ x, y, z from scott.test 
where (floor(z) = 1 and x = 1) or (floor(z) = 2 and y = 1) 
; 

SELECT STATEMENT Optimizer Mode=ALL_ROWS  10   8         
    CONCATENATION            
    TABLE ACCESS FULL COS_DM.TEST 5 140  4         
    TABLE ACCESS FULL COS_DM.TEST 5 140  4         

select x, y, z from test where (floor(z) = 1 and x = 1) 
union all 
select x, y, z from test where (floor(z) = 2 and y = 1) 
; 

SELECT STATEMENT Optimizer Mode=ALL_ROWS  10   8         
    UNION-ALL            
    TABLE ACCESS FULL COS_DM.TEST 5 140  4         
    TABLE ACCESS FULL COS_DM.TEST 5 140  4         

それが表示される(それはを使用されていないにもかかわらず)オプティマイザをインデックスを追加した後結局のところヒントを使うことに決めました!

おそらくあなたはこれを試すことができますか?

+0

ありがとうございました!実際、 'x'と' y'で(実際の列で表される)インデックスはありません。 'x'にインデックスを追加しようとします。しかし、 'y'は' LEFT OUTER JOIN'から始まるので、実際のクエリでは簡単には索引付けすることができません。 –

+0

オプティマイザは、これらのことに気をつけています。ビットマップを使用してROWIDにビットマップ変換を実行しようとするか、各列を個別に索引付けすることもできます。 –

+0

OK、わかりました、私はそれらのことをフォローアップしようとします。何らかの調査の後、実際の問い合わせに「CONCATENATION」が実際に適用されていることが分かりました(私は間違いを犯しました)。しかし、あなたはまだ私の単純化された質問に答えました。これは過度に単純化されているようです:) –

関連する問題