EXISTS a NOT EXISTS
EXISTS (existuje) se ptá jednodušeji než IN: najde poddotaz aspoň jeden řádek? Pokud ano, EXISTS platí; pokud poddotaz nenajde nic, neplatí. Kteří zákazníci mají nějakou vrácenou objednávku?
SELECT z.jmeno
FROM zakaznici AS z
WHERE EXISTS (
SELECT *
FROM objednavky AS o
WHERE o.zakaznik_id = z.id AND o.stav = 'vraceno'
)
ORDER BY z.jmeno;| jmeno |
|---|
| Matěj Němec |
| Tomáš Veselý |
Všimněte si podmínky v poddotazu: o.zakaznik_id = z.id. Poddotaz se odkazuje na z, tabulku vnějšího dotazu. Takovému poddotazu se říká korelovaný. Sám o sobě se spustit nedá (skončil by chybou no such column: z.id), protože závisí na řádku, o kterém vnější dotaz právě rozhoduje. Představte si, že ho databáze spustí znovu pro každého zákazníka. U Evy Novotné je z.id rovno 1 a poddotaz hledá vrácené objednávky zákazníka 1. Žádnou nenajde, takže Eva ve výsledku není. U Tomáše Veselého najde objednávku 7, takže on ve výsledku je.
Poddotazy z předchozích cvičení se provedly jen jednou a jejich výsledek byl pro všechny řádky stejný. Korelovaný poddotaz může pro každý řádek dát jinou odpověď. (Databáze ho ve skutečnosti může provádět chytřeji, ale výsledek je stejný, jako kdyby ho spouštěla pro každý řádek.)
Na tom, co poddotaz ukazuje, nezáleží: EXISTS jen zjišťuje, jestli nějaký řádek existuje. Proto tam programátoři píšou SELECT * nebo SELECT 1.
NOT EXISTS platí, když poddotaz nenajde nic. Tohle jsou zákazníci, kteří obchod nikomu nedoporučili, tedy otázka, která s NOT IN spadla do pasti:
SELECT z.jmeno
FROM zakaznici AS z
WHERE NOT EXISTS (
SELECT *
FROM zakaznici AS d
WHERE d.doporucil = z.id
)
ORDER BY z.jmeno;| jmeno |
|---|
| Anna Kučerová |
| Filip Beneš |
| Kristýna Fialová |
| … |
NOT EXISTS žádnou past s NULL nemá: zákazník, který má ve sloupci doporucil hodnotu NULL, prostě nikdy nesplní podmínku d.doporucil = z.id. Vnější dotaz i poddotaz tu používají stejnou tabulku, takže aliasy z a d jsou nutné: říkají, který zákazník je který.
Typická chyba: zapomenout na podmínku, která poddotaz spojuje s řádkem vnějšího dotazu. WHERE EXISTS (SELECT * FROM objednavky WHERE stav = 'vraceno') se ptá, jestli existuje vůbec nějaká vrácená objednávka. Ta existuje, takže podmínka platí pro každého zákazníka a dotaz ukáže všech 14.
Jak psát:
Pokyny
Dotaz v editoru najde hry, které si nikdo nekoupil, pomocí LEFT JOIN … IS NULL, jak jste se naučili v 6. lekci. Přepište ho pomocí NOT EXISTS: nechte hry, pro které poddotaz nenajde v tabulce polozky žádný řádek s ID hry. Výsledek zůstane stejný:
| nazev |
|---|
| Lost Temple |
| Mystery Manor |
| Robot Rebellion |
Pod něj ukažte název a cenu her, které si někdo koupil levněji, než je cena hry, tedy ve slevě. Použijte EXISTS: poddotaz hledá řádek tabulky polozky s touto hrou, jehož cena je nižší než cena hry. Sloupec cena mají obě tabulky, takže na aliasech záleží. Seřaďte podle názvu:
| nazev | cena |
|---|---|
| Shadow Ninja | 649 |
| Zombie Town | 799 |
Nakonec pomocí NOT EXISTS ukažte jméno a datum registrace (registrace) zákazníků, kteří v roce 2024 nezadali žádnou objednávku. Seřaďte je podle data registrace:
| jmeno | registrace |
|---|---|
| Natálie Marková | 2024-08-14 |
| Adéla Králová | 2025-02-12 |
| Filip Beneš | 2025-05-06 |
| Kristýna Fialová | 2025-08-21 |
| Martin Zeman | 2025-10-03 |
Začněte programovat zdarma
4/8