Unione esterna sinistra su problema di prestazioni su due colonne
Domanda
Sto usando una query SQL simile al seguente modulo:
SELECT col1, col2
FROM table1
LEFT OUTER JOIN table2
ON table1.person_uid = table2.person_uid
AND table1.period = table2.period
Ed è in entrambi i casi troppo lento o qualcosa si sta bloccando perché ci vogliono almeno 4 minuti per tornare. Se dovessi cambiarlo in questo:
SELECT col1, col2
FROM table1
LEFT OUTER JOIN table2
ON table1.person_uid = table2.person_uid
WHERE table1.period = table2.period
quindi funziona benissimo (anche se non restituisce il giusto numero di colonne). C'è un modo per accelerare questo?
AGGIORNAMENTO : fa la stessa cosa se cambio le ultime due righe di quest'ultima query:
SELECT col1, col2
FROM table1
LEFT OUTER JOIN table2
ON table1.period = table2.period
WHERE table1.person_uid = table2.person_uid
AGGIORNAMENTO 2: In realtà sono visualizzazioni a cui mi sto unendo. Sfortunatamente, sono su un database su cui non ho il controllo, quindi non posso (facilmente) apportare modifiche all'indicizzazione. Sono propenso a concordare sul fatto che si tratta di un problema di indicizzazione. Aspetterò un po 'prima di accettare una risposta nel caso in cui ci sia un modo magico per ottimizzare questa query di cui non sono a conoscenza. Altrimenti, accetterò una delle risposte attuali e proverò a trovare un altro modo di fare quello che voglio fare. Grazie per l'aiuto di tutti finora.
Soluzione
Ricorda che le affermazioni 2 e 3 sono diverse dalla prima.
Come? Bene, stai facendo un join esterno sinistro e la tua clausola WHERE non lo tiene in considerazione (come fa la clausola ON). Come minimo, prova:
SELECT col1, col2
FROM table1, table2
WHERE table1.person_uid = table2.person_uid (+)
AND table1.period = table2.period (+)
e vedi se riscontri lo stesso problema di prestazioni.
Quali indici hai su queste tabelle? Questa relazione è definita da un vincolo di chiave esterna?
Quello che probabilmente ti serve è un indice composito sia su person_uid che sul punto (su entrambe le tabelle).
Altri suggerimenti
Penso che tu debba capire perché gli ultimi due non sono la stessa query del primo. Se si esegue un join sinistro e quindi si aggiunge una clausola where che fa riferimento a un campo nella tabella sul lato destro del join (quello che potrebbe non avere sempre un record corrispondente alla prima tabella), è stato effettivamente modificato il join in un join interno. C'è un'eccezione a questo e cioè se fai riferimento a qualcosa di simile
SELECT col1, col2
FROM table1
LEFT OUTER JOIN table2
ON table1.person_uid = table2.person_uid
WHERE table2.person_uid is null
In questo caso si richiede il record che non ha un record nella seconda tabella. Ma a parte questo caso speciale, stai cambiando il join sinistro in un join interno se fai riferimento a un campo in table2 nella clausola where.
Se la tua query non è abbastanza veloce, darei un'occhiata alla tua indicizzazione.
Tutto ciò che qualcuno ti dice sulla base delle informazioni che hai fornito è un'ipotesi.
Guarda il piano di esecuzione per la query. Se non vedi un motivo per la lentezza nel piano, pubblica qui il piano.
http://download.oracle .com / docs / cd / B28359_01 / server.111 / b28274 / ex_plan.htm # PFGRF009
Hai indici di copertura su person_uid
e period
per entrambe le tabelle?
In caso contrario, aggiungerli e riprovare.
Dai un'occhiata al piano di esecuzione e guarda cosa sta effettivamente facendo la query.
Inoltre: quali sono i tipi di dati dei campi? Sono uguali in entrambi i tavoli? Un cast implicito può davvero rallentare le cose.
Queste tabelle hanno indici sulle colonne a cui ti stai unendo? Installa il prodotto SQLDeveloper gratuito di Oracle e utilizzalo per fare un "esplicativo" su quella query e vedere se sta eseguendo scansioni sequenziali di entrambe le tabelle.
In un'unione a sinistra, eseguiresti la scansione della tabella1 per ogni combinazione unica di (person_uid, punto) e quindi cercheresti nella tabella2 tutti i record corrispondenti lì. Se table2 non ha un indice appropriato, ciò può comportare anche la scansione dell'intera tabella.
La mia ipotesi migliore, senza vedere un piano di esecuzione, è che la prima query (l'unica che sembra essere corretta) deve eseguire la scansione della tabella2 e della tabella1.
Come dici che non puoi cambiare gli indici, devi cambiare la query. Per quanto ne so, esiste solo una alternativa realistica ...
SELECT
col1, col2
FROM
table2
FULL OUTER JOIN
table1
ON table1.person_uid = table2.person_uid
AND table1.period = table2.period
WHERE
table1.person_uid IS NOT NULL
La speranza qui è di scansionare table2 per ogni combinazione unica di (person_uid, punto), ma di usare gli indici su table1. (A differenza della scansione di table1 e dell'uso degli indici su table2, cosa che mi aspettavo dalla tua query.)
Se table1 non ha indici appropriati, tuttavia, è molto improbabile che tu possa vedere alcun miglioramento delle prestazioni ...
Dems.
In uno degli aggiornamenti l'OP afferma che sta effettivamente interrogando viste e non tabelle. In questo caso, è possibile aumentare le prestazioni interrogando direttamente le tabelle di cui ha bisogno, specialmente se le viste sono complesse e si uniscono a molte altre tabelle che non contengono informazioni di cui ha bisogno o che sono viste che chiamano viste.
La sintassi del join ANSI fornisce una chiara distinzione tra condizioni JOIN e predicati FILTER; questo è molto importante quando si scrivono join esterni. Usando le tabelle emp / dept, guarda i risultati dei seguenti due join esterni
Q1
SELECT dname, d.deptno, e.ename, e.mgr, d.loc
FROM dept d
LEFT OUTER JOIN emp e
on d.deptno = e.deptno
and loc in ('NEW YORK','BOSTON' )
;
DNAME DEPTNO ENAME MGR LOC
-------------- ---------- ---------- ---------- -------------
ACCOUNTING 10 CLARK 7839 NEW YORK
ACCOUNTING 10 KING NEW YORK
ACCOUNTING 10 MILLER 7782 NEW YORK
RESEARCH 20 DALLAS
SALES 30 CHICAGO
OPERATIONS 40 BOSTON
====
Q2
SELECT dname, d.deptno, e.ename, e.mgr, d.loc
FROM dept d
LEFT OUTER JOIN emp e
on d.deptno = e.deptno
where loc in ('NEW YORK','BOSTON' )
;
DNAME DEPTNO ENAME MGR LOC
-------------- ---------- ---------- ---------- -------------
ACCOUNTING 10 CLARK 7839 NEW YORK
ACCOUNTING 10 KING NEW YORK
ACCOUNTING 10 MILLER 7782 NEW YORK
OPERATIONS 40 BOSTON
Il primo esempio, mostra Q1 è un esempio di "unione su una costante". In sostanza, la condizione del filtro viene applicata prima di eseguire il join esterno. Quindi si eliminano le righe, che vengono successivamente aggiunte come parte del join esterno. Non è necessariamente sbagliato, ma è quella la domanda che hai veramente chiesto? Spesso sono richiesti i risultati mostrati in Q2, in cui il filtro viene applicato dopo il join (esterno).
Esiste anche un'implicazione sulle prestazioni, per set di dati di grandi dimensioni. In molti casi, l'unione su una costante deve essere risolta internamente dall'ottimizzatore creando una vista laterale, che di solito può essere ottimizzata solo tramite un join loop nidificato anziché un hash join
Per gli sviluppatori che hanno familiarità con la sintassi del join esterno Oracle, la query sarebbe stata probabilmente scritta come
SELECT dname, d.deptno, e.ename, e.mgr, d.loc
FROM dept d
,emp e
where d.deptno = e.deptno(+)
and loc in ('NEW YORK','BOSTON' )
Questa query è semanticamente equivalente a Q2 sopra.
Quindi, in sintesi, è estremamente importante comprendere le differenze tra la clausola JOIN e la clausola WHERE quando si scrivono i join esterni ANSI.