10

UNIONまた、クエリは、特定の状況下で -connected 述語UNION ALLを使用して同等のクエリよりも優れたパフォーマンスを発揮する場合があります。OR私の知る限り、これは部分的には、UNIONサブセレクトを並行して実行できるため、OR-connected 述語の各部分に固有の独自の「サブプラン」を持つことができるためです。これは、適用可能なクエリ変換がより単純であるため、おそらくはるかに最適です。

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

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 を使用できます。

4

2 に答える 2

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;
/

次に、TOAD で次のクエリを説明しました ( 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                                

そのため、ヒントが機能していないようです。次に、x & y 列にインデックスを追加しました。

create index test_x on test (x, y);

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

今すぐクエリを再実行します:

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                                

インデックスを追加した後 (使用されていなくても)、オプティマイザは結局ヒントを使用することを決定したようです!

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

于 2012-05-08T14:41:14.700 に答える
0

場合によっては、オプティマイザーがヒントを無視し、別の方法でクエリを作成する必要があります。UNION ALL を使用してすべてのクエリを書き換える代わりに、句のみを書き換えることができます。

SELECT * FROM a /* ... you can put here more joins with many tables ... */
WHERE a.rowid in (
    select innerQry.rowid from a innerQry where /*your first clause of OR*/innerQry.x = 1
    union all
    select innerQry.rowid from a innerQry where /*your second clause of OR*/innerQry.y = 1
)
于 2018-07-23T09:56:40.417 に答える