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.

È stato utile?

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.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top