Maturitní otázka č. 24 - SQL dotazy

Jazyk SQL má hodně volnou syntax. Tady budeme psát klíčová slova velkými písmeny a používat hodně závorek, ale je to jen pro lepší přehlednost kódu. Taky tady používáme dialekt MySQL, ale to není příliš důležité.

INSERT

Příkaz insert vkládá řádky do tabulky. Nejjednodušší syntax pro vložení jednoho řádku je: INSERT INTO tabulka VALUE ('hodnota_prvního_sloupce', 'hodnota_druhého_sloupce'). Pokud chceme vložit víc řádků zároveň a nastavovat jen některé sloupce, můžeme psát INSERT INTO tabulka (jméno_sloupce1, jméno_sloupce2) VALUES ('hodnota1', 'hodnota2'), ('hodnota3', 'hodnota4'). Nezapomeňte na jednoduché uvozovky okolo každé hodnoty. Jména tabulek a sloupců do uvozovek dávat nemusíte, ale když už, tak do zpětných: `tabulka`.

DELETE

Příkaz delete maže řádky z tabulek. Obvykle se používá jako DELETE FROM tabulka WHERE podmínka LIMIT maximální_počet_smazaných_řádků.

UPDATE

Update mění hodnoty řádků. Syntax je obecně UPDATE tabulka SET (sloupec1='hodnota1', sloupec2='hodnota2') WHERE podmínka. Místo hodnoty můžeme použít i nějaký výpočet se sloupci, například: UPDATE ovce SET (id = -id) WHERE id < 0 změní všem ovcím záporná ID na kladná.

SELECT

Příkaz select slouží k získávání dat z databáze a tedy je asi nejdůležitější. Nijak nemění uložená data.

Základní použití

Při práci s jedinou tabulkou píšeme obvykle SELECT sloupec1, sloupec2 FROM tabulka WHERE podmínka. Pokud chceme získat hodnoty ze všech sloupců, nemusíme je vyjmenovávat a napíšeme jen hvězdičku. Můžeme také slovem limit omezit počet (či rozsah od ... do) získaných řádků a slovy order by si řádky nechat setřídit podle některých sloupců. Například: SELECT * FROM tabulka WHERE podmínka ORDER BY datum ASC, id DESC LIMIT 10,20 získá řádky setříděné nejdřív podle sloupce datum vzestupně, pak podle id sestupně a z výsledku vybere jen řádky od desátého do dvacátého.

Čtení víc tabulek naráz

Pomocí klíčového slova join můžeme získávat data naráz z více tabulek po dvojicích řádků, které si nějakým způsobem odpovídají. Výsledky se chovají tak, jako bychom tím po dvojicích spárovali všechny řádky z obou tabulek (nebo po n-ticích, pokud použijeme n tabulek). Výběr s použitím join na tři tabulky vypadá třeba takhle: SELECT tabulka1.sloupec1, tabulka2.sloupec1, tabulka3.sloupec5 FROM tabulka1 JOIN tabulka2 ON podmínka JOIN tabulka3 ON podmínka WHERE podmínka.

Podmínky zmíněné přímo za join by se měly vztahovat vždycky k připojované tabulce (například: SELECT * FROM bankovni_ucet JOIN banka ON banka.kod = bankovni_ucet.kod_banky). Je to ale jenom pro lepší čitelnost, klidně si můžeme tu podmínku i celou odpustit.

To neplatí ale u příkazů left join, resp. right join. Ty zajistí, aby se ve výsledku objevily nejen všechny úspěšně spárované dvojice řádků, ale také všechny řádky z levé, resp. pravé tabulky. Chybějící hodnoty sloupců druhé tabulky se potom doplní hodnotami NULL. Tím pádem záleží také na pořadí, v jakém tabulky připojujeme (narozdíl od původního jednoduchého případu, který se nazývá inner join).

Tabulku můžeme připojovat i na sebe samotnou. Syntax příkazu pak musíme trochu pozměnit: například, pokud chceme získat všechny dvojice uživatelů, kteří mají narozeniny ve stejný den, napíšeme SELECT prvni.jmeno, druhy.jmeno FROM uzivatele AS prvni JOIN uzivatele AS druhy ON DAY(prvni.datum) = DAY(druhy.datum) AND MONTH(prvni.datum) = MONTH(druhy.datum). Zrovna tenhle příkaz by vypsal všechny kombinace, tj. každou dvojici dvakrát a každého uživatele spároval i samotného se sebou.

Seskupování

Často nechceme získávat přímo řádky, ale počítat nad nimi nějakou statistiku: sčítat je, průměrovat a podobně. Než bychom je všechny stahovali, skoro vždycky se vyplatí nechat počítat databázi.

Pokud chceme zpracovat všechny řádky tabulky do jediného výsledku, stačí si najít vhodnou funkci. Například, počet uživatelů a jejich průměrný věk zíksáme příkazem SELECT COUNT(*), AVG(vek) FROM uzivatele. Zpracovávané řádky můžeme jako obvykle omezit podmínkou where.

Zajímavější je ale situace, kdy nejdřív řádky rozdělíme podle nějakého klíče do skupin a každou pak zpracováváme zvlášť. Jednoduše můžeme předchozí příkaz pozměnit tak, aby počítal průměrnou výšku všech uživatelů určitého věku: SELECT vek, AVG(vyska) FROM uzivatele GROUP BY vek. Jádro věci je ve slovech group by, díky tomu příkaz navrátí několik výsledků místo jednoho. Seskupovat můžeme i podle vypočtené hodnoty: SELECT ROUND(vek, -1) AS zaokrouhleny_vek, AVG(vyska) AS prumerna_vyska FROM uzivatele GROUP BY zaokrouhleny_vek. Podobně je možné seskupovat i podle více kritérií zároveň, pak databáze navrací všechny přítomné kombinace zvlášť.

Transakce

Transakce slouží pro bezpečnost dat, když provádíme několik složitějších příkazů. V rámci jednoho připojení k databázi můžeme začít transakci příkazem START TRANSACTION; později ji buďto potvrdíme příkazem COMMIT; nebo zrušíme příkazem ROLLBACK;. Úpravy dat, které jsme během transakce naplánovali, se provedou buďto zcela, anebo vůbec. Stručná (absurdní) představa, kde to použít, je převod peněz z účtu na účet: když by mezi oběma dotazy na databázi spadlo spojení, měli bychom problém.

Má to dva háčky. Především, tabulka musí transakce vůbec podporovat; to zvládá například engine InnoDB, ale je potřeba ho nastavit ručně (je prý o něco pomalejší). Druhak, v PhpMyAdminu (webovém rozhraní) se vám při každém načtení stránky založí a ukončí nové spojení, takže se transakce vždycky potvrdí. Když si je budete chtít vyzkoušet, musíte napsat víc dotazů zároveň a oddělit je středníkem.

Bonus?

SQL toho umí mnohem víc. Je to ukázkový deklarativní — neprocedurální jazyk; píšeme co chceme spočítat a vůbec se nezmiňujeme o tom, jak to má databáze zařídit. Snaží se tedy mít syntax takovou, aby umožňovala jednoduše zapsat prakticky cokoliv, co vás napadne. Je možné různě kombinovat příkazy: třeba DELETE ... JOIN nebo INSERT ... SELECT. Nemá smysl se toho učit moc nazpaměť, ale přijde mi i zábavné objevovat ty zběsilé kombinace.

Kromě toho je možné v SQL psát příkazy z oblasti procedurálního programování, jako function, while atd.