Hlavní obsah
Internet, technologie a elektronika

Pod kapotou relačních databází: proč Pepa ve skladu nenašel váš displej

Foto: Claude Opus

Systém hlásí 3 kusy na regálu C-14. Regál je prázdný.

Databáze e-shopu tvrdila, že displeje jsou na skladě. Regál byl prázdný. Proč si data neodporovala, a přesto lhala? Průvodce integritou, klíči a transakcemi od Codda až po ACID.

Článek

Explanatory článek pro Medium — série „Pod kapotou“

Pondělní ráno, sklad e-shopu v Horních Počernicích. Pepa dostane výdejku: displej DD-2742, jeden kus, objednávka č. 8841. Jde k regálu C-14, kde by podle systému měly ležet tři kusy. Regál je prázdný.

Pepa zavolá na helpdesk. Operátorka otevře skladovou evidenci — displej DD-2742 tam svítí, tři kusy, lokace C-14, stav „dostupný“. Systém hlásí, že je všechno v pořádku. Všechny objednávky mají přiřazené produkty, produkty mají platné skladové záznamy, zásoby jsou kladné. Z pohledu databáze neexistuje žádný problém.

Jenže Pepa stojí před prázdným regálem.

Co se stalo? Možností je řada — noční směna přesunula paletu a nezapsala to, dodávka minulý týden přišla kratší a nikdo nesrovnal příjemku, nebo někdo jednoduše ukradl tři displeje. Na příčině teď nezáleží. Důležité je něco jiného: databáze měla pravdu sama v sobě, ale neměla pravdu o světě.

Tohle je zásadní rozlišení, které většina vývojářů přehlíží — a které stojí za celou architekturou relačních databází. Relační model nabízí mocné nástroje, jak zajistit, aby si data neodporovala. Ale žádné omezení na světě nepozná, že Pepa stojí před prázdným regálem.

Tento článek je o tom, jak tyto nástroje fungují, proč vznikly a kde končí jejich moc. Projdeme si cestu od prázdného regálu přes klíče, vazby a integritní omezení až k transakčnímu modelu ACID — a pochopíme, proč víc než půl století stará technologie přežila všechny své vyzyvatele.

Dvě integrity: vnitřní a vnější

Začněme u Pepova problému. Databáze e-shopu splňovala všechna svá vlastní pravidla. Primární klíče byly unikátní. Cizí klíče ukazovaly na existující záznamy. Množství na skladě bylo kladné číslo. Objednávka odkazovala na platný produkt, produkt odkazoval na platnou skladovou pozici. Data si navzájem neodporovala.

V databázové teorii se tomuhle říká integrita — stav, kdy data splňují všechna deklarovaná omezení. Edgar F. Codd, matematik z IBM, který relační model v roce 1970 navrhl, to ve svém průlomovém článku definoval přes pojem konzistence: máme kolekci relací, množinu omezení a aktuální stav dat. Pokud data splňují všechna omezení, stav je konzistentní; pokud ne, je nekonzistentní.

Ale Pepův problém není o tom, že by si data odporovala. Data si odpovídají výborně. Problém je v tom, že neodpovídají realitě.

Databázová teorie toto rozlišení formálně nepojmenovává — nenajdete v učebnicích kapitolu „vnitřní vs. vnější integrita“. Ale implicitně je přítomno všude. A Dictionary of Computing definuje integritu databáze jako stav, kdy data jsou korektní ve dvou smyslech: zaprvé „reflecting the state of the real world“ a zadruhé „obeying rules of mutual consistency“. Dva odlišné smysly, dvě odlišné záruky.

Vnitřní integrita je o tom, že data dodržují svá vlastní pravidla. Databázový engine ji umí vynucovat sám — pomocí klíčů, omezení, transakcí. Pokud se pokusíte vložit objednávku s neexistujícím ID zákazníka, databáze odmítne.

Vnější integrita je o tom, že data odpovídají skutečnosti. Zákazník se opravdu jmenuje Jan Novák. Na regálu C-14 opravdu leží tři displeje. Objednávka opravdu proběhla v pondělí. Tohle žádný databázový engine neumí ověřit.

Filozofie má pro tento rozdíl přesné termíny: koherence (data si neodporují — vnitřní soulad) versus korespondence (data odpovídají realitě — shoda se světem). Databáze garantuje koherenci. Korespondenci musí zajistit člověk, proces, validace na vstupu — a jednou za čas inventura.

A celý relační model je v jádru pokus tu propast mezi koherencí a korespondencí minimalizovat. Čím méně míst v databázi obsahuje stejnou informaci, tím méně míst se může rozsynchronizovat se skutečností. Jestli máte adresu zákazníka na jednom místě, stačí ji opravit jednou. Jestli je redundantně ve třech tabulkách, vnější integrita se rozpadá třikrát rychleji.

Ale nepředbíhejme. Abychom pochopili, jak relační model koherenci vynucuje, musíme začít od základů.

Tabulky, řádky, sloupce — ale jinak, než si myslíte

Když řeknete „databázová tabulka“, většina lidí si představí excelový list. Řádky, sloupce, buňky. Vizuálně to sedí, ale koncepčně je to zavádějící — a rozdíl má praktické důsledky.

Codd ve svém článku z roku 1970 nepoužil slovo „tabulka“. Použil slovo relace — matematický pojem z teorie množin. Relace je množina n-tic (řádků) nad doménami (množinami povolených hodnot). A protože je to množina, platí dvě zásadní vlastnosti, které Excel nemá:

Za prvé: žádné duplicity.Množina z definice neobsahuje dva stejné prvky. V relaci nemohou existovat dva identické řádky. Každý řádek musí být jednoznačně odlišitelný od všech ostatních. Tohle je důvod, proč existují primární klíče — ale k těm se dostaneme.

Za druhé: pořadí nemá význam.Prvky množiny nemají pořadí. Řádky v relaci nemají „pátý řádek“ ani „poslední řádek“. Pokud váš kód závisí na pořadí řádků v tabulce, stojí na vratkých základech.

Terminologie relačního modelu je přesná a stojí za to ji znát, i když ji v praxi málokdo používá. Tomu, čemu říkáme tabulka, Codd říkal relace. Sloupec je atribut. Řádek je n-tice (tuple). Množina povolených hodnot pro sloupec — třeba „kladné celé číslo“ nebo „datum, ne budoucí“ — je doména. Definice sloupců a jejich typů tvoří schéma (struktura), aktuální obsah tabulky je instance (data k tomuto okamžiku). Počet sloupců se nazývá arita, počet řádků kardinalita.

A pak je tu NULL — hodnota, o které se vedou nejdelší debaty v historii databázové teorie. NULL neznamená nulu. NULL neznamená prázdný řetězec. NULL znamená „nevíme“ nebo „neaplikovatelné“. Zákazník bez telefonního čísla nemá telefon 0 — prostě nevíme, jaký má telefon, nebo žádný nemá. Codd sám později navrhl rozlišovat dva druhy NULL (neznámé vs. neaplikovatelné), ale žádný velký databázový systém to neimplementoval.

Entity: co vlastně modelujeme

Databáze neexistuje ve vzduchoprázdnu. Modeluje kousek reality — to, čemu se v teorii říká minisvět(mini-world nebo universe of discourse). E-shop modeluje zákazníky, produkty, objednávky, sklady. Nemocnice modeluje pacienty, lékaře, diagnózy, předpisy. Banka modeluje účty, transakce, klienty.

Každý rozpoznatelný objekt nebo koncept v tomto minisvětě je entita. Zákazník Jan Novák je entita. Objednávka č. 8841 je entita. Displej DD-2742 je entita.

Entity téhož druhu sdílejí vlastnosti — atributy. Každý zákazník má jméno, e-mail, adresu. Každý produkt má kód, název, cenu, hmotnost.

Některé entity existují samostatně — zákazník existuje, i když nemá žádnou objednávku. To je silná entita. Jiné entity nedávají smysl bez entity, na které závisejí — položka objednávky nedává smysl bez objednávky. To je slabá entita; nemá vlastní jednoznačný identifikátor a identifikuje se kombinací svého parciálního klíče a klíče nadřazené entity.

Pro vizualizaci těchto vztahů vznikly ER diagramy (Entity-Relationship), které navrhl Peter Chen v roce 1976. Dnes se nejčastěji kreslí v notaci vraních noh(crow’s foot notation), kde symboly na koncích spojnic vyjadřují, kolik entit se účastní vztahu:

Čára (|) znamená „právě jeden“. Vidlička, která vypadá jako vraní noha (⊳), znamená „mnoho“. Kolečko (○) před symbolem znamená „volitelně — může být nula“. Čárka (|) před symbolem znamená „povinně — musí být aspoň jeden“. Kombinací vznikají čtyři základní varianty: právě jeden povinný, nanejvýš jeden volitelný, jeden nebo více povinných, nula nebo více volitelných.

Klíče: jak se řádky poznají

Vraťme se k Pepovu displeji. Proč systém ví, že objednávka 8841 se týká právě produktu DD-2742, a ne jiného displeje? Protože oba záznamy mají jednoznačné identifikátory — klíče — a jeden na druhý odkazuje.

Klíče jsou nejdůležitější koncept relačního modelu. Bez nich by tabulka byla jen hromada dat bez struktury — jako telefonní seznam bez jmen.

Superklíč je jakákoli množina sloupců, která jednoznačně identifikuje řádek. V tabulce zákazníků je superklíčem kombinace {id, jméno, email}, ale taky {id, jméno} nebo jen {id} — pokud je id unikátní.

Kandidátní klíč je minimálnísuperklíč — takový, ze kterého nemůžete odebrat žádný sloupec, aniž by přestal být unikátní. V tabulce zákazníků může být kandidátním klíčem id i email — oba jednoznačně identifikují zákazníka, oba jsou minimální.

Primární klíč (PK) je kandidátní klíč, který zvolíte jako hlavní identifikátor. Výběr je pragmatický: primární klíč nesmí být NULL (to je pravidlo entitní integrity, o kterém budeme mluvit) a měl by být stabilní — neměnit se v čase.

Alternativní klíč je každý kandidátní klíč, který nebyl zvolen jako primární. E-mail zákazníka může být alternativní klíč — unikátní, ale nepraktický jako primární (lidi mění e-maily).

A teď to klíčové: cizí klíč (FK) je sloupec (nebo skupina sloupců) v jedné tabulce, který odkazuje na primární klíč jiné tabulky. Objednávka má sloupec zakaznik_id, který odkazuje na idv tabulce zákazníků. Položka objednávky má objednavka_idodkazující na id objednávky a produkt_id odkazující na idproduktu.

Cizí klíče jsou lepidlo, které drží relační databázi pohromadě. Bez nich byste měli hromadu nesouvisejících tabulek — jako kartotéku, kde někdo odstranil všechny odkazy mezi kartami.

V praxi se často setkáte s debatou přirozený vs. surrogate klíč. Přirozený klíč je hodnota z reálného světa — rodné číslo, ISBN knihy, IČO firmy. Surrogate klíč je umělý identifikátor vygenerovaný databází — typicky auto-increment integer nebo UUID. Přirozené klíče mají kouzlo srozumitelnosti, ale v praxi se mění (lidi mění rodná čísla po změně pohlaví, firmy zanikají a jejich IČO s nimi), můžou mít komplikovaný formát a bývají delší. Proto většina moderních systémů používá surrogate klíče a přirozené klíče vyžaduje jen jako alternativní — unikátní, ale ne primární.

Vazby: jak spolu entity souvisejí

Klíče nám umožňují propojit entity. Ale jak? Vztahy mezi entitami mají různou kardinalitu— a ta zásadně ovlivňuje, jak databázi navrhneme.

Jeden k jednomu (1:1) — občan má jeden občanský průkaz, občanský průkaz patří jednomu občanu. V praxi vzácné. Typicky vzniká, když chcete oddělit zřídka používané sloupce do vlastní tabulky (vertikální partitioning) nebo když máte bezpečnostní důvod — citlivé údaje v oddělené tabulce s přísnějšími právy.

Jeden k mnoha (1:N) — nejběžnější vztah. Zákazník má mnoho objednávek, ale každá objednávka patří jednomu zákazníkovi. Implementace je prostá: tabulka na straně „mnoha“ (objednávky) obsahuje cizí klíč odkazující na primární klíč tabulky na straně „jednoho“ (zákazníci).

Mnoho k mnoha (N:M) — student navštěvuje mnoho předmětů, předmět navštěvuje mnoho studentů. Takhle to vypadá na první pohled. Ale N:M vztah je vždy signál nedokončené analýzy.

Proč? Protože mezi těmi dvěma entitami se vždy skrývá třetí — entita, která ten vztah zprostředkovává a nese vlastní informaci. Student a předmět nejsou propojeni přímo. Propojuje je zápis — konkrétní událost, která se odehrála v konkrétním čase, v konkrétním semestru, a která má vlastní atributy: datum zápisu, známku, počet pokusů. Objednávka a produkt nejsou propojeny přímo. Propojuje je položka objednávky — s množstvím, cenou v okamžiku nákupu, slevou.

N:M vztah v konceptuálním modelu je iluze — vypadá jako přímá vazba, ale ve skutečnosti maskuje entitu, kterou analytik přehlédl. Když ji najdete, vždy se rozpadne na dvě vazby 1:N. A ta „spojovací tabulka“ přestane být technickým detailem — stane se plnohodnotnou entitou s vlastním životním cyklem a vlastním významem. Kdo si ji při návrhu neuvědomil, ten nepochopil doménu.

Vztahy mají i rozměr povinnosti účasti. Musí mít objednávka zákazníka? Ano — objednávka bez zákazníka nedává smysl (mandatory). Musí mít zákazník objednávku? Ne — zákazník se může zaregistrovat a nikdy nic neobjednat (optional). V notaci vraních noh se to vyjadřuje kolečkem (volitelné) nebo čárkou (povinné) na příslušném konci spojnice.

A existují i rekurzivní vazby — entita odkazuje sama na sebe. Typický příklad: zaměstnanec má nadřízeného, nadřízený je také zaměstnanec. Tabulka zamestnancimá sloupec nadrizeny_id, který je cizím klíčem odkazujícím zpět na idtéže tabulky. Generální ředitel má nadrizeny_id = NULL — nemá nadřízeného.

Integritní omezení: strážci koherence

Teď se dostáváme k jádru. Klíče a vazby by byly k ničemu, kdyby je databáze nevynucovala. K čemu je cizí klíč, pokud můžete do sloupce zakaznik_idzapsat číslo 99999, přestože zákazník 99999 neexistuje?

Právě proto existují integritní omezení (integrity constraints) — pravidla, která databáze kontroluje při každé změně dat. Pokud změna pravidlo poruší, databáze ji odmítne.

Codd ve svých 12 pravidlech z roku 1985 (pravidlo č. 10 — Integrity Independence) stanovil, že integritní omezení musí být definována v databázi samotné, ne v aplikačním kódu. Důvod je pragmatický: aplikací může být mnoho, databáze je jedna. Pokud integritní pravidlo existuje jen v kódu e-shopu, ale ne v databázi, každá další aplikace přistupující k těm samým datům — reportovací nástroj, mobilní aplikace, interní admin — ho může obejít.

Relační model definuje čtyři úrovně integrity:

Entitní integrita říká, že žádná složka primárního klíče nesmí být NULL. Důvod je prostý: primární klíč identifikuje řádek. Pokud je NULL, řádek je neidentifikovatelný — jako dvě osoby bez jména a bez dokladů. Nemůžete je od sebe rozlišit a nemůžete na žádnou z nich spolehlivě odkázat.

Referenční integrita říká, že každá hodnota cizího klíče musí odpovídat existující hodnotě primárního klíče v referované tabulce — nebo být NULL (pokud je vztah volitelný). Objednávka s zakaznik_id = 42 smí existovat jen tehdy, pokud v tabulce zákazníků existuje řádek s id = 42.

Ale co se stane, když smažete zákazníka 42, který má objednávky? Databáze nabízí několik strategií:

RESTRICT — odmítne smazání okamžitě, bez ohledu na cokoli jiného. Nezkoumá, jestli by situaci zachránila jiná kaskáda. Prostě řekne „ne“.

NO ACTION — vypadá stejně, ale liší se v načasování. Kontrola proběhne po vykonání všech ostatních referenčních akcí v rámci příkazu. Pokud jiný cizí klíč mezitím problém vyřešil kaskádou, chyba nenastane. Subtilní rozdíl — ale v komplexních schématech se složitými kaskádami zásadní.

CASCADE — smazání zákazníka automaticky smaže všechny jeho objednávky. Elegantní, ale nebezpečné — jedno DELETE může spustit řetěz mazání napříč desítkami tabulek.

SET NULL — cizí klíč v objednávkách se nastaví na NULL. Objednávka zůstane, ale ztratí vazbu na zákazníka.

V učebnicích se těmto strategiím věnuje hodně prostoru. V praxi je situace jiná: v informačních systémech se data nemažou.

Ne proto, že by to bylo technicky nemožné. Ale proto, že data mají životní cyklus — vznikají, mění stav, zastarávají, archivují se. Zákazník nezmizí, když zruší účet. Objednávka nezmizí, když je stornovaná. Zaměstnanec nezmizí, když odejde z firmy. Všechny tyto záznamy mají právní, účetní a analytickou hodnotu i po „smazání“. Daňové doklady se musí archivovat řadu let. Auditní stopa musí být rekonstruovatelná.

Místo fyzického mazání se používá soft delete — příznak smazano = TRUE, případně datum_smazania smazal_uzivatel. Záznam zůstává v databázi, jen se neobjevuje v běžných dotazech. Pohledy (views) filtrují smazané záznamy automaticky, takže aplikace pracuje, jako by neexistovaly — ale auditoři, právníci a analytici se k nim dostanou.

Tím se celá debata o ON DELETE CASCADE stává akademickou. Pokud nikdy nevoláte DELETE, nepotřebujete kaskády. RESTRICT jako pojistka — ano. CASCADE jako design — v produkčním informačním systému téměř nikdy.

Doménová integrita omezuje, jaké hodnoty smí sloupec obsahovat. Cena nesmí být záporná. E-mail musí obsahovat zavináč. Datum narození nesmí být v budoucnosti. V SQL se vynucuje kombinací datových typů, NOT NULL, CHECK omezení a CREATE DOMAIN.

Sémantická (uživatelsky definovaná) integrita pokrývá obchodní pravidla, která přesahují jednoduchou doménovou kontrolu. Zaměstnanec nesmí schválit vlastní žádost o dovolenou. Objednávka nesmí překročit kreditní limit zákazníka. Celkový počet studentů v kurzu nesmí překročit kapacitu místnosti. Tato pravidla se v praxi vynucují kombinací CHECK omezení, triggerů a aplikační logiky.

SQL standard z roku 1992 zavedl ještě mechanismus ASSERTION — omezení na úrovni celého schématu, které může odkazovat na více tabulek najednou. Na papíře elegantní. V praxi nepoužitelné: žádný velký databázový systém — ani PostgreSQL, ani Oracle, ani SQL Server — aserce nikdy neimplementoval. Důvod je výkon: kontrola omezení, které může zahrnovat libovolné tabulky, při každém INSERT/UPDATE/DELETE by byla příliš nákladná.

Codd i C. J. Date důsledně trvali na tom, že integritní omezení mají být deklarativní — říkáte co musí platit, ne jak to zajistit. Primární klíč, cizí klíč, UNIQUE, CHECK — to jsou deklarace. Databáze sama rozhodne, jak je efektivně kontrolovat. Deklarativní omezení je samopopisné — vidíte ho ve schématu, je zdokumentované svou existencí.

Proti tomu stojí procedurální integrita — triggery a uložené procedury, kde píšete kód, který se spustí při určité události. A tady je na místě varování, které v učebnicích často chybí: obchodní logika postavená na triggerech je cesta do pekel.

Proč? Triggery jsou neviditelné. Nevidíte je v SQL dotazu. Nevidíte je ve schématu, pokud se cíleně nepodíváte. Spouštějí se implicitně — vložíte řádek a někde na pozadí se změní data v jiné tabulce, odešle se notifikace, aktualizuje se agregát. Triggery se mohou řetězit — trigger A vyvolá změnu, která spustí trigger B, který spustí trigger C. Ladění takového řetězu je noční můra. Výkon je nepředvídatelný. A hlavně: nový člen týmu, který otevře schéma databáze, o existenci triggerů neví, dokud ho něco nepřekvapí.

Triggery mají své oprávněné místo — auditní logy, automatické timestampy, jednoduchá denormalizace pro výkon. Ale kdykoli máte chuť implementovat obchodní pravidlo jako trigger, zastavte se a zkuste ho nejdřív vyjádřit deklarativně. CHECK, UNIQUE, FK — co jde vyjádřit v DDL, patří do DDL.

Normalizace: jeden fakt, jedno místo

Vraťme se k Pepovu skladu. Řekli jsme, že čím méně míst v databázi obsahuje stejnou informaci, tím méně míst se může rozsynchronizovat se skutečností. Tohle je esence normalizace — procesu rozkladu tabulek tak, aby každý fakt byl uložen právě jednou.

Normalizaci zavedl sám Codd v roce 1971, rok po svém průlomovém článku. Cíl formuloval takto: osvobodit kolekci relací od nežádoucích závislostí při vkládání, aktualizaci a mazání dat.

Nežádoucí závislosti vedou k anomáliím — a ty jsou konkrétní a bolestivé:

Update anomálie. Představte si tabulku, kde je u každé objednávky uložena i adresa zákazníka. Zákazník se přestěhuje. Má 47 objednávek. Musíte aktualizovat adresu ve 47 řádcích. Aktualizujete 46. V jednom řádku zůstane stará adresa. Databáze neprotestuje — pravidla nejsou porušena. Ale data si teď odporují. Vnitřní integrita je porušena, přestože žádné omezení nebylo formálně narušeno. Tohle je případ, kdy koherence selhává ne kvůli chybějícím omezením, ale kvůli špatnému návrhu.

Insert anomálie. Chcete zaznamenat nového pedagoga na fakultě, ale nemáte pro něj přidělený předmět. V tabulce se složeným klíčem (pedagog_id, předmět_id) ho nemůžete vložit — součást primárního klíče by byla NULL, což porušuje entitní integritu. Nemůžete zaznamenat fakt, který existuje, protože schéma to neumožňuje.

Delete anomálie. Zaměstnanec 103 je jediná osoba přiřazená k projektu „Arktida“. Smažete zaměstnance 103 (odešel z firmy) a tím ztrácíte i informaci, že projekt „Arktida“ vůbec existoval.

Všechny tři anomálie mají společnou příčinu: v jedné tabulce jsou smíchané fakty, které spolu nesouvisejí dost těsně. Normalizace je rozplétá.

Základ normalizace je pojem funkční závislost. Atribut B funkčně závisí na atributu A, pokud znalost hodnoty A jednoznačně určuje hodnotu B. Rodné číslo → jméno (každé rodné číslo patří jednomu člověku). ISBN → název knihy (každé ISBN identifikuje jednu knihu). Formálně: pro každé dvě n-tice, pokud se shodují v hodnotě A, musejí se shodovat i v hodnotě B.

První normální forma (1NF)vyžaduje, aby všechny hodnoty atributů byly atomické — žádné seznamy, žádné vnořené tabulky, žádné opakující se skupiny. Sloupec „telefony“ s hodnotou „603111222, 604333444“ porušuje 1NF. Řešení: samostatná tabulka telefonů.

Druhá normální forma (2NF)přidává požadavek, aby každý neklíčový atribut závisel na celém primárním klíči, ne jen na jeho části. Relevantní jen u složených klíčů. Pokud máte tabulku zápisů s klíčem (student_id, předmět_id) a sloupcem „jméno_studenta,“ jméno závisí jen na student_id — parciální závislost. Řešení: jméno patří do tabulky studentů.

Třetí normální forma (3NF)přidává požadavek, aby žádný neklíčový atribut nezávisel na jiném neklíčovém atributu. Pokud máte v tabulce zaměstnanců sloupce „oddělení_id“ a „jméno_vedoucího_oddělení,“ jméno vedoucího závisí na oddělení, ne přímo na zaměstnanci — tranzitivní závislost. Řešení: jméno vedoucího patří do tabulky oddělení.

Populární shrnutí zní: Každý neklíčový atribut musí poskytnout fakt o klíči, celém klíči a ničem jiném než o klíči — so help me Codd. Výrok se obvykle připisuje Billu Kentovi, i když jeho přesný původ je sporný.

Existují i vyšší normální formy — BCNF (zpřísněná 3NF pro případy s překrývajícími se kandidátními klíči), 4NF (eliminace vícehodnotových závislostí) a 5NF (eliminace join závislostí). V praxi většina systémů cílí na 3NF nebo BCNF a dál nejde.

A někdy se záměrně jde zpět. Denormalizace — vědomé zavedení redundance kvůli výkonu — je legitimní technika v systémech, kde se data čtou mnohonásobně častěji, než se zapisují. Datové sklady, analytické databáze, vyrovnávací vrstvy. Ale denormalizace je dohoda: získáváte rychlost čtení, platíte rizikem nekonzistence při zápisu. Musíte vědět, co děláte.

Jazyk, kterým se s databází mluví

Máme tabulky, klíče, omezení, normalizované schéma. Jak z toho ale dostat data ven?

Codd nenavrhoval jen strukturu dat. Navrhl i způsob, jak s nimi pracovat — relační algebru, sadu formálních operací nad relacemi. Klíčové z nich:

Selekce (σ) — výběr řádků, které splňují podmínku. „Dej mi všechny objednávky z ledna.“ V SQL: klauzule WHERE.

Projekce (π) — výběr sloupců. „Zajímá mě jen jméno a e-mail zákazníka, ne adresa.“ V SQL: výčet sloupců za SELECT.

Spojení (⋈) — propojení dvou tabulek přes společný atribut. „Ke každé objednávce připoj jméno zákazníka.“ V SQL: JOIN. Existuje v několika variantách — INNER JOIN vrátí jen řádky s odpovídajícím protějškem v obou tabulkách, LEFT JOIN zachová všechny řádky z levé tabulky i bez protějšku, CROSS JOIN vytvoří kartézský součin.

Sjednocení, průnik, rozdíl — množinové operace nad dvěma relacemi se stejným schématem. V SQL: UNION, INTERSECT, EXCEPT.

Na těchto operacích stojí SQL— jazyk, který Coddovy abstraktní operace převádí do prakticky použitelné syntaxe. SQL je jazyk čtvrté generace (4GL). Pro srovnání: první generace je strojový kód, druhá assembler, třetí jazyky jako C, Pascal nebo Java — říkáte jakpostupovat krok za krokem. Čtvrtá generace dělá kvalitativní skok: říkáte co chcete, ne jak to najít. Neříkáte „projdi index B-stromu, přeskoč na list 47, přečti ukazatel.“ Říkáte „dej mi jména zákazníků, kteří objednali víc než třikrát za poslední měsíc.“ Databáze sama rozhodne, jakou strategii zvolit.

Tohle oddělení záměru od implementace — deklarativní přístup čtvrté generace — je jeden z klíčových důvodů, proč relační model přežil tolik technologických epoch. SQL dotaz napsaný v roce 1990 funguje na hardwaru z roku 2026 — databáze ho jen provede efektivněji.

K SQL patří i dva mocné nástroje pro složitější dotazy: poddotazy (vnořené SELECTy — dotaz uvnitř dotazu) a CTE (Common Table Expressions — pojmenované dočasné výsledky v klauzuli WITH, které zpřehledňují složité dotazy a umožňují rekurzi).

A konečně agregace: operace, které z mnoha řádků vypočítají jedno číslo. COUNT, SUM, AVG, MIN, MAXv kombinaci s GROUP BY (seskupení) a HAVING (filtr nad skupinami). Kolik objednávek má každý zákazník? Jaký je průměrný obrat na prodejnu? Agregace nebyly součástí původní relační algebry — přidalo je až SQL. Ale právě ony dělají z databáze analytický nástroj, ne jen úložiště.

Máme teď všechny stavební kameny: tabulky, klíče, vazby, omezení, normalizaci a jazyk pro dotazování. Zbývá poslední zásadní otázka: co se stane, když se dvě operace potkají ve dveřích?

ACID: čtyři písmena, která drží svět pohromadě

Představte si bankovní převod: 10 000 korun z účtu A na účet B. Dva SQL příkazy: odečíst z A, přičíst k B. Co když systém spadne mezi nimi? Na účtu A ubylo, na účtu B nepřibylo. Deset tisíc korun zmizelo.

Tohle řeší transakce — skupina operací, která se provede buď celá, nebo vůbec. Koncept formalizoval Jim Gray v roce 1981 a o dva roky později Theo Härder a Andreas Reuter zavedli akronym ACID:

Atomicita (Atomicity): Transakce je nedělitelná. Buď se provedou všechny její operace, nebo žádná. Pokud cokoliv selže, všechny dosavadní změny se vrátí zpět (rollback). Bankovní převod buď proběhne celý — odečet i přičtení — nebo se nestane nic.

Konzistence (Consistency):Transakce převede databázi z jednoho platného stavu do jiného. Všechna integritní omezení, která platila před transakcí, musí platit i po ní.

Tady je důležitá nuance. Martin Kleppmann ve své knize Designing Data-Intensive Applicationsargumentuje, že „C“ do ACID vlastně nepatří — atomicitu, izolaci a trvanlivost zajišťuje databáze, ale konzistenci ve smyslu splnění obchodních pravidel zajišťuje aplikace. Databáze vám může garantovat, že cizí klíč ukazuje na existující řádek. Ale nemůže vědět, jestli je bankovní převod obchodně správný. Sám Andreas Reuter potvrdil, že C původně znamenalo, že aplikace rozhoduje o kompletnosti změn.

A pozor: „consistency“ v ACID nemá nic společného s „consistency“ v CAP teorému. Jsou to úplně odlišné pojmy se stejným jménem. CAP konzistence (linearizovatelnost) znamená, že všechny uzly distribuovaného systému vidí v každém okamžiku stejná data. ACID konzistence znamená, že integritní omezení jsou splněna po transakci. Kleppmann na to výslovně upozorňuje — tyto dva pojmy spolu nesouvisejí, i když nesou stejné jméno.

A existuje ještě třetí význam: eventuální konzistence (eventual consistency), kdy se repliky distribuovaného systému nakonec sjednotí, ale dočasně mohou ukazovat různá data. Tři totožná slova, tři zásadně odlišné koncepty. Jeden z hlavních zdrojů zmatků v celém oboru.

Izolace (Isolation): Souběžné transakce se navzájem neovlivňují — výsledek je stejný, jako kdyby běžely postupně. V praxi se izolace implementuje pomocí zamykání nebo vícevrstvých snímků (MVCC) a SQL standard definuje čtyři úrovně, od nejslabší po nejsilnější:

READ UNCOMMITTED — vidíte změny, které jiná transakce ještě nepotvrdila (dirty read). Nebezpečné, ale rychlé.

READ COMMITTED — vidíte jen potvrzené změny. Ale pokud přečtete řádek dvakrát a mezi tím ho jiná transakce změní a potvrdí, dostanete dvě různé hodnoty (non-repeatable read).

REPEATABLE READ — pokud přečtete řádek, jeho hodnota se v rámci vaší transakce nezmění. Ale jiná transakce může mezitím vložit nové řádky, které splňují vaši podmínku WHERE (phantom read).

SERIALIZABLE — úplná izolace. Výsledek je totožný se sekvenčním provedením. Nejbezpečnější, ale nejpomalejší.

Trvanlivost (Durability): Jakmile databáze potvrdí transakci (COMMIT), data přežijí i pád systému, výpadek proudu, zhroucení serveru. Implementuje se pomocí WAL (Write-Ahead Log) — před zápisem dat na disk se nejdřív zapíše záznam o změně do logu. Po restartu databáze přehraje log a obnoví stav.

Pod kapotou: jak databáze „myslí“

Integritní omezení a transakce jsou pravidla. Ale aby databáze fungovala efektivně, potřebuje i chytrou implementaci. Tři koncepty, které stojí za to znát:

Indexy jsou datové struktury pro rychlé vyhledávání — analogie rejstříku na konci knihy. Bez indexu musí databáze projít celou tabulku řádek po řádku (sekvenční sken). S indexem najde hledaný řádek v logaritmickém čase. Nejčastější implementace je B-strom (B-tree) — vyvážený strom, kde každý uzel obsahuje seřazené klíče a ukazatele na potomky. Primární klíč má index automaticky; cizí klíče obvykle také, ale ne vždy — a chybějící index na cizím klíči je jeden z nejčastějších důvodů pomalých dotazů.

Query planner (optimalizátor dotazů) rozhoduje, jak provést váš SQL dotaz. Jeden SELECT se dá vykonat desítkami způsobů — který index použít, v jakém pořadí spojovat tabulky, kdy filtrovat. Planner analyzuje statistiky o datech (počet řádků, distribuce hodnot, selektivita indexů) a vybere plán s nejnižšími odhadovanými náklady. Příkaz EXPLAIN vám ukáže, co si planner myslí — a EXPLAIN ANALYZEvám ukáže, co skutečně udělal. Rozdíl mezi nimi bývá poučný.

Pohledy (views) jsou virtuální tabulky definované dotazem. Neukládají data — při každém dotazu se přepočítají. Užitečné pro zjednodušení složitých dotazů, řízení přístupu (uživatel vidí pohled, ne zdrojové tabulky) a abstrakci nad fyzickým schématem. Materializované pohledy jdou dál — výsledek dotazu uloží a periodicky aktualizují. Kompromis mezi čerstvostí dat a rychlostí dotazu.

Proč to přežilo víc než půl století

Relační model navrhl Edgar F. Codd v roce 1970 — před víc než 55 lety. Nahradil hierarchické a síťové databáze své doby. A od té doby přišly objektové databáze, XML databáze, dokumentové databáze, grafové databáze, sloupcové databáze, vektorové databáze. Každá vlna přinesla proklamace, že relační model je mrtvý.

Přežil. Proč?

Za prvé: stojí na matematice, ne na módě. Teorie množin a predikátová logika se nemění s technologickými cykly.

Za druhé: odděluje co od jak. SQL jako jazyk čtvrté generace říká, jaká data chcete. Databáze rozhodne, jak je najít. Když se změní hardware, optimalizátor se přizpůsobí. Dotazy zůstanou stejné.

Za třetí — a tohle je to nejdůležitější — vynucuje koherenci na úrovni dat, ne na úrovni aplikace. Omezení definujete jednou, platí pro všechny. Každou aplikaci, každý nástroj, každý import. Žádná jiná databázová technologie nenabízí srovnatelnou úroveň deklarativní integrity.

A Pepa? Pepa nakonec našel displeje o dvě uličky dál. Noční směna je přesunula a nezapsala to. Vedoucí skladu zavedl pravidlo, že každý přesun musí projít čtečkou čárových kódů — validace na vstupu, ještě než se data dostanou do systému. Inventuru dělají jednou za měsíc místo jednou za kvartál.

Databáze pořád neumí poznat, kde ty displeje leží. Ale aspoň teď, když se Pepa podívá do systému a systém říká „regál C-14, tři kusy“ — je větší šance, že tam opravdu budou.

To je to nejlepší, co relační model umí: minimalizovat vzdálenost mezi tím, co databáze tvrdí, a tím, co je pravda. Ne ji eliminovat. Minimalizovat.

Zdroje:

Edgar F. Codd, „A Relational Model of Data for Large Shared Data Banks,“ Communications of the ACM, 1970. — Jim Gray, „The Transaction Concept,“ VLDB 1981. — Theo Härder & Andreas Reuter, „Principles of Transaction-Oriented Database Recovery,“ ACM Computing Surveys, 1983. — C. J. Date, Database in Depth: Relational Theory for Practitioners, O’Reilly, 2005. — Elmasri & Navathe, Fundamentals of Database Systems, 7th ed. — Martin Kleppmann, Designing Data-Intensive Applications, O’Reilly, 2017.

Metodická poznámka

Koncepce, struktura a redakční linie článku jsou dílem autora, který vypracoval obsahovou skicu, stanovil klíčové

teze a řídil celý proces tvorby. Generativní AI (Claude, Anthropic) byla využita jako technický nástroj pro rešerši,

ověřování faktů a rozepsání autorovy předlohy.

Autor výstupy průběžně redigoval, ověřil klíčová zjištění a schválil finální znění. Žádná část textu nebyla

publikována bez lidské kontroly. Všechny faktické údaje byly ověřeny proti veřejně dostupným zdrojům

uvedeným v textu.

Postup je v souladu s požadavky Čl. 50 Nařízení EU 2024/1689 (AI Act) na transparentnost AI-generovaného

obsahu. #poweredByAI

Máte na tohle téma jiný názor? Napište o něm vlastní článek.

Texty jsou tvořeny uživateli a nepodléhají procesu korektury. Pokud najdete chybu nebo nepřesnost, prosíme, pošlete nám ji na medium.chyby@firma.seznam.cz.

Související témata:
Informační systémy

Sdílejte s lidmi své příběhy

Stačí mít účet na Seznamu a můžete začít publikovat svůj obsah. To nejlepší se může zobrazit i na hlavní stránce Seznam.cz