Oracle
 sql >> Database >  >> RDS >> Oracle

Conversione da NOT IN a NOT EXISTS

È piuttosto semplice, quando ci prendi la mano:

SELECT s.S_Fname, s.S_Lname
FROM STUDENT s
WHERE s.S_Sex = 'F'
AND S.S_Id NOT IN(SELECT e.S_Id           -- take this line
        FROM ENROLLMENT e
        WHERE e.Mark < 70);

Quella riga fondamentalmente confronta S.S_Id con tutti gli e.S_Id valori che provengono dalla sottoquery.

Ora cambialo in NOT EXISTS e metti un controllo di uguaglianza S.S_Id = e.S_Id , all'interno della sottoquery:

SELECT s.S_Fname, s.S_Lname
FROM STUDENT s
WHERE s.S_Sex = 'F'
AND NOT EXISTS (SELECT e.S_Id          
        FROM ENROLLMENT e
        WHERE (e.Mark < 70)       -- if this is complex, you'll need parentheses
        AND S.S_Id = e.S_Id);

Una piccola modifica possibile è rendersi conto che (SELECT e.S_Id ... non ha davvero bisogno del e.S_Id . Sottoquery con EXISTS e NOT EXISTS controlla solo se ci sono righe restituite o meno e i valori delle colonne non contano. Puoi inserire SELECT * o una costante lì (SELECT 1 è comune) o SELECT NULL o anche SELECT 1/0 (Sì, funzionerà!):

SELECT s.S_Fname, s.S_Lname
FROM STUDENT s
WHERE s.S_Sex = 'F'
AND NOT EXISTS (SELECT 1
        FROM ENROLLMENT e
        WHERE e.Mark < 70  
        AND S.S_Id = e.S_Id);

Un'altra considerazione importante è che quando si esegue la conversione in questo modo, il (apparentemente equivalente) NOT EXISTS e NOT IN gli scritti di una query sono realmente equivalenti solo se entrambi S_Id le colonne non sono annullabili. Se il e.S_Id la colonna è nullable, il NOT IN potrebbe far sì che l'intera query non restituisca alcuna riga (perché x NOT IN (a, b, c, ...) è equivalente a x<>a AND x<>b AND ... e quella condizione non può essere vera quando uno dei a,b,c... è NULL .)

Per ragioni simili, avrai risultati diversi se s.S_Id è nullable (questo non è molto probabile in questo caso poiché è probabilmente la chiave primaria ma in altri casi è importante.)

Quindi è quasi sempre meglio usare NOT EXISTS , poiché si comporta in modo diverso anche se una delle colonne è nullable (il S.S_Id = e.S_Id check scarterà le righe con null in precedenza) e di solito questo comportamento è quello desiderato. Ci sono molti dettagli nella domanda: NON IN vs NON ESISTE , nella risposta di @Martin Smith. Troverai anche dei modi per convertire il NOT IN a NOT EXISTS e mantieni il comportamento correlato (sgradevole) nullo.