2012-03-15 10 views
7

Ho notato uno strano comportamento di FULL OUTER JOIN in Oracle 11. Stavo unendo le tabelle dello schema delle risorse umane, in particolare i DIPENDENTI e i DEPARTMENT.Strano comportamento del join esterno completo in Oracle: come potrebbe essere spiegato?

Ad esempio, la seguente query restituisce 123 righe:

SELECT * FROM employees e 
    FULL JOIN departments d ON e.department_id = d.department_id 

Tuttavia, ciò che è difficile da capire - quando ho messo una serie di particolari colonne nella clausola di selezione, la query restituirà 122 righe (un fila manca è per un lavoratore dipendente che non ha assegnato reparto - quello che è inoltre restituito con sinistra unirsi in confronto ad unirsi interno):

SELECT first_name, last_name, department_name FROM employees e 
    FULL JOIN departments d on e.department_id = d.department_id 

Anche quando io conto le righe restituisce 122 (COUNT(*)) !!! COSA STA SUCCEDENDO? Qual è la differenza tra SELECT * e SELECT COUNT(*)?

Il piano per spiegare SELECT * ...:

SELECT STATEMENT          122 
    VIEW     VW_FOJ_0      122 
    HASH JOIN       FULL OUTER  122 
     Access Predicates 
     E.DEPARTMENT_ID = D.DEPARTMENT_ID 
     TABLE ACCESS  DEPARTMENTS  FULL   27 
     TABLE ACCESS  EMPLOYEES  FULL   107 

e per SELECT COUNT(*) ...:

SELECT STATEMENT            1 
    SORT          AGGREGATE   1 
    VIEW    VW_FOJ_0       122 
     HASH JOIN       FULL OUTER  122 
     Access Predicates 
      E.DEPARTMENT_ID = D.DEPARTMENT_ID 
     INDEX   DEPT_ID_PK   FAST FULL SCAN 27 
     INDEX   EMP_DEPARTMENT_IX FAST FULL SCAN 107 
+2

Cosa succede se si utilizza il 'union' per quelle colonne ? ottieni qualcosa di diverso quando usi 'union all'?cosa succede se contate con 'group by first_name, last_name, department_name'? –

+0

'SELECT * FROM dipendenti e FULL JOIN reparti d su e.department_id = d.department_id' restituisce 123 righe e' SELECT count (*) FROM impiegati e FULL JOIN reparti d su e.department_id = d.department_id' restituisce 122 righe? –

+0

Sì, esattamente - ecco perché ho postato questa domanda. –

risposta

5

L'ottimizzatore non dovrebbe essere la scelta di utilizzare l'indice su EMP.DEPT_ID nella seconda query, poiché può avere valori NULL. Questo è ciò che sta causando di escludere una riga dai risultati.

L'unica spiegazione non di bug che riesco a pensare al momento è che in qualche modo sono stati creati vincoli nella modalità DISABLE RELY in modo che l'ottimizzatore pensi che il campo non possa contenere valori NULL. In questo caso sarebbe corretto utilizzare l'indice dato le informazioni errate nei vincoli. Tuttavia, sembra che l'opzione RELY non sia disponibile per i vincoli NOT NULL, quindi non vedo come questo potrebbe essere il problema. Tuttavia, dare un'occhiata a tutti i vincoli sui tavoli.

A parte questo, ci sono un numero sorprendente di bug sul sito di Oracle in merito a risultati errati da join esterni completi. Potresti colpire uno di loro. In non pochi di questi casi, la soluzione è quella di disabilitare "nativo" full outer join, che si può fare per la sessione corrente con questa affermazione:

alter session set "_optimizer_native_full_outer_join"=off; 
+0

+1: sembra la spiegazione più plausibile. Sarebbe interessante conoscere la versione del DB. –

1

(Impossibile scrivere questo in un commento)

I risultati sono conformi alla piani di esecuzione.

Il piano di esecuzione count (*) utilizza l'indice EMP_DEPARTMENT_IX che contiene tutti i dept_id dalla tabella dei dipendenti. Ma gli indici non contengono valori nulli. Quindi, questo piano di esecuzione "perderà" gli emp con nullo department_id.

Tuttavia, dovrebbe essere spiegato il motivo per cui Oracle sceglie questo piano di esecuzione in caso di

select first_name, last_name, department_name 

e

select count(*) 

in opposizione a

select * 
+1

Sì, sono d'accordo. Comunque questa è una specie di incoerenza secondo me. Il join esterno completo non funziona come dovrebbe in tutti i casi tranne che per selezionare *. –

+0

@luckyjaca Potete fare più test con 'select emp_id',' select first_name', 'select e.department_id',' select d.department_id', 'select d.department_name', etc? –

+0

heh ... Ho fatto i test come suggerito. Tuttavia, tutti hanno restituito 122 righe: l'unico modo per ottenere 123 righe è selezionare tutte le colonne per asterisco. Comunque, finalmente sono riuscito a restituire 123 righe usando hint: '/ * + no_index (e) * /'. È sufficiente far funzionare tutte le query: 'COUNT (*)' o 'first_name, last_name, department_name'. –

Problemi correlati