5. blok
Souhrnné a skupinové dotazy
Studijní cíl Tento blok je věnován základům při vytváření souhrnných a skupinových dotazů s využitím agregačních funkcí SUM(), AVG(), MIN(), MAX() a COUNT() a klauzulí GROUP BY a HAVING u příkazů v jazyce SQL. Souhrnné a skupinové dotazy jsou základem při zpracování hromadných dat a tvorbě tiskových sestav. 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 SELECT, spojováním tabulek a použitím vyhledávacích podmínek a základních funkcí. 1. Souhrnné dotazy V mnoha případech nepotřebujeme znát detailní informace pocházející z SQL dotazů, ale potřebujeme tyto hodnoty sumarizovat. Mezi takové požadavky patří například odpovědi na následující otázky:
Kolik zaměstnanců má společnost? Jaké jsou tržby pobočky? Kolik lidí jede průměrně v jednom vlaku? Jaká je průměrná délka života? Jaká je průměrná mzda? Kolik vstupenek se prodalo na všechna utkání? Jaká je nejvyšší a nejnižší vyplácená sociální dálka?
Souhrnná data je možné získat prostřednictvím agregačních funkcí a klauzulí GROUP BY (případně doplněné o klauzuli HAVING) v příkazu SELECT. 1.1. Agregační funkce Sumarizovat data z databáze je možné prostřednictvím množiny agregačních (v některé literatuře uváděno jako sloupcových) funkcí jazyka SQL. Tyto funkce přijímají jako argument obvykle celý sloupec a vytvoří jedinou hodnotu, která sumarizuje sloupec. Podívejme se na následující dotaz pro výpočet průměrné hodnoty ve sloupci: David Žák, Jiří Zechmeister, Tomáš Váňa IDAS1/5 – Souhrnné a skupinové dotazy
1
Jaká je průměrná cena produktu? SELECT AVG(CENA) FROM PRODUKTY;
Pro správné pochopení tvorby výsledku se podívejme na obrázek 1 na postup, jakým se výsledek dotazu generuje. Funkce přijímá hodnoty ve sloupci CENA a vypočítá jejich průměr. Dotaz vytvoří jediný řádek výsledku sumarizující data z tabulky PRODUKTY.
AVG
Obrázek 1: Postup při vyhodnocení souhrnného dotazu.
V jazyce SQL jsou definovány následující agregační funkce:
SUM() AVG() MIN() MAX() COUNT() COUNT(*)
výpočet součtu hodnot ve sloupci výpočet průměru hodnot ve sloupci najde minimální hodnotu ve sloupci najde maximální hodnotu ve sloupci výpočet počtu hodnot ve sloupci (NOT NULL) výpočet počtu řádků ve výsledku dotazu
Výsledky funkcí SUM(), MIN()a MAX()mají shodné datové typy jako data v kalkulovaném výrazu, ale výsledek SUM() může mít větší přesnost. U funkce AVG()může být výstupní datový typ rozdílný od datového typu kalkulovaných hodnot ve sloupci či výrazu, což je způsobeno tím, že součet těchto hodnot je dělen jejich počtem.
David Žák, Jiří Zechmeister, Tomáš Váňa IDAS1/5 – Souhrnné a skupinové dotazy
2
Data ve výrazech zpracovávaných funkcemi SUM() a AVG() musí být numerického typu, funkce pro hledání extrémních hodnot MIN() a MAX() mohou pracovat nad sloupci a výrazy v numerickém formátu, řetězci či ve formátu datum/čas. Při použití funkcí MIN() a MAX() nad řetězcovými daty je třeba si uvědomit, že porovnání dvou řetězců je závislé na použité znakové sadě. Ačkoli standard SQL2 obsahuje podporu pro národní znakové sady a alternativní posloupnosti řazení, implementace těchto vlastností se však mezi jednotlivými databázovými systémy liší. 1.2. Zpracování hodnot NULL Agregační funkce SUM(), AVG(), MIN(), MAX(), COUNT() přijímají jako svůj argument sloupec datových hodnot a jako výsledek vytváří jedinou datovou hodnotu. Standard ANSI/ISO SQL definuje, že agregační funkce ignorují hodnoty NULL ve sloupci. Jaký je počet produktů v tabulce PRODUKTY a u kolika z nich je uvedena cena? SELECT COUNT(*), COUNT(CENA) FROM PRODUKTY;
Jak můžeme vidět z obrázku 1, tabulky PRODUKTY obsahuje 5 řádků a u 4 z nich je uvedena cena, tomu odpovídá i výsledek předchozího dotazu. O tom, že hodnoty NULL jsou ignorovány, se můžeme přesvědčit i v následujícím příkladu. SELECT COUNT(*), COUNT(PRODUKT_ID), SUM(CENA), AVG(CENA) FROM PRODUKTY;
COUNT(CENA),
Všechny agregační funkce pracující se sloupcem CENA ignorují hodnotu NULL. Ignorace hodnoty NULL nám však při nesprávném pochopení může způsobit nemalé problémy. Podívejme se na následující příklad: SELECT SUM(SKUTECNE_TRZBY)-SUM(PLAN_TRZBY), SUM(SKUTECNE_TRZBY-PLAN_TRZBY) FROM OBCHODNI_VYSLEDKY;
David Žák, Jiří Zechmeister, Tomáš Váňa IDAS1/5 – Souhrnné a skupinové dotazy
3
Pozor na skutečnost, že výsledkem tohoto dotazu mohou být za jistých okolností 2 různé hodnoty! Rozdíl nastane v okamžiku, kdy v některém řádku bude scházet například vyplněná hodnota SKUTECNE_TRZBY (bude tedy NULL), ale plánovaná tržba bude uvedena. Zatímco plánovaná tržba ovlivní výsledek SUM(PLAN_TRZBY), tak hodnota SUM(SKUTECNE_TRZBY-PLAN_TRZBY) ovlivněna nebude, neboť výsledkem tohoto rozdílu v daném řádku bude hodnota NULL, která bude ignorována funkcí SUM(). Analogická situace nastane, pokud prázdná hodnota (NULL) bude ve sloupci PLAN_TRZBY. Volba správného dotazu záleží na tom, jaký výsledek požadujeme a zda dokážeme potřebu správně naformulovat do SQL dotazu. V uvedeném případě totiž nelze určit, které řešení je správné a které nikoli, ale každé poskytuje odpověď na jinou otázku. V prvním případě se provede přesně to, co je zapsáno, ve výsledku bude rozdíl SUM(SKUTECNE_TRZBY)a SUM(PLAN_TRZBY), tedy součet hodnot ve sloupci PLAN_TRZBY bude odečten od součtu hodnot ve sloupci SKUTECNE_TRZBY, ve druhém případě bude ve výsledku součet rozdílů ve sloupcích SKUTECNE_TRZBY a PLAN_TRZBY, které jsou vyhodnoceny v jednotlivých řádcích. Při výskytu NULL prázdných hodnot v uvedených sloupcích však oba případy vedou na různé výsledky. Základní pravidla pro zpracování prázdných hodnot (NULL) v agregačních funkcích dle standardu ANSI/ISO:
Jakákoliv hodnota NULL je ignorována pro výpočet hodnoty agregační funkce. Pokud mají všechny zpracovávané položky hodnotu NULL, vrací agregační funkce SUM(), AVG(), MIN(), MAX() hodnotu NULL. Funkce COUNT()vrací hodnotu nula. Nejsou-li ve sloupci žádné datové položky, vrací agregační funkce SUM(), AVG(), MIN(), MAX() hodnotu NULL. Funkce COUNT() vrací hodnotu nula. Funkce COUNT(*) vrací počet řádků.
1.3. Eliminace duplicitních řádků (DISTINCT) U agregačních funkcí MIN(), MAX() a COUNT()je možné specifikovat klíčové slovo DISTINCT, které slouží k eliminaci duplicitních hodnot ze sloupce dřív, než se na něj aplikuje agregační funkce.
David Žák, Jiří Zechmeister, Tomáš Váňa IDAS1/5 – Souhrnné a skupinové dotazy
4
Pro názornost se podívejme na tyto 2 dotazy: U kolika produktu je uvedeno DODAVATEL_ID? SELECT COUNT(DODAVATEL_ID) FROM PRODUKTY;
Kolik různých dodavatelů dodává produkty? SELECT COUNT(DISTINCT DODAVATEL_ID) FROM PRODUKTY;
Standard SQL povoluje použití klíčového slova DISTINCT u agregačních funkcí SUM(), AVG() a COUNT(). Mnoho implementací však umožňuje použití klíčového slova DISTINCT i u agregačních funkcí MIN() a MAX(), přestože nemají vliv na jejich výsledky. 2. Skupinové dotazy (klauzule GROUP BY) V předchozích kapitolách jsme se zabývali použitím agregačních funkcí v takzvaných souhrnných dotazech, jejichž výsledkem byl vždy jediný řádek obsahující sumarizované hodnoty vypočtené ze všech řádku. Často však požadujeme získat mezisoučty pro různé kombinace atributů. Typickými požadavky mohou být například:
Kolik zaměstnanců mají jednotlivá oddělení společnosti? Jaké jsou tržby poboček společnosti? Kolik lidí jede průměrně v jednom vlaku na dané trase? Jaká je průměrná délka života pro muže a ženy? Jaká je průměrná mzda v různých sektorech? Kolik vstupenek se prodalo na jednotlivá utkání? Jaká je nejvyšší a nejnižší vyplácená sociální dálka v krajích?
David Žák, Jiří Zechmeister, Tomáš Váňa IDAS1/5 – Souhrnné a skupinové dotazy
5
Takovou možnost vytváření mezisoučtů poskytuje použití klauzule GROUP BY. Její použití si ukážeme na následujícím příkladu: Jaká je průměrná cena a počet produktů nabízených jednotlivými dodavateli? SELECT DODAVATEL_ID, AVG(CENA), COUNT(*) FROM PRODUKTY GROUP BY DODAVATEL_ID;
Z výsledku je zřejmé, že u jednoho produktu není uvedena ani cena, ani dodavatel. Dodavatel s DODAVATEL_ID=2 dodává 3 produkty s průměrnou cenou necelých 39667 Kč a dodavatel s DODAVATEL_ID=3 dodává jediný produkt. Vidíme, že v případě skupinových dotazů vzniká více souhrnných řádků – 1 řádek pro každou skupinu, v našem případě byla skupina tvořena sloupcem DODAVATEL_ID. Databázový systém nejdříve rozdělí produkty do skupin podle DODAVATEL_ID, pro každého dodavatele jedna skupina. V rámci každé skupiny mají všechny produkty stejnou hodnotu DODAVATEL_ID. Pro každou skupinu pak vypočte požadované agregační funkce. Dotazy obsahující klauzuli GROUP BY označujeme jako skupinové dotazy, neboť seskupují data ze zdrojových tabulek do skupin a pro každou z těchto skupin vytváří jediný souhrnný řádek. Sloupce uváděné za klauzulí GROUP BY označujeme jako seskupující sloupce. 2.1. Hodnoty NULL v seskupujících sloupcích V obecném případě platí, že při porovnání dvou hodnot NULL za klauzulí WHERE je výsledek NULL (nikoli TRUE!). Jde o prázdnou (neudanou) hodnotu, a dvě hodnoty NULL tedy nejsou považovány za stejné. Jinak je však k hodnotě NULL přistupováno v případě, pokud je tato uvedena v některém ze seskupujících sloupců. V takovém případě považuje standard ANSI/ISO tyto hodnoty za stejné a seskupuje tyto řádky do stejné skupiny.
David Žák, Jiří Zechmeister, Tomáš Váňa IDAS1/5 – Souhrnné a skupinové dotazy
6
2.2. Skupinové vyhledávací podmínky (klauzule HAVING) Obdobně jako za klauzuli WHERE se uvádí vyhledávací podmínka pro řádky, které budou dotazem zpracovávány a následně například spojeny s jinou tabulkou či seskupeny, za klauzuli HAVING se uvádí vyhledávací podmínka, která se aplikuje na již vytvořené skupiny. Funkci klauzule HAVING lze ilustrovat například na tomto příkladu: Vyberte
dodavatele,
kteří
nabízí
více
než
1
produkt?
SELECT DODAVATEL_ID, COUNT(*) FROM PRODUKTY GROUP BY DODAVATEL_ID HAVING COUNT(*) >1;
2.3. Postup generování výsledku dotazu Generování výsledku dotazu probíhá v následujících krocích: 1. Vytvoří se spojení tabulek uvedených za klauzulí FROM (JOIN) dle definovaných podmínek spojení (pokud jsou tyto uvedeny) 2. Obsahuje-li příkaz klauzuli WHERE, aplikuje se vyhledávací podmínka na každý řádek a zachovají se jen ty, které vyhovují uvedené podmínce. Jinak řečeno, pokud uvedená podmínka (která může mít několik částí spojených pomocí AND či OR) je vyhodnocena jako TRUE, řádek je ponechán, pokud je vyhodnocena jako FALSE nebo NULL, je řádek vyloučen z dalšího zpracování. 3. Pokud dotaz obsahuje klauzuli GROUP BY, jsou řádky rozděleny do skupin tak, aby všechny řádky v dané skupině měly shodné hodnoty ve všech seskupujících sloupcích. 4. Nad řádky v jednotlivých skupinách jsou vyhodnoceny agregační funkce, výsledkem je jeden souhrnný řádek pro každou skupinu. 5. Obsahuje-li příkaz klauzuli HAVING, aplikuje se vyhledávací podmínka na souhrnné řádky a ve výsledku jsou ponechány pouze ty, kdy vyhledávací podmínka je vyhodnocena jako TRUE. 6. Obsahuje-li příkaz klauzuli ORDER BY, je výsledek dotazu náležitě seřazen. Uvedený postup je třeba chápat jako ilustrativní a o konkrétním postupu provedení dotazu rozhoduje optimalizátor databázového systému.
David Žák, Jiří Zechmeister, Tomáš Váňa IDAS1/5 – Souhrnné a skupinové dotazy
7
Následující příklad ilustruje komplexnější příkaz využívající možností skupinových dotazů: Zjistěte názvy dodavatelů, kteří nabízí produkty s průměrnou cenou vyšší než 20000 Kč, jejich průměrnou cenu a počet produktů, které nabízejí, neuvažujte produkt s označením 'A2500H'. SELECT DODAVATELE.NAZEV, AVG(PRODUKTY.CENA) PRUM_CENA, COUNT(*) POCET_PRODUKTU FROM DODAVATELE JOIN PRODUKTY ON DODAVATELE.DODAVATEL_ID = PRODUKTY.DODAVATEL_ID WHERE OZNACENI NOT LIKE 'A2500H' GROUP BY DODAVATELE.NAZEV HAVING AVG(PRODUKTY.CENA)>20000 ORDER BY NAZEV;
Pojmy k zapamatování Příkazy a funkce: SELECT s klauzulemi GROUP BY a HAVING, SUM(), AVG(), MIN(), MAX(), COUNT(), hodnota NULL, eliminace duplicitních hodnot DISTINCT Problém: souhrnné a skupinové dotazy, práce s hodnotou NULL v agregačních funkcích, eliminace duplicitních hodnot pomocí DISTINCT, problematika řazení řetězců Shrnutí V této lekci jste se seznámili se souhrnnými a skupinovými dotazy v jazyce SQL a s agregačními funkcemi. Agregační funkce slouží k výpočtu součtu, průměru, minimální a maximální hodnoty nebo k určení počtu hodnot ve sloupci nebo ve výsledku dotazu. Souhrnný dotaz neobsahuje klauzuli GROUP BY a generuje jediný řádek výsledku dotazu. Skupinové dotazy obsahují klauzuli GROUP BY a generují řádky na výstup dotazu pro každou kombinaci atributů uvedených za klauzulí GROUP BY. Klauzule HAVING vykonává obdobnou funkci jako klauzule WHERE. Zatímco klauzule WHERE se aplikuje na řádky vstupující do zpracování, klauzule HAVING se aplikuje až na agregované hodnoty v již seskupených řádcích. David Žák, Jiří Zechmeister, Tomáš Váňa IDAS1/5 – Souhrnné a skupinové dotazy
8
Otázky na procvičení 1. Jaký je rozdíl mezi skupinovými a souhrnnými dotazy? 2. S jakými datovými typy pracuje funkce MIN()? 3. Jaký bude rozdíl ve výsledku agregační funkce COUNT(sloupec) a COUNT(*)? 4. V jakém pořadí jsou aplikovány jednotlivé klauzule příkazu SELECT na zpracovávaná data? 5. Jaký počet řádků může vygenerovat skupinový dotaz?
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.
David Žák, Jiří Zechmeister, Tomáš Váňa IDAS1/5 – Souhrnné a skupinové dotazy
9