2012-05-08 10 views
10

UNION e UNION ALL le query possono superare le query equivalenti utilizzando OR - predicati connessi in determinate circostanze. A mia conoscenza, questo è parzialmente dovuto al fatto che i sottolettori UNION possono essere eseguiti in parallelo e possono quindi avere il proprio "piano secondario" specifico per ciascuna parte del predicato connesso OR, che è probabilmente molto più ottimale a causa di trasformazioni di query applicabili più semplici .Consentire a Oracle di trasformare i predicati collegati all'OR in operazioni UNION ALL

Tuttavia, la scrittura di predicati collegati è in genere molto più leggibile e concisa, anche se il factoring delle subquery è stato applicato a una soluzione UNION ALL. La mia domanda è: c'è un modo per indicare a Oracle, che un singolo, costoso predicato connesso OR deve essere trasformato in un'operazione UNION ALL? Se esiste un tale suggerimento/metodo, in quali circostanze può essere applicato (ad esempio, devono essere presenti eventuali vincoli sulle colonne coinvolte nei predicati, ecc.)? Un esempio:

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 

nota, sono a conoscenza del /*+ USE_CONCAT */ suggerimento:

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

Ma non sembra produrre quello che mi serve (non forzata UNION ALL operazione nel piano di esecuzione):

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

Forse, c'è qualche restrizione a questo suggerimento? Ho Oracle 11g2 disponibile per questo.

+0

Quante righe (in% di tutte le righe) restituiranno la condizione 'x = 1 o y = 1' (nella tabella reale)? Che ne pensi di usare l'hint 'PARALLEL' nella query" o "? –

+0

@a_horse_with_no_name: In realtà, la condizione (reale) è della forma '(flag_function() = 1 e condition1) o (flag_function() = 0 e condition2)'. Le due sotto-condizioni si escludono a vicenda a seconda di un 'flag_function() 'di PL/SQL. Ho notato che Oracle crea un piano molto migliore per questo quando si usa 'UNION ALL' piuttosto che quando si usa' OR'. 'PARALLEL' probabilmente non aiuterà molto, in quanto la quantità di dati non è così grande in questo caso, ma il piano è complesso ... Inoltre, questa query viene eseguita molto frequentemente nelle sessioni utente.Non vorrei ingannare troppe risorse con i suggerimenti 'PARALLEL' –

+1

Queste query non sono equivalenti. È necessario utilizzare UNION anziché UNION ALL. Otterrai risultati diversi quando avrai righe in cui x e y uguali 1. – GriffeyDog

risposta

3

Credo che questo possa avere qualcosa a che fare con gli indici esistenti sulle colonne che si usano nel predicato OR.

Ho provato utilizzando quanto segue in 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; 
/

Ho poi spiegato le seguenti query nel ROSPO, (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         

così appare il suggerimento non funziona. Ho poi aggiunto un indice dei x & colonne y:

create index test_x on test (x, y); 

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

rieseguire la query ora:

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         

Sembra che dopo l'aggiunta dell'indice (anche se è non viene utilizzato) l'ottimizzatore Dopotutto, ho deciso di usare il suggerimento!

Forse potresti provare questo?

+0

Grazie mille per questa analisi! In effetti non ci sono indici su (le colonne reali rappresentate da) 'x' e' y'. Proverò ad aggiungere un indice su 'x'. 'y', tuttavia, non può essere indicizzato facilmente nella query reale, poiché proviene da un' LEFT OUTER JOIN' ... –

+0

L'ottimizzatore è pignolo su queste cose. Potresti scoprire che potrebbe anche provare a eseguire una conversione bitmap in rowid con una bitmap OPPURE se indicizzi ciascuna colonna in modo indipendente. –

+0

OK, vedo, proverò a dare un seguito a quelle cose. Dopo alcune indagini, si scopre che il 'CONCATENATION' è * realmente * applicato alla query reale (ho fatto un errore). Ma hai comunque risposto alla mia domanda semplificata, che sembra essere eccessivamente semplificata ... :) –