Je to docela jednoduché, když to pochopíte:
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);
Tento řádek v podstatě porovnává S.S_Id
se všemi e.S_Id
hodnoty, které pocházejí z poddotazu.
Nyní to změňte na NOT EXISTS
a zadejte kontrolu rovnosti S.S_Id = e.S_Id
, uvnitř poddotazu:
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);
Menší možná změna je uvědomit si, že (SELECT e.S_Id ...
ve skutečnosti nepotřebuje e.S_Id
. Dílčí dotazy s EXISTS
a NOT EXISTS
stačí zkontrolovat, zda jsou vráceny řádky nebo ne, a na hodnotách sloupců nezáleží. Můžete zadat SELECT *
nebo tam konstanta (SELECT 1
je běžné) nebo SELECT NULL
nebo dokonce SELECT 1/0
(Ano, to bude fungovat!):
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);
Dalším důležitým aspektem je, že když provedete převod tímto způsobem, (zdánlivě ekvivalentní) NOT EXISTS
a NOT IN
zápisy dotazu jsou skutečně ekvivalentní, pouze pokud jsou oba S_Id
sloupce nemají hodnotu null. Pokud e.S_Id
sloupec má hodnotu null, NOT IN
může způsobit, že celý dotaz nevrátí vůbec žádné řádky (protože x NOT IN (a, b, c, ...)
je ekvivalentní x<>a AND x<>b AND ...
a tato podmínka nemůže být pravdivá, když jeden z a,b,c...
je NULL
.)
Z podobných důvodů budete mít jiné výsledky, pokud s.S_Id
má nulovou hodnotu (v tomto případě to není příliš pravděpodobné, protože se pravděpodobně jedná o primární klíč, ale v jiných případech na tom záleží.)
Téměř vždy je tedy lepší použít NOT EXISTS
, protože se chová odlišně, i když je v každém sloupci povolena hodnota Null (S.S_Id = e.S_Id
check zahodí řádky s null dříve) a toto chování je obvykle žádoucí. V otázce je mnoho podrobností: NENÍ IN vs. NEEXISTUJE
, v odpovědi @Martin Smith. Najdete zde také způsoby, jak převést NOT IN
na NOT EXISTS
a zachovat nulové (nepříjemné) chování.