Databáze

Pod pojmem databáze si můžeme představit soubor dat, který je strukturovaný a je uzpůsobený pro snadné vyhledání v těchto datech. Vyhledávání může probíhat na základě obecných kritérií. Nejčastější se bavíme o relačních databázích, které udržují data ve formě vzájemně provázaných tabulek. V rámci tohoto tématu se budeme věnovat právě relačním databázím. Cílem je osvojit si vytváření tabulek, vkládání a úpravu dat a hlavně se naučit v datech vyhledávat - formulovat dotazovací kritéria tak, abychom dostali ty odpovědi, které nás zajímají.

Úvod

Hlavní vlastnosti relačních databází:

Oblasti umožňující přístup k databázím

Abychom mohli s databázemi pracovat, nabízí nám výrobci unifikovaný jazyk, pomocí kterého s databází komunikujeme. Ten se rozděluje na 3 základní druhy:

SQL

Pro jednoduchost začneme rovnou jazykem SQL. Budeme tedy pro začátek předpokládat, že databázi za nás někdo vytvořil a naplnil nějakými daty. Naším úkolem tedy bude osvojení si pokládání správných dotazů. Pro tvoření dotazů slouží jazyk SQL, který je navržen tak, aby vypadal pokud možno co nejvíce jako obyčejná anglická věta.

Jak už bylo v úvodu řečeno, relační databáze uchovávají data v podobě tabulek. Představme si tedy, že se naše databáze skládá z planet sluneční soustavy. Mohla by vypadat třeba následovně.

tabulka planety
nazev delka_obehu [roky] vzdalenost_od_slunce [km]
Merkur0,24057 900 000
Venuše0,615108 200 000
Země1,000149 600 000
Mars1,880227 900 000
Jupiter11,862778 300 000
Saturn29,4471 429 200 000
Uran84,0162 875 000 000
Neptunnull4 504 000 000

Za slovem SELECT vyjmenujeme sloupce, které chceme zobrazit a za slovem FROM napíšeme jméno tabulky, ve které chceme vyhledávat. Všimněte si, že název sloupce může být vždy pouze jedno slovo, proto víceslovné názvy spojujeme většinou pomocí podtržítka. Bývá také dobrým zvykem nepoužívat u názvů tabulek a sloupečků diakritiku.

Pamatujte si: Jazyk SQL není CASE-SENSITIVE, čili nerozlišuje velká a malá písmena. Pozor: to ovšem platí pouze u klíčových slov a názvů tabulek a sloupečků. Přímo v datech (například název planety) databáze už velikosti písmen rozlišuje.

Null hodnoty

V tabulce nemusí být vždy všechny hodnoty u všech sloupců vyplněny. V takovém případě mluvíme o tzv. NULL hodnotách. V předchozí tabulce se jedná o případ Neptunu a jeho délky oběhu.

Primární klíč tabulky

V relačních databázích musí být každý řádek jasně definován pomocí tzv. primárního klíče. Je to hodnota sloupečku, která musí být v celé tabulce unikátní. V našem případě bychom mohli jako primární klíč zvolit například název planety - to by nám zajistilo, že nikdo nebude moci vložit do tabulky dva řádky se stejným názvem. Nicméně nejčastější praxí je, že se do každé tabulky přidává sloupeček s názvem ID, který představuje pro každý řádek unikátní číslo. Naše tabulka planet by pak mohla vypadat takto.

tabulka planety
id nazev delka_obehu [roky] vzdalenost_od_slunce [km]
1Merkur0,24057 900 000
2Venuše0,615108 200 000
3Země1,000149 600 000
4Mars1,880227 900 000
5Jupiter11,862778 300 000
6Saturn29,4471 429 200 000
7Uran84,0162 875 000 000
8Neptunnull4 504 000 000

Tento přidaný sloupec si můžeme jako každý jiný sloupec zobrazit:

Primární klíče budeme používat hlavně pro spojování tabulek, které si ukážeme v dalších kapitolách.

Výběr sloupců

Jak lze vybrat seznam sloupců k zobrazení jsme si již ukázali dříve. Přidáme tedy pouze pár jednoduchých poznámek. Pořadí sloupců nemusí dodržovat pořadí sloupců definovaných v tabulce. Můžeme si vybrat pouze podmnožinu sloupců, nebo můžeme stejný sloupec zobrazit třeba dvakrát - to ale není moc praktické. Každý vybraný sloupec si můžeme ve výstupních datech pojmenovat podle vlastního uvážení tím, že za název sloupce uděláme mezeru a napíšeme, jak chceme, aby se sloupec jmenoval ve výstupních datech.

Poslední poznámka k výběru sloupců k možnosti zobrazit jednoduše všechny sloupce, aniž bychom je museli všechny vypisovat. Toho dosáhneme pomocí hvězdičky.

Sloupce lze také spojovat mezi sebou nebo přidávat textové řetězce či čísla. Spojování jednotlivých sloupečků, textu i čísel se provádí pomocí znaku |. Spojením pak vznikne jeden sloupeček ve výstupní tabulce.

U takových sloupců, které vznikly spojením několika výrazů, pak většinou používáme přejmenování, aby název výsledného sloupečku vypadal lépe. Konkrétně zde ho celý pojmenujeme názvem veta.

Úlohy
  1. Z tabulky planety zobrazte sloupečky ID a název.

  2. Z tabulky planety zobrazte sloupečky ID a název, kde název bude přejmenovaný na sloupeček planeta.

  3. Z tabulky planety zobrazte všechny sloupečky, které tabulka má, aniž byste je museli všechny vyjmenovávat.

  4. Z tabulky planety zobrazte data tak, aby se pro každou řádku zobrazila věta ve tvaru př: "Mars oběhne Slunce 1,88 krát za rok s průměrnou vzdáleností 227 900 000 km." Tato věta ať se zobrazí ve sloupečku pojmenovaném pojednani_o_planetach_nasi_slunecni_soustavy.

Výběr řádků

Stejně tak jako nám SQL umožňuje vybrat sloupečky tabulek, máme také možnost vybírat konkrétní řádky. Toho nedosahujeme vyjmenováním řádků jako v případě sloupců (řádky totiž nemají své jméno), ale nadefinováním podmínek, které musí být pro řádek splněny, aby se dostal do výstupu dat. Tyto podmínky se skládají z logických výrazů a můžeme tak vytvořit podmínky od těch nejsnazších až po velice komplexní. Podmínky většinou obsahují názvy sloupečků a píší se za klíčové slovo WHERE.

Podmínky můžeme skládat pomocí logických spojek AND a OR, kde AND má větší prioritu - stejně jako má násobení přednost před sčítáním. Pokud tuto potřebujeme tuto prioritu změnit, můžeme použít závorky, stejně jako v matematickém výrazu.

Při tvoření podmínek můžeme používat následující operátory (výčet obsahuje pouze základní operátory):

Pojďme si zmíněné operátory vyzkoušet na příkladech. Nejdříve si zobrazíme všechny planety, které mají ID větší než 3.

Nyní si zobrazíme planety, které se v názvu obsahují písmeno a - na kterémkoli místě.

Pamatujte si: Pokud porovnáváme řetězce jako celek můžeme použit operátor = resp. != nebo LIKE reps. NOT LIKE. Ovšem v případě, že hledáme podřetězec, musíme použít operátor LIKE nebo NOT LIKE.

Nebo si můžeme nechat vypsat ty planety, které končí na slovo tun.

Můžeme si také vypsat řádky, které neobsahují část slova tu.

Nebo můžeme použít obdobu pomocí NOT LIKE operátoru.

Nebo si můžeme nechat vypsat ty planety, které nemají zadanou hodnotu delka_obehu.

Nebo naopak ty planety, které mají délku oběhu zadanou.

Úlohy
  1. Napište dotaz pro nalezení všech planet, které se nejmenují Mars.

  2. Napište dotaz pro nalezení všech planet, které jsou od Slunce vzdálené aspoň 300 000 000 km.

  3. Napište dotaz pro nalezení všech planet, které obíhají Slunce méně než dva roky a nebo nemají dobu oběhu vůbec zadanou. Ve výstupu přejmenujte délku oběhu na doba.

Řazení

Výstup dat můžeme kromě filtrování také řadit sestupně nebo vzestupně dle výběru. Vždy uvedeme název sloupce a směr řazení. Můžeme také řadit v několika úrovních - nejdříve podle jednoho sloupce a v případě shody podle druhého atd. Důležité je ale poznamenat, že při definování řazení se musí používat názvy sloupců tak, jak se objevují na výstupu. Pokud jsme si tedy některé sloupce přejmenovali, je lepší uvést naše vlastní jméno sloupečku.

Pro řazení se používá klíčové slovo ORDER BY a za něj se vyjmenují sloupečky, podle kterých chceme řadit. Pro sestupné řazení přidáme klíčové slovo DESC, pro vzestupné ASC.

Na příkladu si ukážeme, jak seřadit planety podle názvu vzestupně.

V případě podmínky pro výběr řádků se řazení definuje až za podmínkami. následující dotaz zobrazí planety, které jsou blíže Slunci než Země (mají délku oběhu menší než jeden rok) a seřadí je dle vzdálenosti sestupně. V případě, že by byly dvě planety stejně daleko, jako druhou úroveň třízení použije název vzestupně.

V případě, že se přejmenuje název výstupního sloupce, uvádíme v řazení nové jméno.

Úlohy
  1. Napište dotaz pro nalezení názvů všech planet, které začínají na písmeno M a výslednou tabulku seřaďte podle těchto názvů sestupně.

  2. Planety vypište pomocí jednoho sloupce, který bude mít formát "ID - nazev", bude se jmenovat planeta a podle tohoto sloupce setřiďte výsledek vzestupně. Do výsledné tabulky zahrňte pouze řádky, které mají ID větší než 3.

Agregační funkce

Někdy nás může zajímat pouze agregační pohled na data. S tím nám mohou pomoci tzv. agregační funkce, které vracejí pouze jednu hodnotu vypočtenou ze všech řádků (které splňují podmínku WHERE).

Například celkový počet vybraných řádků - konkrétně například těch, co mají zadanou délku oběhu. Použijeme funkci COUNT.

Nebo průměrná doba oběhu planet, které mají dobu oběhu větší než 1. Použijeme funkci AVG.

Nebo minimální a maximální vzdálenost planet od Slunce. Použijeme funkci MIN a funkci MAX.

Úlohy
  1. Napište dotaz pro nalezení maximální doby oběhu ze všech planet, které mají ve svém názvu písmeno u. Výsledný sloupeček pojmenujte MAX_DOBA_OBEHU.

  2. Napište dotaz pro nalezení počtu planet, které mají dobu oběhu menší než 2 roky.

Dekompozice dat

Náš první případ, kdy databázi tvořil seznam platet, nebyl příliš komplexní. V praxi potřebujeme pracovat se složitější strukturou dat, která se pomocí jedné tabulky špatně modelují. Uveďme si následující příklad. Pro evidenci jednoho ročníku výuky čar a kouzel v bradavické škole potřebujeme následující data.

Pokud bychom vzali tento seznam a vytvořili z něj jednu strukturu, do které budeme data ukládat, vznikla by nám asi následující tabulka.

tabulka bradavice
Jméno studenta Příjmení studenta Pohlaví studenta Email studenta Kolej Předmět Titul učitele Jméno učitele Příjmení učitele Pohlaví učitele
Harry Potter muž potter@bradavice.uk Nebelvír Lektvary profesor Severus Snape muž
Hermiona Grangerová žena grangerova@bradavice.uk Nebelvír Přeměňování profesor Albus Brumbál muž
Ron Weasley muž weasley@bradavice.uk Nebelvír Lektvary profesor Severus Snape muž
Draco Malfoy muž malfoy@bradavice.uk Zmijozel Lektvary profesor Severus Snape muž
Luna Láskorádová žena laskoradova@bradavice.uk Havraspár Lektvary profesor Severus Snape muž
Neville Longbottom muž longbottom@bradavice.uk Nebelvír Kouzla profesor Minerva McGonagallová žena
Cho Changová žena changova@bradavice.uk Havraspár Pozemky lesník Rubeus Hagrid muž
Vincent Crabbe muž Zmijozel Černá magie profesor Severus Snape muž
Gregory Goyle muž Zmijozel Bylinkářství Pomona Prýtová žena
Cedric Diggory muž diggory@bradavice.uk Mrzimor
Astronomie
profesor Filius Kratiknot
Fred Weasley muž fwealsey@bradavice.uk Nebelvír Jasnovidectví profesorka Sybila Trelawneyová žena
Fred Weasley muž fwealsey@bradavice.uk Nebelvír Pozemky lesník Rubeus Hagrid muž
Fred Weasley muž fwealsey@bradavice.uk Nebelvír Černá magie profesor Severus Snape muž
Fred Weasley muž fwealsey@bradavice.uk Nebelvír Bylinkářství Pomona Prýtová žena

Tabulka sice obsahuje všechna data, která obsahovat má, ale při podrobnějším pohledu objevíme hned několik problémů.

  1. Předně je to zřejmá duplicita dat. To znamená, že některá data se zbytečně opakují. Podivme se třeba na předmět Lektvary. Na něj jsou zapsaní 4 žáci. Jelikož u každého předmětu chceme vidět, kdo jej vyučuje, jméno Severus Snape se nám neustále opakuje na všech 4 řádcích. A to i včetně jeho titulu, pohlaví atd. Ale ve skutečnosti se jedná stále o téhož stejného profesora Snapa. V čem je problém? Obecně duplicitu v datech nemáme rádi a snažíme se jí vyhnout. Duplicita totiž vede:

    1. ke zbytečně zabranému místu - uvědomte se, že databáze běžně pracují se stovkami miliónů řádek. Pokud v každé řádce budeme zbytečně ukládat data, objem těchto zbytečných dat může být nezanedbatelný.

    2. k nekonzistenci dat - nekonzistence znamená, že se data a vztahy mezi nimi nějakým způsobem rozbila. Představte si, že na jedné řádce bychom uvedli, že profesor Snape je muž (například u Harryho Pottera), kdežto u Draca Malfoye, že profesor Snape je žena. Tento fakt je zřejmě v rozporu, ale naše struktura dat toto "nastavení" umožňuje. Při návrhu datové struktury se vždy snažíme o to, aby nekonzistence nemohla vůbec nastat.

    3. k evidenci stejných dat na více místech - někdo by mohl říct, že bude kontrolovat při editaci dat, zda k nekonzistenci nedochází. Ano, to je možné - budeme se kontrolami bránit, aby se nekonzistence do našich dat nedostala. Ovšem i kdyby se nám toto povedlo, vede duplicita dat k potřebě evidovat změny "jednoho záznamu" na více místech. Představte si, že Hagrid složí profesůru a vy mu budete chtít změnit titul na profesora. To znamená, že musíte projít celou tabulku a ve všech řádcích, kde se Hagrid vyskytuje, jeho titul změnit. V našem malém příkladu by to ještě nebylo tak hrozné, změnu bychom prováděli pouze u dvou řádků, ale u milionu záznamů už to dopad mít bude.

    Podobné duplicity můžeme v tabulce vidět hned na několika místech. Je to například:

    Mezi duplicitu bychom mohli zařadit i příklad s kolejí. Na první pohled se může zdát, že informace o tom, jak přiřadit studenta do koleje, není možno vyjádřit jiným způsobem, než že uvedeme, ke které koleji student náleží. Ale duplicita vzniká přímo i s uvedením názvu koleje - v případě, že bychom chtěli nebelvírskou kolej přejmenovat, tak musíme opět projít všechny záznamy a všechny výskyty slova Nebelvír zaměnit za nový název.

  2. Dále si můžeme všimnout ještě jednoho nešvaru - v případě, že sice na škole existuje učitel, kterého chceme vást v naší evidenci, ale tento učitel zrovna tento rok žádný předmět nevyučuje, musíme pro něj zavést speciální řádku, která je ve většině sloupců prázdná. To je případ profesora Filiuse Kratiknota. Podobný problém nastává v případě předmětu, který se tento rok zrovna nevyučuje - například Astronomie. A aby toho nebylo málo, naše tabulka má problém s evidencí studentů, kteří tento rok nenavštěvují žádný předmět - třeba Cedric Diggory.

Problémů s naší tabulkou je tedy více než dost. Lékem na naše problémy bude lepší navržení struktury, do které budeme data ukládat - mluvíme o navržení datového modelu. Tabulky sice budeme používat nadále, ale jednu velkou tabulku se pokusíme rozbít do více menších a uvidíme, jestli to náš problém vyřeší. Budeme se snažit o tzv. dekompozici dat.

První pokus o dekompozici

student
jmeno prijmeni pohlavi email kolej
Harry Potter muž potter@bradavice.uk Nebelvír
Hermiona Grangerová žena grangerova@bradavice.uk Nebelvír
Ron Weasley muž weasley@bradavice.uk Nebelvír
Draco Malfoy muž malfoy@bradavice.uk Zmijozel
Luna Láskorádová žena laskoradova@bradavice.uk Havraspár
Neville Longbottom muž longbottom@bradavice.uk Nebelvír
Cho Changová žena changova@bradavice.uk Havraspár
Vincent Crabbe muž Zmijozel
Gregory Goyle muž Zmijozel
Fred Weasley muž fwealsey@bradavice.uk Nebelvír
Cedric Diggory muž diggory@bradavice.uk Mrzimor
ucitel
titul jmeno prijmeni pohlavi
profesor Severus Snape muž
profesor Albus Brumbál muž
profesor Minerva McGonagallová žena
lesník Rubeus Hagrid muž
Pomona Prýtová žena
profesorka Sybila Trelawneyová žena
profesor Filius Kratiknot muž
predmet
nazev
Lektvary
Přeměňování
Kouzla
Pozemky
Černá magie
Bylinkářství
Jasnovidectví
Bylinkářství
Astronomie
Co zafungovalo

Zkusme se podívat na to, čeho jsme zmíněným rozdělením docílili.

Co se nepovedlo

Některé problémy ale zatím nezmizely a některé jsme rozdělením naopak zavedli:

Úloha

Zkuste se na tabulky podívat a pokračujte v dekompozici ještě dále (nebo navrhněte úplně jinou strukturu podle vaší libosti) tak, abyste se zbavili všech (nebo aspoň většiny ) problémů. Tabulky si zkopírujte do excelu (to už umíte - pamatujete na kopírování dat s formátem nebo jenom s hodnotami :-) ?) a v excelu pracujte s tabulkami tak, že je budete rozdělovat a možná pro vyřešení všech problémů budete muset nějaké sloupce (které zatím naše tabulky nemají) do tabulek přidat - bude to potřeba pro vyřešení vazeb mezi jednotlivými tabulkami. Například abyste byli schopni říct, jaký předmět je vyučován jakým učitelem, do jaké koleje studenti patří, nebo jací studenti navštěvují jaké předměty.

Nápověda může znít takto: zaveďte si primární klíče a odkazujte se na ně.

Finální dekompozice

V této kapitole se pokusíme vyřešit všechny problémy plynoucí z naší dekompozice, jak jsme nastínili v předchozí kapitole. Začněme postupně. Nicméně důležité je říci, že obecně dekompozice dat nemá vždy jedno správné řešení. Podmínky datového modelu se mohou pro každý případ lišit - i malá změna v zadání může vést na odlišný datový model. Pojďme si tedy ukázat jedno z možných řešení.

Duplicita kolejí

Duplicitu snadno odstraníme stejně jako u duplicit studentů a předmětů tak, že si pro koleje zavedeme samostatnou tabulku. Pro každou kolej budeme mít pouze jeden řádek. Přejmenování koleje pak bude spočívat ve změně pouze jednoho řádku.

Duplicita titulů

Tady to uděláme podobně. Akorát tentokrát si nezavedeme tabulku jednu, ale rovnou dvě. V první budou tituly podle druhu - nebudeme rozlišovat, zda se jedná a ženský či mužský druh titulu. Bude to prostě titul profesor. atd. V druhé tabulce, kterou nazveme oslovení, přidáme každou řádku pro mužské (profesor) i ženské (profesorka) oslovení. Oba tyto záznamy budou napojeny na titul, ze kterého pocházejí. Z tabulky ucitel se budeme odkazovat na oslovení podle toho, jaký titul učitel má a zároveň jakého je pohlaví.

Kontaktní údaje

Abychom se vyhnuli problémům s "řídkými" hodnotami emailů, můžeme si pro tabulku kontaktů vytvořit samostatnou tabulku. Do této tabulky vložíme řádek pouze v případě, kdy studentův email známe. Musíme samozřejmě zajistit, že správně propojíme tuto novou tabulku kontakt a tabulku student, abychom poznali, ke kterému studentovi email patří.

Duplicita typů pohlaví muž a žena

S duplicitou si už umíme poradit. Pro danou hodnotu zavedeme samostatnou tabulku pohlavi, do které uvedeme všechny přípustné hodnoty. S výhodou můžeme tuto tabulku napojit jak na tabulku student, tak na tabulku ucitel.

Vazba mezi vyučujícím a předmětem

V našem prvním pokusu o dekompozici jsme již tabulky ucitel a predmet rozdělili, čímž jsme vyřešili duplicity. Takže nám zbývá už jenom zajistit vazbu mezi těmito tabulkami, aby bylo poznat, který učitel učí který předmět.

Vazba mezi studenty a předměty

Poslední bod k řešení (vztah tabulek student a predmet) je skoro stejný jako problematika vazby mezi učitelem a předmětem. Ale skutečně jenom skoro. Ve skutečnosti se jedná o trochu složitější problém. Zatímco u předmětu si můžeme dovolit napojit jeden předmět na jednoho učitele, u studentů je to tak, že jeden předmět můžeme napojit na více studentů.

Schéma finální dekompozice dat bradavické školy

Pojďme všechny předchozí body realizovat a podívejme se, jak budou vypadat jednotlivé tabulky. Na první pohled je vidět, kam se nám namapují různá data. Co na první pohled vidět není, jsou vztahy mezi tabulkami. Částečně si vztahy můžeme zobrazit tak, že zajedeme myší nad buňky tabulek, čímž se nám červeně rozsvítí relevantní záznamy v ostatních tabulkách (poznámka: zobrazení provázání není vždy úplně kompletní, berte to vždy jako ukázku toho, jaké tabulky spolu souvisí).

Ovládání tabulky: primární klíče ON / OFF. | cizí klíče ON / OFF.
kolej   PK
idnazev
1Nebelvír
2Zmijozel
3Mrzimor
4Havraspár
pohlavi   PK
idnazev
1žena
2muž
titul   PK
idnazev
1prof.
2les.
3řed.
4škol.
osloveni   PK
titul_idpohlavi_idosloveni
11profesorka
12profesor
21lesník
22lesník
31ředitelka
32ředitel
41školnice
42školník
ucitel   PK   FK
idjmenoprijmenipohlavi_idtitul_id
1SeverusSnape21
2AlbusBrumbál23
3MinervaMcGonagallová11
4RubeusHagrid22
5PomonaPrýtová1
6SybilaTrelawneyová11
7FiliusKratiknot21
kontakt   PK
idemail
1potter@bradavice.uk
2grangerova@bradavice.uk
3weasley@bradavice.uk
4malfoy@bradavice.uk
5laskoradova@bradavice.uk
6longbottom@bradavice.uk
7changova@bradavice.uk
10fwealsey@bradavice.uk
11diggory@bradavice.uk
student   PK   FK
idjmenoprijmenipohlavi_idkolej_id
1HarryPotter21
2HermionaGrangerová11
3RonWeasley21
4DracoMalfoy22
5LunaLáskorádová14
6NevilleLongbottom21
7ChoChangová14
8VincentCrabbe22
9GregoryGoyle22
10FredWeasley21
11CedricDiggory23
predmet   PK   FK
idnazevucitel_id
1Lektvary1
2Přeměňování2
3Kouzla3
4Pozemky4
5Černá magie1
6Bylinkářství5
7Jasnovidectví6
8Astronomie
vyuka   PK/FK
predmet_idstudent_id
11
21
31
12
22
34
44
......
......

Interaktivní podbarvení sice může vypadat efektivně, ale ve skutečnosti takto databáze nefungují a ani by to při větším počtu řádků nebylo přehledné. Musí tedy existovat způsob, jak vztahy mezi tabulkami vepsat přímo do tabulek. Než si povíme jak na to, podivme se blíže, jaké druhy takovýchto vztahů mohou vlastně existovat.

Vazby mezi tabulkami

Vztahům mezi tabulkami se v databázích říká nejčastěji vazby nebo relace a je to důvod proč jsme na začátku zmínili, že se budeme bavit o relačních databázích. Relační databáze jsou tedy tvořeny tabulkami a vazbami mezi těmito tabulkami. Databáze nám umožňují tyto vazby definovat a využívat je při dotazování se nad daty. Vazeb může existovat celá řada a navíc vazby mohou mít ještě nejrůznější vlastnosti jako je povinnost nebo násobnost, ale my si ukážeme jenom tři základní vazby.

Vazba 1:1

Toto je nejsnazší způsob, jak provázat dvě tabulky dohromady. Název nám říká, že v nejprostším případě bude jeden řádek z tabulky A odpovídat jednomu řádku z tabulky B. Ve skutečnosti ale není důležité, zda tabulky obsahují stejný počet řádků. Důležité je, že k jednomu řádku tabulky A můžeme přiřadit maximálně jeden řádek tabulky B. V našem případě bradavické školy tomu odpovídá vztah mezi tabulkami student a kontakt - jeden student může mít maximálně jeden email.

Vazba 1:N

Kdybychom podmínku v přechozím odstavci změnili a řekli bychom, že jeden student může mít více (N) emailů, už by se nejednalo o vazbu 1:1, ale vazbu 1:N. Vazba 1:N tedy říká, že k jedné řádce tabulky A může existovat 0 až N řádků z tabulky B. Ale zároveň musí platit, že jednomu řádku z B odpovídá vždy maximálně jeden řádek z tabulky A. V našem datovém modelu můžeme vazeb 1:N najít hned několik.

Možná, že někdo z vás chtěl příklady ještě rozšířit o vazbu mezi studenty a zapsanými předměty. Ale to by byla chyba. Tato vazba totiž nesplňuje podmínky vazby 1:N. A to z toho důvodu, že jeden student může mít zapsáno 0..N předmětů, ale jeden předmět nemusí být zapsaný maximálně jedním studentem - na stejný předmět může chodit vícero studentů. Tato vazba totiž odpovídá typu N:M.

Vazba N:M

Jak je v předchozím odstavci uvedeno, naše tabulky student a předmět jsou ve vztahu N:M. Při označení není důležité co je N a co M. Ani není řečeno, že N je více než M atd. Jedná se pouze o označení stavu, kdy jednomu řádku z tabulky A může odpovídat 0 až N řádků z tabulky B a zároveň každému řádku z tabulky B může odpovídat 0 až M řádků z tabulky A.

Realizace vazeb pomocí klíčů

Když jsme si rozdělili, s jakými typy vazeb se můžeme v relačních databázích potkat, můžeme si ukázat, jak jednotlivé typy vazeb v tabulkách realizovat. Trik spočívá v přidání sloupců, pomocí kterých se budeme odkazovat do jiných tabulek. Na začátku jsme se zmínili o tzv. primárních klíčích - primary key, což jsou sloupečky, které nám jasně identifikují řádek tabulky. Tyto primární klíče můžeme použít jako odkaz v jiných tabulkách. Takovýto odkaz na primární klíč jiné tabulky nazýváme cizí klíč - foreign key. Pokud je takovýchto klíčů určující jiný řádek v jiné tabulce více, hovoříme o složených klíčích. V předchozím schématu datového modelu můžeme klikat na PK a FK u jednotlivých tabulek pro zobrazení použitých primárních a cizích klíčů.

Realizace vazby 1:1

Řekli jsme si, že tabulky student a kontakt jsou v našem případě ve vztahu 1:1. Každá z těchto tabulek má svůj primární klíč ID a tak je velmi snadné tyto tabulky provázat. Prostě si řekneme, že záznamy, které k sobě patří, budou mít stejnou hodnotu primárního klíče. Pokud nějaký student email mít nebude, tak se prostě v tabulce kontakt jeho ID neobjeví. Vyzkoušejte si to na interaktivním schématu.

Realizace vazby 1:N

Jako příklad vazby 1:N si uvedeme vztah mezi tabulkami kolej a student. Vazbu zrealizujeme tak, že k tabulce student přidáme cizí klíč kolej_id, který bude ukazovat na primární klíč ID tabulky kolej. ID jedné koleje se může u několika studentů opakovat - to představuje fakt, že několik studentů může patřit do stejné koleje. Jen tak na okraj: pokud bychom dovolili, že některý student ještě není do koleje zapsán, pak by na místě jeho cizího klíče ukazující na kolej byla hodnota null.

Podobným způsobem jsme přidali do tabulky student další cizí klíč se jménem pohlavi_id, který ukazuje na primární klíč ID v tabulce pohlavi a určuje nám tak, jakého pohlaví student je. Podobný cizí klíč pohlavi_id je zaveden i v tabulce ucitel.

Pamatujte si: Je důležité si uvědomit, že u vazby 1:N záleží na tom, do které tabulky přidáme cizí klíč. Pokud bychom to prohodili a klíč přidali do druhé tabulky, rázem změníme vztahy mezi tabulkami. Představte si, že bychom vazbu mezi tabulkou student a kolej realizovali tak, že přidáme cizí klíč student_id do tabulky kolej. Pak bychom dostali situaci, kdy jsme schopni každé koleji přiřadit pouze jednoho studenta a to navíc tak, že jeden student by mohl být přiřazen do vícero kolejí - to opravdu neodpovídá realitě, proto pozor, kam sloupeček cizího klíče přidáváte.
Realizace vazby N:M

Jak už jsme dříve naznačili, v našem modelu existuje jedna vazba typu N:M. Jedná se o vazbu mezi tabulkou student a predmet - stejný student může navštěvovat několik předmětů a zároveň několik různých studentů může navštěvovat stejný předmět. Předpokládejme, že obě tabulky budou mít primární klíč ID identifukující příslušný řádek. Jak provážeme tyto primární klíče, abychom postihli příslušnou vazbu? Můžeme zkusit přidat cizí klíč predmet_id do tabulky student. Tím jsme schopni zachytit fakt, že několik studentů navštěvuje jeden předmět, ale už nejsme schopni postihnout případ, že jeden student chodí na více předmětů. Jasně, mohli bychom místo jednoho cizího klíče přidat klíčů několik - predmet_1, predmet_2 a predmet_3. Tímto způsobem už bychom uměli jednoho studenta zapsat až na 3 předměty. Ale toto řešení není vhodné ze dvou důvodů:

Pokud nám tedy nevyhovuje toto řešení, můžeme zkusit věc otočit a přidat do tabulky predmet sloupeček student_id. No ale po zamyšlení dojdeme k poznání, že tím jsme opět problém nevyřešili. Akorát jsme předchozí nedostatek otočili. Zatím jsme na optimální řešení tedy nepřišli, ale to neznamená, že neexistuje.

Pamatujte si: vazbu N:M můžeme vždy nahradit dvěma vazbami 1:N.

Prakticky si to můžeme znázornit na schématu, pro který se při modelování tabulek a vztahů mezi nimi říká ER - diagram z anglického Entity - Relationship diagram.

Datový model - ER diagram

ER diagram znázorňuje jednotlivé tabulky a jejich sloupečky. Mezi sloupečy jsou zachyceny i primární a cizí klíče realizující vazby mezi tabulkami. Nás nyní zajímá vztah mezi tabulkou student a predmet. Zde je znázorněna vazba N:M.

er

Pokud použijeme pravidlo o substituci N:M vazby pomocí dvou 1:N vazeb, můžeme ER diagram upravit následovně. Zavedeme si umělou tzv. vazební tabulku, na kterou z obou stran pomocí 1:N vztahu napojíme původní tabulky, které mezi sebou vystupovaly ve vztahu N:M. Vazební tabulka se bude sestávat pouze ze dvou sloupečků obsahující cizí klíče - jeden (student_id), ukazující na primární klíč tabulky student a druhý (predmet_id), ukazující na primární klíč tabulky predmet. V našem případě dává smysl, abychom vazební tabulku pojmenovali třeba vyuka. Ale jsou situace, kdy se nám ani žádné "hezké" jméno nalézt nepodaří. Pak se dá jméno vytvořit prostou složeninou názvů obou tabulek - něco jak studento_predmet nebo prostě student_predmet. V tomto případě je čeština spíše na škodu oproti anglickému způsobu skládání jmen. Je to jeden z důvodů proč i čeští databázoví analytici a programátoři používají spíše anglické názvy tabulek a sloupců. ER diagram s vazební tabulkou vyuka vypadá následovně.

er

A proč jsme říkali, že vazební tabulka se na původní tabulky váže vazbou 1:N? Je to z toho důvodu, že jeden student (student_id) se v tabulce vyuka může objevit 0..N krát. A to stejné platí i pro jeden předmět (predmet_id). Vraťte se zpátky k finálnímu dekomponovanému modelu a zapněte si zobrazení primárních i sekundárních klíčů, čímž se objeví vazební tabulka vyuka, u které můžete při najetí myši sledovat, jak se cizí klíče starají o propojení navázaných tabulek.

Úlohy

Navrhněte ER diagram pro databázi pro následující případy:

  1. Pro místní jídelnu navrhněte DB pro evidenci žáků, jídel a objednávek na konkrétní den. Každý žák si může na jeden den objednat maximálně jedno jídlo.

  2. Pro místní pobočku Kaflandu navrhněte systém, který eviduje druh zboží, jeho jednotkovou cenu, v jakém počtu je na skladě k dispozici. Podle zákaznických karet evidujte, jaký zákazník si který produkt koupil - evidujte jednotlivé položky, datum a cenu nákupu. Zkuste si pamatovat, kolik za ně skutečně v daný čas zákazník zaplatil.

Spojování tabulek

Nyní už víme, jak lze data dekomponovat pomocí několika tabulek a vztahů mezi nimi. Ovšem to, že jsme data "rozdrobili", nám může vadit v momentě, kdy chceme tabulky pomocí SQL selectů procházet a data zobrazovat. Zatím jsme se naučili zobrazovat data pouze z jedné tabulky, ale to ve většině případů nestačí a my se musíme naučit, jak lze v selectu jednotlivé tabulky přes cizí klíče spojovat dohromady. Asi se ptáte, proč jsme tabulky rozdělovali, když je nyní opět budeme spojovat dohromady. Dobrá otázka. Jde o to, že dekomponovaná data se dobře ukládají. Pamatujete? Dekompozici jsme dělali kvůli odstranění duplicit. Ale v náhledu na data se nám duplicity mohou hodit. Nicméně duplicitní zobrazení použijeme třeba jenom na malý vzorek dat, který omezíme pomocí podmínky WHERE. Navíc občas data nemusíme zobrazovat celá - můžeme nad daty provádět agregační výpočty (například počítat, kolik záznamů k tabulce A existuje v tabulce B). Místo dlouhého popisování si to pojďme raději ukázat na příkladu.

Alias tabulky

Dříve než začneme tabulky spojovat, ukážeme si ještě jednu vlasnost SQL dotazu, bez které bychom se u propojení více tabulek neobešli. Jelikož nyní budeme pracovat s vícero tabulkami, které mohou obsahovat stejné názvy sloupečků, musíme jasně rozlišit, jaké tabulce konkrétní sloupečky náleží. Abychom před každým názvem sloupečku nemuseli opakovat celý název tabulky, můžeme si pro každou tabulku zvolit tzv. alias.

Pamatujte si: Je zvykem používat jednopísmenné aliasy podle prvního písmene tabulky. Když dvě tabulky začínají stejným písmenem, můžeme použít klidně dvoupísmenný alias atd. Definice aliasu následuje za tabulkou a je od ní oddělen mezerou.

V následující ukázce jsme si pro tabulku student zavedli alias s, pomocí kterého se na tabulku můžeme odkazovat jak v sekci vybrání sloupečků, tak v sekci podmínky WHERE.

Join

Představte si, že si chceme vypsat studenty společně s informací o tom, do které koleje chodí. Určitě se budeme dotazovat na tabulku student, ale zároveň budeme chtít tuto tabulku napojit na tabulku kolej. A to pomocí cizího klíče kolej_id v tabulce student. Výsledný select bude vypadat takto:

Spojení tabulek je implementované pomocí klíčového slova JOIN, za které se napíše název tabulky, kterou chceme s první tabulkou propojit. Aby databáze věděla, které řádky má spojit dohromady, za klíčové slovo ON napíšeme názvy sloupečků, u kterých se zkoumá rovnost.

V našem případě jsme si pro tabulku student zvolili alias s a pro tabulku kolej alias k a tabulky jsme spojili přes sloupce k.id a s.kolej_id. Jinými slovy říkáme, že chceme zobrazit všechny řádky z tabulky student, u kterých existuje vazba na tabulku kolej a to tak, že ke každému řádku přidáme řádek z tabulky kolej tak, aby si odpovídaly student.kolej_id a kolej.id. Výsledek si můžete prohlédnout v editoru.

Všimněte si, že výsledná spojená tabulka obsahuje všechny sloupce z obou tabulek. Pokud bychom chtěli vypsat pouze některé sloupečky, můžeme použít způsob zápisu, který známe ze selectů nad jednoduchou tabulkou. Prostě požadované sloupečky vyjmenujeme, místo toho, abychom použili *. I zde bývá dobrým zvykem před každý název sloupce uvádět alias tabulky. Jako příklad si uveďme vypsání tabulky, která zobrazí pouze jméno a příjmení studenta (to rovnou zobrazíme sloučené v jednom sloupci s názvem jméno_studenta), jeho id a název koleje, kterou navštěvuje.

Spojením dvou či více tabulek nám ve skutečnosti vznikne nová tabulka a s jako takovou s ní můžeme pracovat úplně stejně, jako kdyby se jednalo pouze o základní jednu tabulku. Na spojenou tabulku můžeme aplikovat například podmínky pro filtrování řádků nebo aplikovat řazení podle jednotlivých sloupců. Můžeme si například vypsat pouze studenty, kteří chodí do Nebelvíru a výsledný seznam můžeme seřadit podle příjmení studentů. V tom případě bude lepší otočit výpis jmen tak, aby se první zobrazovalo příjmení a až pak jméno.

Spojovat samozřejmě můžeme i více tabulek. Představte si, že bychom k našemu seznamu chtěli uvést i e-mail studenta. E-mail najdeme v tabulce kontakt a pokud si vzpomínáte, vazba mezi tabulkou student a kontakt je typu 1:1 - příslušný studentův kontakt najdeme podle shodnosti primárních klíčů. Pro přehlednost vypíšeme podstatné sloupečky a některé přejmenujeme tak, aby nedošlo k duplicitě názvů - konkrétně sloupec id z tabulek student a kontakt.

Všimněte si, že jsme naši tabulku propojili s další tabulkou kontakt (jako alias jsme zvolili knt, protože samotné k by nám vedlo ke konfliktu s aliasem pro tabulku kolej). a přes primární klíče s.id a knt.id jsme tabulku z kontakty připojili. Z nově připojené tabulky jsme použili pouze sloupec knt.id a knt.email. Podobným způsobem můžeme výslednou tabulku seskládat z velkého množství zdrojových tabulek tak, že jednotlivé tabulky budeme přidávat pomocí klíčového slovo JOIN ... ON ....

Pokud se pozorně podíváte na předchozí příklad, všimnete si, že z nějakého důvodu nám ze seznamu vypadli někteří studenti - jsou to Crabbe a Goyle. Proč se to stalo? Je to právě kvůli spojování tabulek. Konkrétně v našem případě kvůli připojení tabulky kontakt. Mezi tabulkou studnet a kontakt sice existuje vazba 1:1, ale zároveň jsme řekli, že to neznamená, že pro každého studenta musí být kontakt uveden - vazba 1:1 říká, že každý student může mít maximálně jeden kontakt a naopak. V našem případě mluvíme o tom, že záznam z tabulky kontakt je ve vazbě nepovinný. A to právě nastalo u studentů Crabbe a Goyle.

Když jsme se snažili tabulku kontakt připojit k tabulce student pomocí klíčového slova JOIN, databáze pro tyto dva studenty nenašla existující záznam v tabulce kontakt, a proto tyto dva řádky reprezentující studenty neměla s čím spojit. Výsledkem je vynechání těchto studentů z výsledného seznamu.

Pamatujte si: JOIN dává do výsledné tabulky pouze záznamy, které lze pomocí propojení najít v obou zdrojových tabulkách - jak v levé (před JOINem), tak v pravé (za JOINem). Řádky, které nemají v partnerské tabulce příslušný záznam jsou z výsledného výpisu vynechány.

Left (Outer) join

Chování JOINu popsané v předchozí kapitole se nám nemusí vždy hodit. Někdy budeme potřebovat seznam všech studentů, ať už kontakt uvedený mají či nikoli. U těch, kteří kontakt mají, by se ve výpise objevil e-mail, a u těch, kteří kontakt nemají, by zůstalo prázdné políčko - vlastně null hodnota (schválně jestli si ještě vzpomínáte, co to null hodnota je).

Popsaného chování jsme schopni dosáhnout pomocí tzv. LEFT JOINU. Správný název by měl být LEFT OUTER JOIN (oproti obyčejnému JOINU, který někdy nazýváme správně INNER JOIN), ale databáze nás nenutí slovo INNER a OUTER používat, tak se obejdeme bez nich. Důležité je vědět, jaký je mezi JOIN a LEFT JOIN rozdíl. A ten spočívá přesně v tom, co bylo popsána na začátku této kapitoly.

Pamatujte si: LEFT JOIN dává do výsledné tabulky všechny záznamy z levé tabulky a ke každému řádku připojí příslušný řádek z pravé tabulky. Pokud příslušný řádek v pravé tabulce neexistuje, doplní místo něj hodnoty null.

Můžeme si to hned ověřit na našem přechozím příkladu, kde místo obyčejného JOINu použijeme LEFT JOIN (pro názornost budeme spojovat pouze tabulku student a kontakt). Studenti Crabbe a Goyle se nám tentokrát v tabulce objeví přesně tak, jak jsme chtěli.

Nenechte se zmást. Ani použití LEFT JOINu vám nezaručí, že se do výsledného seznamu dostane každý student právě jednou. To bude fungovat pouze v případě, že spojujete tabulky s vazbou 1:1. Pokud jakýkoli JOIN použijete na vazbu 1:N nebo M:N, tak se řádek do výsledku dostane tolikrát, kolikrát se objeví ve vztahu s druhou tabulkou. Jako příklad si můžeme uvést vazbu 1:N, která je mezi tabulkami pohlavi a ucitel.

V příkladu vidíme, že položka muž či žena se ve výsledné tabulce vyskytne tolikrát, kolikrát je obsažena ve vazbě - jinými slovy, tolikrát kolik je na dané pohlaví napojeno učitelů.

Jako další příklad si uvedeme jedinou vazbu N:M, kterou v našem databázovém schématu máme. Jde o vazbu mezi tabulkou predmet a student. Víme, že je realizována pomocí vazební tabulky vyuka. Tuto tabulku použijeme v následujícím selectu.

Jak je vidět, v seznamu se opakují jak studenti (pokud si zapsali vícero předmětů), tak předměty (pokud si jeden předmět zapsalo více studentů). Protože jsme použili JOIN, z výsledného seznamu vypadnou ti studenti, kteří nemají zapsaný žádný předmět a také ty předměty, který si nikdo nezapsal.

Kdybychom chtěli zachovat v seznamu všechny studenty, museli bychom použít LEFT JOIN. Na tomto příkladu si také ukážeme, že u LEFT JOINu záleží na pořadí spojovaných tabulek - jinými slovy záleží na tom, která tabulka se objeví vlevo (první) a která vpravo (druhá). V následujícím LEFT JOINu uvedeme první jako tabulku student. Tím se nám do seznamu dostane i Luna Láskorádová, která nemá zapsaný žádný předmět. Ale ty předměty, které si nikdo nezapsal, se v seznamu neobjeví.

Kdežto v následujícím příkladu uvedeme jako první tabulku predmet, čímž se nám do seznamu dostane Jasnovidectví a Astronomie, které žádný student nenavštěvuje. Luna Láskorádová v seznamu ale není.

V následujícím ukázce můžeme vidět, že v podmínce (LEFT) JOIN ... ON ... můžeme použít i složitější podmínky složené pomocí logických operátorů AND a OR. Vazba mezi tabulkami ucitel a osloveni není tvořena pouze jedním sloupcem - tabulka osloveni má totiž složený primární klíč tvořený dvěma sloupci titul_id a pohlavi_id. Pokud chceme ke každému učiteli zobrazit jeho titul závislý na pohlaví, musíme vytvořit následující select.

Rozdíl mezi JOIN a LEFT JOIN

kolej   PK
idnazev
1Nebelvír
2Zmijozel
3Mrzimor
4Havraspár
4Nicota
student   PK   FK
idjmenoprijmenikolej_id
1HarryPotter1
2HermionaGrangerová1
3RonWeasley1
4DracoMalfoy2
5LunaLáskorádová4
6NevilleLongbottom1
7ChoChangová4
8VincentCrabbe2
9GregoryGoyle2
10FredWeasley1
11CedricDiggory3
12NovákNováčeknull
JOIN   PK   FK
KOLEJ STUDENT
idnazev idjmenoprijmenikolej_id
1Nebelvír1HarryPotter1
1Nebelvír2HermionaGrangerová1
1Nebelvír3RonWeasley1
1Nebelvír6NevilleLongbottom1
1Nebelvír10FredWeasley1
2Zmijozel4DracoMalfoy2
2Zmijozel8VincentCrabbe2
2Zmijozel9GregoryGoyle2
3Mrzimor11CedricDiggory3
4Havraspár5LunaLáskorádová4
4Havraspár7ChoChangová4
LEFT JOIN   PK   FK
KOLEJ STUDENT
idnazev idjmenoprijmenikolej_id
1Nebelvír1HarryPotter1
1Nebelvír2HermionaGrangerová1
1Nebelvír3RonWeasley1
1Nebelvír6NevilleLongbottom1
1Nebelvír10FredWeasley1
2Zmijozel4DracoMalfoy2
2Zmijozel8VincentCrabbe2
2Zmijozel9GregoryGoyle2
3Mrzimor11CedricDiggory3
4Havraspár5LunaLáskorádová4
4Havraspár7ChoChangová4
5Nicotanullnullnullnull
Úlohy
  1. Zobrazte z učitelského sboru pouze ženy.

  2. Zobrazte pouze ty studenty, kteří nemají zadaný email.

  3. Zobrazte profesory (muže) s jejich správným oslovením.

  4. Zobrazte studentky, které chodí do nebelvíru.

  5. Zobrazte všechny studentky, které chodí na nějaký předmět s Hermionou Grangerovou.

  6. Zobrazte předměty, které vyučuje ředitel školy.

  7. Zobrazte pouze ty studenty, kteří mají zadaný email a mají ve jméně slovo bottom.

  8. Zobrazte studenty, kteří chodí na předmět Lektvary.

  9. Zobrazte studentky, které učí nějaká profesorka.

  10. Zobrazte studenty, kteří nejsou zapsáni na žádný předmět.

  11. Zobrazte studenty, kteří nejsou zapsáni na žádný předmět.

  12. Zobrazte učitele, kteří neučí žádný předmět.

  13. Vypiště předměty předměty, kde se mohou setkat studenti z Nebelvíru a Zmijozelu.

Seskupování

Při spojování tabulek dost často používáme tzv. seskupování. Jedná se o způsob jak odstranit duplicitní řádky ve výstupní tabulce. Možná bychom to mohli nazvat termínem setřesení. Při setřesení často používáme agregační funkce, pomocí kterých můžeme se setřásanými záznamy provádět různé operace - například spočítat počet duplicitních řádků, nebo spočítat maximální, minimální nebo průměrné hodnoty.

Seskupování pomocí GROUP BY

Nejlepší bude ukázat si to na příkladu. Představme si, že chceme zjistit obsazenost jednotlivých kolejí. Začněme tím, že si vypíšeme seznam kolejí a studentů.

Když chceme zjistit obsazenost kolejí, jednotlivá jména studentů nás ve skutečnosti nezajímají. Proto je můžeme z výsledné tabulky odstranit. Tím nám ve výsledné tabulce zbudou pouze hodnoty, které jsou v několika řádcích duplicitní.

Tyto duplicitní řádky můžeme odstranit a zároveň si pro každý unikátní řádek nechat spočítat, kolikrát se ve výsledné tabulce vyskytoval. Pro seskupení použijeme klíčové slovo GROUP BY, za které uvedeme seznam sloupců, přes které chceme zjišťovat duplicitu - tento seznam nám určí, které sloupečky mají být považovány za unikátní. Počet duplicitních řádků pro každý unikátní skupinu řádků zjistíme pomocí agregační funkce COUNT. Počet duplicitních řádků pro každou kolej představuje počet studentů patřící k této koleji. Proto jsme sloupeček s COUNT(*) přejmenovali na pocet_studentu.

Dost často chceme mít výstup seřazený podle agregačních kritérií. V našem případě podle obsazenosti kolejí - od nejobsazenější koleje až po nejméně obsazenou. Abychom toho dosáhli, stačí k předchozímu selectu přidat klauzuli ORDER BY a říct, že chceme řadit podle sloupečku pocet_studentu sestupně.

Ukážeme si ještě, že klíčové slovo GROUP BY můžeme samozřejmě používat ve spojení se vším, co jsme se zatím naučili. Výslednou tabulku můžeme řadit podle sloupců a stejně tak můžeme filtrovat řádky pomocí klíčového slova WHERE. U seskupování si ale musíme uvědomit, že při použití WHERE dochází k filtrování řádků před samotným seskupením. Proto se také WHERE píše před GROUP BY. Kdybychom chtěli zjistit obsazenost kolejí dívkami, použili bychom následující select.

Pamatujte si: Při seskupování klauzule WHERE nefiltruje řádky výsledné seskupené tabulky, ale řádky, které do seskupování vstupují.
Úlohy
  1. Kolik můžů působí v učitelském sboru?

  2. Zobrazte seznam předmětů a počty studentů, kteří předmět navštěvují.

  3. Zobrazte seznam studentek a počet předmětů, které navštěvují.

  4. Zobrazte seznam učitelů a kolik předmětů vyučují.

Klauzule HAVING

Co kdybychom ale chtěli filtrovat řádky po seskupení? A ještě k tomu podle výsledku agregačních funkcí. Například chtěli bychom vypsat jenom ty koleje, kde je obsazenost větší než nějaká hodnota? V ten moment nám WHERE moc nepomůže a musíme použít nové klíčové slovo HAVING. Za toto klíčové slovo napíšeme podmínky, podle kterých se do výsledné tabulky buď řádky dostanou nebo ne.

Pokud bychom chtěli vypsat jenom ty koleje, kde je více než jedna dívka, použili bychom následující select.

Pamatujte si: Pokud chceme filtrovat výstupní řádky seskupené tabulky na základě podmínek obsahující výstup z agregační funkce, musíme použít klíčové slovo HAVING.

Další příkladem může být select, který vypisuje pouze ty učitele, kteří učí více jak jeden předmět. V tomto příkladě je zároveň ukázáno, že za GROUP BY nemusíme použít název sloupečku, ale můžeme zde napsat přímo název agregační funkce určující počet předmětů na jednoho učitele.

Subselecty

Kromě seskupování se v SQL poměrně často pracuje také s tzv. subselecty - neboli podselecty. Subselect je obyčejný select, který se provádí pro každou řádku výsledné tabulky. V subselectech je důležité, aby byla výsledkem pouze jedna konkrétní hodnota - pouze jeden řádek a jeden sloupec. Většinou se jedná o výsledek funkce COUNT, MIN nebo MAX. Subselecty se mohou objevit na dvou místech - v seznamu zobrazovaných sloupců nebo v podmínce za WHERE nebo HAVING.

Subselect jako nový sloupec

Ukážeme si stejný příklad jako jsme dělali pomocí seskupování. Spočteme si, jaká je obsazenost kolejí, ale tentokrát to uděláme pomocí subselectu.

Jak je vidět, ke každé řádce z tabulky kolej se provede subselect, který pro každou řádku spočítá, kolik studentů náleží konkrétní koleji.

Subselect v podmínce WHERE

Subselect můžeme použít také v podmínce WHERE, kde se pro každou řádku spočítá konkrétní hodnota (třeba počet studentů) a do výsledné tabulky se dostanou pouze řádky, které danou podmínku splňují. Můžeme například vypsat jenom ty koleje, kde je obsazenost větší než 2

Subselecty můžeme samozřejmě kombinovat a uvádět je jak v seznamu sloupců tak i v podmínce WHERE.

Seznam učitelů vyučující více jak jeden předmět můžeme pomocí subselectu získat takto.

DML - Data Manipulation Language

Do této doby jsme se zabývali pouze vyhledáváním v datech. Nyní si ukážeme, jak můžeme data vytvářet, upravovat a mazat v rámci existujících tabulek. K tomu slouží příkazy INSERT, UPDATE a DELETE jazyka DML (data manipulation language).

Pamatujte si: DML pracuje s daty v již vytvořených tabulkách. Pro vyváření a mazání tabulek se používají příkazy jazyka DDL, který se ukážeme později.

Vytváření záznamů

Pro vkládání řádků do tabulek se používá příkaz INSERT, který má následující podobu. Nejdříve se uvede název tabulky, do které chceme data vložit, společně se seznamem sloupečků, pro které pak definujeme ve stejném pořadí konkrétní data. Následující příkaz vloží do tabulky kolej jednu novou řádku s ID=5 a názvem Brumbálov. Přesvědčte se o tom selectem pro zobrazení všech kolejí.

Pamatujte si: příkaz INSERT vkládá jednu řádku do jedné tabulky.

Úprava záznamů

Pro úpravu řádků v tabulce se používá příkaz UPDATE, který má následující podobu. Nejdříve se uvede název tabulky, pak seznam sloupců s novými hodnotami (můžeme změnit víc sloupců naráz - oddělíme je čárkami) a nakonec seznam řádků, kterých se změna týká, definovaných pomocí podmínky WHERE. Následující příkaz přejmenuje v tabulce kolej řádku Havraspár (ID=4) na Vránospár. Přesvědčte se o tom selectem pro zobrazení všech kolejí.

Jako další příklad si ukážeme trošku komplexnější update, který změní jména všech studentek na Brenda a před příjmení přidá slovo slečna. To, že se jedná o studentky zajistíme v podmínce WHERE pomocí subselectu, který vyhledá id pohlaví, které má název žena.

Pamatujte si: příkaz UPDATE mění všechny řádky, které jsou uvedené v podmínce WHERE. Pokud podmínku WHERE neuvedete, změna se bude týkat všech řádků! Takže opatrně.

Smazání záznamů

Pro smazání řádků v tabulce se používá příkaz DELETE, který má následující podobu. Nejdříve se uvede název tabulky a pak seznam řádků, které chceme smazat, definovaný pomocí podmínky WHERE. Následující příkaz smaže z tabulky predmet řádku odpovídající Astronomii (ID=8). Přesvědčte se o tom selectem pro zobrazení všech předmětů.

Nebo stejného cíle bychom dosáhli následovně

I v příkazech delete můžeme používat složitější konstrukce - například subselecty. Následující příkaz smaže všechny předměty, na které není zapsaný žádný student a nevyučuje je žádný učitel.

Pamatujte si: příkaz DELETE maže všechny řádky, které jsou uvedené v podmínce WHERE. Pokud podmínku WHERE neuvedete, příkaz smaže celou tabulku! Takže opatrně.
Pamatujte si: U mazání si je potřeba uvědomit, že odstraněním řádku můžeme přerušit vazby mezi tabulkami. Pokud na mazaný záznam existuje odkaz z jiné tabulky, tato vazba se smazáním rozbije. Správně navržené databázové schéma používá mechanizmu nazvaném referenční integrita, který nám nepovolí smazat řádek, pokud na něj odkazuje jiná tabulka. To zajistí, že je naše databáze neustále v konzistentním stavu. Nastavení referenční integrity je ale za hranou obsahu našeho studia.

Zkuste například smazat učitele Brumbála z tabulky ucitel.

Když si nyní vylistujete seznam učitelů, zjistíte, že učitel s ID=2 v tabulce již není, ale v tabulce predmety na něj stále odkazuje předmět Přeměňování.

DDl - Data Definition Language

DDL (jazyk pro definování dat) definuje příkazy pro vytváření a mazání tabulek. Detaily v tomto kurzu nebudeme nijak rozebírat, pouze si ukážeme příklad pomocí kterého lze vytvořit pár tabulek našeho schématu z Bradavic.

V případě, že chceme nějakou tabulku odstranit, můžeme použít příkaz DROP.

Tady zakončíme teorii ohledně databází. Pokud rozumíte výše uvedenému textu, máte již velmi pokročilé znalosti ohledně databází a jazyka SQL. Nasbírané vědomosti se pokusíme zužitkovat v následujících příkladech.

TŘÍDNÍ DISKUZNÍ FÓRUM

Tento příklad funguje na sdílené serverové databázi, takže řádky, které vloží jeden student uvidí celá třída. Můžete komentáře vkládat, mazat a zároveň si je lajkovat. Vyzkoušejte si svoje znalosti na následující stránce Třídní chatovací místnost