SQL umí mnohem víc než SELECT * FROM
Dnes rozšíříme SQL o šest mocných nástrojů: ORDER BY, LIKE, INNER JOIN, LEFT JOIN, GROUP BY + agregace, a rozdíl mezi WHERE a HAVING.
20 zákazníků ze 4 měst, 30 produktů v 6 kategoriích, 30 objednávek, 80 položek.
15 autorů z 5 zemí, 40 knih v 10 žánrech, 20 čtenářů, 50 výpůjček (15 stále nevrácených).
ORDER BYGGlosářWWikipedie CS, LIMITGGlosářWWikipedie CS, DISTINCT
Tři klauzule, které budete denně používat. ORDER BY řadí, LIMIT omezuje počet, DISTINCT odstraňuje duplicity.
ORDER BY cena DESC ORDER BY prijmeni ASC ORDER BY cena DESC, id ASC
ASC = vzestupně (default), DESC = sestupně. Lze řadit podle víc sloupců.
LIMIT 5 LIMIT 10 OFFSET 20
Omezuje počet řádků. OFFSET přeskočí N řádků (stránkování).
SELECT DISTINCT mesto FROM zakaznici
Vrátí jen unikátní hodnoty. Užitečné pro „kolik různých…".
LIKEGGlosářWWikipedie CS – částečná shoda
= hledá přesnou shodu, LIKE umí wildcards: % (libovolně znaků) a _ (právě jeden znak).
| LIKE 'M%' | začíná na M |
| LIKE '%ová' | končí na „ová" |
| LIKE '%kabel%' | obsahuje „kabel" |
| LIKE '___ova' | 3 znaky + „ova" |
Pozor na diakritiku. SQLite LIKE je defaultně case-insensitive jen pro ASCII. LIKE 'nová%' chytí „Novák", ale LIKE 'něm%' nemusí chytit „NĚmec", protože znak ě není považován za stejný jako Ě. Pro češtinu používej UPPER(). Například … WHERE UPPER(prijmeni) LIKE UPPER('Němec%').
INNER JOINGGlosářWWikipedie CS – jen shodné řádky
Cizí klíče by byly k ničemu, kdybychom neuměli tabulky spojit. INNER JOIN vezme jen ty řádky, kde oba klíče odpovídají.
Syntax s aliasy
SELECT o.id, o.datum, z.jmeno, z.prijmeni, z.mesto FROM objednavky o INNER JOIN zakaznici z ON o.zakaznik_id = z.id ORDER BY o.datum DESC LIMIT 5;
o a z jsou aliasy – zkracují zápis. Místo objednavky.datum stačí o.datum. V SELECT lze použít jak aliasy, tak plné názvy.
Praktická ukázka
Chci vypsat všechny autory a počet jejich knih v naší knihovně. Někteří autoři mohou mít 0 knih – chci je přesto vidět.
-- INNER JOIN by autory bez knih vynechal SELECT a.prijmeni, COUNT(k.id) AS pocet_knih FROM autori a LEFT JOIN knihy k ON k.autor_id = a.id GROUP BY a.id ORDER BY pocet_knih DESC;
GROUP BYGGlosářWWikipedie CS + COUNT
GROUP BY seskupí řádky podle hodnoty sloupce. COUNT(*) spočítá, kolik jich v každé skupině je.
SELECT kategorie, COUNT(*) AS pocet FROM produkty GROUP BY kategorie ORDER BY pocet DESC;
Vrátí 1 řádek pro každou kategorii s počtem produktů. AS pocet přejmenuje sloupec ve výstupu.
Pět agregačních funkcíGGlosářWWikipedie CS
Všechny pracují nad skupinou (nebo celou tabulkou, pokud neuvedeš GROUP BY).
| COUNT(*) | počet řádků (včetně NULL) |
| COUNT(sloupec) | počet ne-NULL hodnot |
| SUM(sloupec) | součet |
| AVG(sloupec) | průměr (ignoruje NULL) |
| MIN(sloupec) | minimum |
| MAX(sloupec) | maximum |
Scalar funkce
Agregační funkce pracují na sloupci/skupině. Scalar funkce pracují na jednom řádku:
| ROUND(x, N) | zaokrouhlení na N míst |
| UPPER(text) | velká písmena |
| LENGTH(text) | délka řetězce |
| SUBSTR(text, od, N) | výřez |
WHERE vs HAVING
Obě klauzule filtrují výsledky dotazu. Ale každá v jinou chvíli. Nesmí být proto zaměněny.
Filtruje jednotlivé řádky před tím, než se seskupí. Nemůže používat agregační funkce.
-- OK: filtr jednotlivých objednávek SELECT zakaznik_id, COUNT(*) FROM objednavky WHERE status = 'dokončena' GROUP BY zakaznik_id;
Filtruje skupiny po agregaci. MŮŽE používat agregační funkce (COUNT, SUM atd.).
-- Chci jen zákazníky s > 2 objednávkami SELECT zakaznik_id, COUNT(*) FROM objednavky GROUP BY zakaznik_id HAVING COUNT(*) > 2;
Pořadí provádění v SQL
- FROM – vezmi řádky z tabulek (JOIN)
- WHERE – vyfiltruj řádky před agregací
- GROUP BY – seskup je
- HAVING – vyfiltruj skupiny po agregaci
- SELECT – vyber sloupce (a agregace)
- ORDER BY – seřaď
- LIMIT – omez počet
Klíč k porozumění. WHERE nemůže filtrovat „zákazníky s více než 2 objednávkami", protože v okamžiku WHERE ještě neexistují skupiny – je to jednotlivý řádek objednávky. HAVING to umí, protože běží až po GROUP BY.
Ještě 3 úlohy – nad datasetem knihovna
Přepínáme na knihovna.db. Úlohy používají LEFT JOIN, IS NULL a HAVING – vše, co jsme dnes probrali.
data/*.sql nebo si v playgroundu experimentujte – SQLite nic neuloží.
Souhrnný kvíz – 8 otázek
- ASELECT * FROM produkty LIMIT 3 ORDER BY cena ASC
- BSELECT * FROM produkty WHERE cena MAX LIMIT 3
- CSELECT * FROM produkty ORDER BY cena DESC LIMIT 3
- DSELECT TOP 3 * FROM produkty
- Aprávě jeden znak
- Blibovolný počet znaků (včetně nuly)
- Cčíslice
- Dkonec textu
- AINNER vrátí jen řádky se shodou; LEFT vrátí všechny z levé tabulky a NULL místo chybějících
- BINNER je rychlejší, LEFT pomalejší
- CINNER funguje v SQLite, LEFT jen v MySQL
- DJsou identické
- AWHERE je novější než HAVING
- BWHERE filtruje před GROUP BY, HAVING po GROUP BY (smí používat agregační funkce)
- CWHERE je pro čísla, HAVING pro text
- DJsou totožné
- AŘadí řádky
- BFiltruje řádky
- CSeskupí řádky podle hodnoty sloupce pro následnou agregaci
- DPřejmenuje sloupec
- APočet řádků, kde sloupec NENÍ NULL
- BPrůměr hodnot
- CSoučet hodnot
- DMaximum
- ✓COUNT
- ✓SUM
- ✓AVG
- ✓MAX
- ASELECT * FROM autori WHERE knihy = NULL
- BSELECT a.* FROM autori a LEFT JOIN knihy k ON k.autor_id = a.id WHERE k.id IS NULL
- CSELECT * FROM autori INNER JOIN knihy ON 0 = 0
- DSELECT * FROM autori EXCEPT knihy