C'est assez simple, quand on a compris :
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);
Cette ligne compare essentiellement S.S_Id
avec tous les e.S_Id
valeurs issues de la sous-requête.
Changez maintenant cela en NOT EXISTS
et mettre un contrôle d'égalité S.S_Id = e.S_Id
, à l'intérieur de la sous-requête :
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);
Le changement mineur possible consiste à réaliser que (SELECT e.S_Id ...
n'a pas vraiment besoin du e.S_Id
. Sous-requêtes avec EXISTS
et NOT EXISTS
vérifiez simplement s'il y a des lignes retournées ou non et les valeurs des colonnes n'ont pas d'importance. Vous pouvez mettre SELECT *
ou une constante ici (SELECT 1
est courant) ou SELECT NULL
ou encore SELECT 1/0
(Oui, ça marchera !) :
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);
Une autre considération majeure est que lorsque vous effectuez la conversion de cette façon, le (apparemment équivalent) NOT EXISTS
et NOT IN
les écritures d'une requête ne sont réellement équivalentes que si les deux S_Id
les colonnes ne sont pas nullables. Si le e.S_Id
la colonne est nullable, le NOT IN
peut avoir pour résultat que la requête entière ne renvoie aucune ligne (parce que x NOT IN (a, b, c, ...)
est équivalent à x<>a AND x<>b AND ...
et cette condition ne peut pas être vraie lorsque l'un des a,b,c...
est NULL
.)
Pour des raisons similaires, vous obtiendrez des résultats différents si le s.S_Id
est nullable (ce n'est pas très probable dans ce cas car il s'agit probablement de la clé primaire, mais dans d'autres cas, cela compte.)
Il est donc presque toujours préférable d'utiliser NOT EXISTS
, car il se comporte différemment même si l'une ou l'autre des colonnes est nullable (le S.S_Id = e.S_Id
check supprimera les lignes avec null plus tôt) et généralement ce comportement est celui recherché. Il y a beaucoup de détails dans la question : PAS DANS vs PAS EXISTE
, dans la réponse de @Martin Smith. Vous y trouverez également des moyens de convertir le NOT IN
à NOT EXISTS
et conservez le comportement lié à null (désagréable).