6. blok část B Vnořené dotazy Studijní cíl Tento blok je věnován práci s vnořenými dotazy. Popisuje rozdíl mezi korelovanými a nekorelovanými vnořenými dotazy a zobrazuje jejich použití. Doba nutná k nastudování 2-3 hodiny Průvodce studiem Při studiu tohoto bloku se předpokládá, že čtenář je obeznámen se základní syntaxí příkazu, spojováním tabulek, použitím vyhledávacích podmínek a základních funkcí. 1. Vnořené dotazy Pod pojmem vnořený dotaz rozumíme příkaz, který je vnořen do jiného SQL dotazu. Při provádění SQL dotazu se nejdříve provede vnořený dotaz. Výsledek vnořeného dotazu je po dokončení celého dotazu zahozen. Vnořený dotaz můžeme chápat jako virtuální tabulku definovanou příkazem. Vnořený dotaz je zapisován do jednoduchých závorek a je možné jej využít v těchto částech dotazu: za klauzulí FROM za klauzulí WHERE za klauzulí HAVING bezprostředně za příkazem Nejčastěji jsou vnořené dotazy používány za klauzulí WHERE u příkazů, UPDATE nebo DELETE. Časté použití je také za klauzulí SET u příkazu UPDATE. Vnořené dotazy, které obsahují alias, jsou označovány jako in-line pohledy. 2. Korelované vnořené dotazy Korelované neboli souvztažné vnořené dotazy jsou takové, které obsahují reference na nadřazený dotaz. Korelovaný vnořený dotaz je spouštěn pro každý řádek nadřazeného dotazu. Pro lepší pochopení si ukážeme použití na příkladu. 1
Zjistěte počty nabízených produktů od jednotlivých dodavatelů. nazev, ( COUNT(*) WHERE produkty.dodavatel_id=dodavatele.dodavatel_id ) FROM dodavatele; Podívejme se podrobněji na vnořený dotaz. Za klauzulí WHERE používáme odkaz na tabulku DODAVATELE, která však figuruje až v nadřazeném dotazu. Pro každý řádek z tabulky DODAVATELE je tedy vnořený dotaz spuštěn znovu. Celý tento příklad by bylo možné nahradit použitím konstrukce GROUP BY. Další možností využití korelovaných vnořených dotazů je test existence relací. K tomuto účelu slouží operátor EXISTS. Ukažme si na příkladu. Z tabulky produkty vypište ty produkty, kde dodavatel dodá ještě i jiné produkty. prod1. prod1. prod1. prod1 WHERE EXISTS( prod2.produkt_id prod2 WHERE prod1.dodavatel_id = prod2.dodavatel_id AND prod1.produkt_id <> prod2.produkt_id ); Výsledkem dotazu jsou ty řádky tabulky PRODUKTY, kde existuje alespoň jeden řádek z vnořeného dotazu. Jelikož je vnořený i nadřazený dotaz prováděn nad stejnou tabulkou, je nutné použít alias. Bez použití aliasů databázový systém 2
nerozpozná, které hodnoty má porovnávat. Vnořené dotazy opatřené aliasem se nazývají in-line pohledy a budeme se jimi zabývat později. Použití korelovaných vnořených dotazů je možné v některých případech nahrazovat souhrnnými dotazy. V některých případech je jednodušší ale použít vnořené dotazy, protože se můžeme vyhnout spojování tabulek. 3. Nekorelované vnořené dotazy Nekorelované vnořené dotazy neobsahují žádnou referenci na nadřazený dotaz. Na rozdíl od korelovaných vnořených dotazů jsou provedeny pouze jednou. Nekorelované vnořené dotazy můžeme rozdělit do kategorií, podle typu návratové množiny: Jednořádkové, jednosloupcové Víceřádkové, jednosloupcové Víceřádkové, vícesloupcové 3.1. Jednořádkové, jednosloupcové vnořené dotazy Jsou vnořené dotazy, které vrací jeden řádek a jeden sloupec. V literatuře jsou označovány jako skalární vnořené dotazy, protože tyto dotazy je možné porovnávat běžnými operátory (=, <, >,!=, <=, >=). Skalární vnořené dotazy jako jediné umožňují použití bezprostředně za příkazem. Z tabulky produkty zobrazte všechny produkty, které mají zadanou cenu. Ve výstupu zobrazte oznaceni a rozdíl mezi maximální cenou všech produktů a cenou aktuálního produktu. ( MAX() )- rozdil_od_nejdrazsiho WHERE IS NOT NULL; 3
Dotaz je vyhodnocen tak, že nejdříve je proveden vnořený dotaz a od této hodnoty je v každém řádku odečtena produktu. Z tabulky produkty zobrazte všechny produkty, které mají cenu vyšší než je průměrná všech produktů. WHERE > ( AVG() WHERE IS NOT NULL ); 3.2. Víceřádkové, jednosloupcové vnořené dotazy Jakmile vnořený dotaz vrátí více než jeden řádek, není možné použít běžné porovnávací operátory. Není totiž možné porovnat jednu hodnotu přímo s množinou hodnot. Můžeme ale porovnat danou hodnotu s každou hodnotou v množině. K tomu slouží operátory ANY a ALL. Ukažme si na příkladu. 4
Z tabulky produkty zobrazte všechny produkty, které mají cenu vyšší než alespoň jeden další produkt. WHERE > ANY( WHERE IS NOT NULL ); Operátor ANY pracuje tak, že podmínka je vyhodno jako pravdivá, pokud je hodnota větší (pro náš příklad) než alespoň jedna hodnota z množiny. Z tabulky produkty zobrazte všechny produkty, které mají cenu vyšší nebo stejnou než všechny ostatní produkty. WHERE >= ALL( WHERE IS NOT NULL ); Při použití operátoru ALL je podmínka pravdivá jedině tehdy, pokud je hodnota větší nebo rovna než všechny hodnoty z množiny. 5
3.3. Víceřádkové, vícesloupcové vnořené dotazy Zatímco u předchozích příkladů jsme porovnávali vždy jeden sloupec s výsledkem vnořeného dotazu, vícesloupcové vnořené dotazy umožňují porovnávání více sloupců najednou. Pro porovnávání více sloupců zároveň musíme využít konstruktoru řádkové hodnoty. Syntaxe konstruktoru řádkové hodnoty vypadá následovně: (<název sloupce>, <název sloupce>, <název sloupce>) Takto konstruovaný výraz můžeme porovnat pomocí operátoru =, pokud jsme si jisti, že se vrátí pouze jeden řádek. Nebo pomocí operátoru IN. Práci s vícesloupcovými vnořenými dotazy si ukážeme na příkazu UPDATE. Změňte všechny řádky v tabulce PRODUKTY, které nemají zadanou cenu ani dodavatele. Takovým sloupcům uveďte do pole oznaceni řetězec Průměr a do pole průměrnou cenu všech produktů. UPDATE produkty SET ( ) = ( 'Průměr', AVG() ) WHERE dodavatel_id IS NULL AND IS NULL; * ; U tohoto příkladu jsme předpokládali, že v tabulce PRODUKTY je pouze jeden produkt, který obsahuje ve sloupcích dodavatel_id a hodnotu NULL. Kdyby tomu tak nebylo, skončí příkaz UPDATE chybovým hlášením. 6
4. In-line pohledy In-line pohled je vnořený dotaz, který má alias. Využití in-line pohledů se nabízí pro zjednodušení komplexních dotazů odstraněním spojení tabulek (operací JOIN). Umožňuje také spojení několika samostatných dotazů do jednoho. V příkazu se s in-line pohledem pracuje stejně, jako s běžnou tabulkou. Syntaxe in-line pohledu je: * FROM ( * FROM tabulka) nazev_pohledu; Použití in-line pohledů si ukážeme na jednoduchém příkladu. vproduktydodavatele.nazev, ROUND(AVG(vProduktyDodavatele.),2) as prumerna_ FROM ( dodavatele.nazev, produkty. produkty. dodavatele.dodavatel_id, produkty. FROM dodavatele JOIN produkty ON produkty.dodavatel_id = dodavatele.dodavatel_id ) vproduktydodavatele GROUP BY vproduktydodavatele.nazev; Pro správné pochopení si nyní dotaz rozebereme. Nejdříve byl vyhodnocen in-line pohled vproduktydodavatele. Výstupem z toho pod-dotazu byla množina výsledků, na kterou se provedl druhý dotaz. Stejný příklad by bylo možné řešit i pomocí klasických pohledů: CREATE VIEW vproduktydodavatele AS dodavatele.nazev, produkty. produkty. dodavatele.dodavatel_id, produkty. FROM dodavatele JOIN produkty ON produkty.dodavatel_id=dodavatele.dodavatel_id WITH READ ONLY; 7
vproduktydodavatele.nazev, ROUND(AVG(vProduktyDodavatele.),2) as prumerna_ FROM vproduktydodavatele GROUP BY vproduktydodavatele.nazev; 5. Případ použití vnořených dotazů návrat prvních N řádků V mnoha případech je třeba získat pouze určitý počet řádků z výsledku. Například pokud chceme 3 nejdražší produkty. Taková konstrukce není přímo v SQL implementována a různé databázové systémy mají vlastní mechanismy jak výsledku docílit. V prostředí databázového systému Oracle je problém řešen kombinací pseudosloupce ROWNUM a vnořených dotazů. Ukažme si tedy použití na příkladu. Zobrazte první 3 nejdražší produkty. FROM ( WHERE IS NOT NULL ORDER BY DESC ) WHERE rownum <= 3; 8
Pojmy k zapamatování Příkazy a funkce: kostrukce FROM ( ), UPDATE SET ()=(), WHERE () IN () Problém: užití korelovaných a nekorelovaných vnořených dotazů Shrnutí V této lekci jste se seznámili s použitím vnořených dotazů. Vnořené dotazy dělíme na korelované a nekorelované. Korelované vnořené dotazy obsahují reference na vnější dotaz. Vnořené dotazy mohou být jednosloupcové, jednořádkové (skalární), jednosloupcové, víceřádkové a vícesloupcové, víceřádkové. Otázky na procvičení 1. Co je nekorelovaný vnořený dotaz? 2. Čím můžeme nahradit souhrnné funkce? 3. Co znamená pojem in-line pohled? 4. Jaké operátory můžeme použít u jednořádkových, jednosloupcových vnořených dotazů? 5. Co je konstruktor řádkové hodnoty a k čemu slouží? Odkazy a další studijní prameny http://www.techonthenet.com/oracle (syntaxe příkazů SQL jazyka a funkcí) http://www.sqlcourse.com (interaktivni SQL trénink) http://www.oracle.com/technetwork/database/enterpriseedition/documentation (dokumentace k databázové platformě Oracle) http://www.penguin.cz/noviny/?id=chip/index (seriál Databáze standardu SQL z časopisu CHIP) Odkazy a další studijní prameny ŠIMŮNEK, M. SQL, kompletní kapesní průvodce. Grada Publishing, 1999. ISBN 80-7169-692-7. STEPHENS, K.R., PLEW, R.R. Naučte se SQL za 21 dní. Praha: Computer Press, 2004. ISBN 80-7226-870-8. GROFF, J.R., WEINBERG, P.N. SQL - kompletní průvodce. Praha: Computer Press, 2005. ISBN 80-251-0369-2. 9