2015-12-18 12 views
12

Questa è una domanda di prestazioni semplificata per unire due indici. Prendere la seguente configurazione:MERGE JOIN su due indici causa ancora un SORT?

CREATE TABLE ZZ_BASE AS SELECT dbms_random.random AS ID, DBMS_RANDOM.STRING('U',10) AS STR FROM DUAL CONNECT BY LEVEL <=1000000; 
CREATE INDEX ZZ_B_I ON ZZ_BASE(ID ASC); 
CREATE TABLE ZZ_CHILD AS SELECT dbms_random.random AS ID, DBMS_RANDOM.STRING('U',10) AS STR FROM DUAL CONNECT BY LEVEL <=1000000; 
CREATE INDEX ZZ_C_I ON ZZ_CHILD(ID ASC); 

-- As @Flado pointed out, the following is required so index scanning can be done 
ALTER TABLE ZZ_BASE MODIFY (ID CONSTRAINT NN_B NOT NULL); 
ALTER TABLE ZZ_CHILD MODIFY (ID CONSTRAINT NN_C NOT NULL); -- given the join below not mandatory. 

Ora voglio ESTERNI DI SINISTRA unire queste due tavoli e solo l'uscita del campo ID già indicizzati.

SELECT ZZ_BASE.ID 
FROM ZZ_BASE 
LEFT OUTER JOIN ZZ_CHILD ON (ZZ_BASE.ID = ZZ_CHILD.ID); 
---------------------------------------------------------------------------------------- 
| Id | Operation    | Name | Rows | Bytes |TempSpc| Cost (%CPU)| Time  | 
---------------------------------------------------------------------------------------- 
| 0 | SELECT STATEMENT  |  | 1000K| 9765K|  | 4894 (2)| 00:00:30 | 
|* 1 | HASH JOIN OUTER  |  | 1000K| 9765K| 16M| 4894 (2)| 00:00:30 | 
| 2 | INDEX FAST FULL SCAN| ZZ_B_I | 1000K| 4882K|  | 948 (3)| 00:00:06 | 
| 3 | INDEX FAST FULL SCAN| ZZ_C_I | 1000K| 4882K|  | 948 (3)| 00:00:06 | 
---------------------------------------------------------------------------------------- 

Come si può vedere, non è necessario l'accesso alla tabella, solo l'accesso all'indice. Ma secondo il senso comune, l'unione di HASH non è il modo migliore per unire questi due indici. Se queste due tabelle erano molto più grandi, sarebbe necessario creare un hash table molto grande.

Un modo molto più efficiente sarebbe SORT-MERGE i due indici.

SELECT /*+ USE_MERGE(ZZ_BASE ZZ_CHILD) */ ZZ_BASE.ID 
FROM ZZ_BASE 
LEFT OUTER JOIN ZZ_CHILD ON (ZZ_BASE.ID = ZZ_CHILD.ID); 
----------------------------------------------------------------------------------------- 
| Id | Operation    | Name | Rows | Bytes |TempSpc| Cost (%CPU)| Time  | 
----------------------------------------------------------------------------------------- 
| 0 | SELECT STATEMENT  |  | 1000K| 9765K|  | 6931 (3)| 00:00:42 | 
| 1 | MERGE JOIN OUTER  |  | 1000K| 9765K|  | 6931 (3)| 00:00:42 | 
| 2 | INDEX FULL SCAN  | ZZ_B_I | 1000K| 4882K|  | 2258 (2)| 00:00:14 | 
|* 3 | SORT JOIN   |  | 1000K| 4882K| 22M| 4673 (4)| 00:00:29 | 
| 4 | INDEX FAST FULL SCAN| ZZ_C_I | 1000K| 4882K|  | 948 (3)| 00:00:06 | 
----------------------------------------------------------------------------------------- 

Ma sembra che il secondo indice viene ordinato, anche se lo è già ("Se esiste un indice, quindi il database può evitare l'ordinamento il primo set di dati. Tuttavia, il database ordina sempre il secondo set di dati , indipendentemente indici" 1)

in sostanza, quello che voglio è una query che utilizza una sort merge join e immediatamente inizia ad emettere i record, vale a dire:

  • senza hash join perché ha prima fare una tabella hash (overhead IO se memorizzata su disco) e quindi non viene emessa istantaneamente.
  • nessun NESTED LOOP join che, anche se emetterebbe immediatamente , ha log (N) complessità sulle poket dell'indice e un sovraccarico di I/O su letture di indice non sequenziali nel caso in cui l'indice sia grande.
+0

Oracle a volte esegue un pass-thru sort (che non fa niente ed è non bloccante) che viene etichettato qualcosa come NO_SORT, quindi suppongo che potrebbe essere applicato in questo caso (o almeno dovrebbe essere). –

+0

Anche il commento che hai citato (fonte?) Si applica alla scansione della tabella, non necessariamente alla scansione dell'indice –

+2

_large sovraccarico di IO su letture di indice non sequenziali nel caso in cui l'indice sia grande._ La lettura di un indice nell'ordine di indice, scansione completa dell'indice, è anche IO non sequenziale a livello del disco. La scansione completa rapida dell'indice può eseguire l'I/O sequenziale, ma ottiene i dati in un ordine non ordinato. L'indice non è disposto nell'ordine sul disco. –

risposta

1

INDEX_ASC (o solo INDICE) è il suggerimento che si potrebbe voler provare per confrontare le prestazioni con i dati reali.

Sono un po 'sorpreso di ottenere qualsiasi tipo di scansione dell'indice per la sorgente della riga esterna, poiché gli indici B * Tree non riescono a trovare chiavi NULL e ZZ_BASE non ha un vincolo NOT NULL. Aggiungendo questo e suggerendo un po 'di più si otterrà la scansione completa nell'indice dell'indice ZZ_C_I. Questo non ti salva il passaggio SORT JOIN, sfortunatamente, ma almeno dovrebbe essere molto più veloce - O (n) - poiché i dati sono già ordinati.

alter table zz_base modify (id not null); 
SELECT 
    /*+ leading(zz_base) USE_MERGE(ZZ_CHILD) 
     index_asc(zz_base (id)) index(zz_child (id)) */ ZZ_BASE.ID 
FROM ZZ_BASE left outer join ZZ_CHILD on zz_base.id=zz_child.id; 

Questa query utilizza il seguente piano di esecuzione:

------------------------------------------------------------------------------------ 
| Id | Operation   | Name | Rows | Bytes |TempSpc| Cost (%CPU)| Time  | 
------------------------------------------------------------------------------------ 
| 0 | SELECT STATEMENT |  | 1000K| 9765K|  | 8241 (3)| 00:00:50 | 
| 1 | MERGE JOIN OUTER |  | 1000K| 9765K|  | 8241 (3)| 00:00:50 | 
| 2 | INDEX FULL SCAN | ZZ_B_I | 1000K| 4882K|  | 2258 (2)| 00:00:14 | 
|* 3 | SORT JOIN  |  | 1000K| 4882K| 22M| 5983 (3)| 00:00:36 | 
| 4 | INDEX FULL SCAN| ZZ_C_I | 1000K| 4882K|  | 2258 (2)| 00:00:14 | 
------------------------------------------------------------------------------------ 
+0

hai ragione quando dici che il vincolo 'NOT NULL' è richiesto qui. Ho appena terminato il test e ottengo solo il secondo piano di esecuzione se aggiungo questo vincolo. Non so come mi sia mancato. E tu hai ragione. I suggerimenti effettivamente impongono INDICE FULL SCAN sulla tabella ZZ_CHILD. Ben fatto! E benvenuto nello stackoverflow! Aggiungerò il piano di esecuzione alla tua risposta. –