Relační databáze je asi nejobvyklejší databázový model a patří mezi vůbec nejpoužívanější způsoby, jak strukturovaně ukládat data. Databáze obsahuje tabulky, ty mají každá nějaký pevný počet sloupců a libovolné množství záznamů, kterým pochopitelně říkáme řádky. Každý sloupec má pevně určený název a datový typ. Data jsou pouze v řádcích; tabulky a jejich sloupce nazýváme strukturou databáze a za běhu programu je neměníme.
Primární klíč je sloupec, podle jehož hodnoty můžeme jednoznačně určit každý řádek tabulky (hodnoty se tedy nesmí opakovat). Je dobrým zvykem, aby primární klíč byl celočíselného typu. Kromě toho je stavěný na to, aby se podle něj rychle vyhledávalo: odborněji řečeno, je na něm zavedený index. Mimochodem, index můžeme podle úsudku zavést i na dalších sloupcích, pokud čekáme, že se podle nich bude často vyhledávat; zabere to jen trochu místa na disku navíc.
Když chceme v databázi zaznamenat nějaké složitější vztahy (tj. skoro vždycky), necháme řádky, aby se na sebe vzájemně odkazovaly (proto se tomu asi říká relační databáze).
Jednodušší varianta je vztah one-to-many. Příklad si vezmeme rovnou z následujícího úkolu: mějme tabulky Hráč
a Tah
. Každý tah byl vykonán právě jedním hráčem, ale jeden hráč mohl udělat spoustu tahů. Tabulka Tah
bude mít cizí klíč Tah.id_hrac
– sloupec, který se odkazuje do tabulky Hráč
na její primární klíč Hrac.id_hrac
. U každého řádku tabulky Tah
musí být ve sloupci id_hrac
vyplněné nějaké číslo, které se vyskytuje v některém řádku tabulky Hráč
ve sloupci id_hrac
.
Vztah many-to-many nastane třeba v případě tabulek Hráč
a Turnaj
. Jednoho turnaje účastní několik hráčů a jeden hráč se může dle libosti účastnit několika turnajů. K tomu potřebujeme zavést jednu tabulku navíc, která bude zaznamenávat každou dvojici (hráč, turnaj), mezi kterými je vazba. Ta tabulka si vystačí prostě se dvěma sloupci: id_hrac, id_turnaj
, kterými se bude odkazovat na primární klíče obou tabulek. (Její primární klíč může být buďto nějaký další sloupec, anebo přímo dvojice sloupců id_hrac, id_turnaj
. Jde to zařídit i tak.)
Mimochodem, my na záznamy v databázi nebudeme nastavovat žádná omezení. Například by byla rozumná podmínka, že každého turnaje se účastní aspoň dva hráči; databáze si to sice dokáže zapamatovat, ale pracuje se s ní pak trochu složitěji a proto je většinou lepší se o integritu dat starat ručně.
Jako databázový software znám MySQL (běží na GJS, od firmy Oracle), PostgreSQL (vyvíjený původně na univerzitě v Berkeley) a MS SQL (od firmy Microsoft). Liší se v názvech některých funkcí a zápisem některých složitějších dotazů; ne všechny umí totéž. Existuje jakýsi obecný standard jazyka SQL, který ale dává jistou míru volnosti a i nad tu míru ho všichni porušují. Databázový hardware je obyčejný počítač s rychlým diskem a rychlým připojením na síť.
Alternativou k relační databázi je třeba model jazyka XML, který udržuje všechna data v jednom velkém stromu. Pro některé účely se hodí (zvlášť kvůli kompatibilitě, data se tak dají snadno přenášet), ale špatně se v něm vyhledává a pro větší objemy dat je nevhodný.
Možných řešení je určitě víc, asi je i víc možných výkladů zadání. Tady nabízím jedno řešení, které považuju za správné.
TabulkaHrac
obsahuje záznamy o všech hráčích ve hře:
id_hrac
typu INT
, primární klíč;jmeno
typu TEXT
.Hra
obsahuje záznamy o všech hrách, které kdy proběhly:
id_hra
typu INT
, primární klíč;id_turnaj
typu INT
, cizí klíč na sloupec Turnaj.id_turnaj, může být NULL (pokud hra nebyla v rámci žádného turnaje);hrac_kolecko
typu INT
, cizí klíč na sloupec Hrac.id_hrac, odkazuje na hráče, co měl kolečka;hrac_krizek
typu INT
, cizí klíč na sloupec Hrac.id_hrac, odkazuje na hráče, co měl křížky;vitez
typu ENUM('KOLECKO', 'KRIZEK', 'NEZNAME')
, udávající, kdo tuto hru vyhrál.Tah
obsahuje záznamy o všech tazích v historii:
id_tah
typu INT
, primární klíč;id_hra
typu INT
, cizí klíč na sloupec Hra.id_hra;hrac
typu ENUM('KOLECKO', 'KRIZEK')
, udávající, který hráč tento tah vykonal;poradi
typu INT
, udávající pořadí tahu v rámci hry (počítaje od 1 tahy obou hráčů)pozice_x
typu INT
, na tomto sloupci je vedený index;pozice_y
typu INT
, na tomto sloupci je vedený index.Turnaj
obsahuje záznamy o turnajích:
id_turnaj
typu INT
, primární klíč;zacatek
typu DATETIME
, udávající datum začátku turnaje;vitez
typu INT
, cizí klíč na sloupec Hrac.id_hrac, musí to být jeden z účastníků turnaje, může být NULL (pokud vítěz není známý).Hrac_in_turnaj
zaznamenává účast hráčů v turnajích:
id_hrac_in_turnaj
typu INT
, primární klíč;id_turnaj
typu INT
, cizí klíč na sloupec Turnaj.id_turnaj, na tomto sloupci je vedený index;id_hrac
typu INT
, cizí klíč na sloupec Hrac.id_hrac, na tomto sloupci je vedený index.Redundance znamená, že nějakou informaci ukládáme víckrát. Tady jsme ukládali vítěze hry, přestože se dá odvodit z tahů (najít tam pět piškvorek v řadě) a vítěze turnaje, přestože se dá odvodit z vítězů příslušných her (herní systém nemáme pevně daný, ale nějaký je). Určitě tím ušetříme výpočetní výkon: třeba když si někdo zobrazí statistiky turnaje, nemusíme procházet tahy všech her, ale vítěze stačí z tabulky přečíst.
Stručněji můžeme návrh databáze zakreslit UML diagramem. Ten se ale dá použít i jinak než pro relační databáze, takže vztahy zakresluje obecnějším způsobem a primární klíče neobsahuje: