Zpět do magazínu
Databáze16.04.2026
Databáze

Jak navrhnout databázové schéma, které tě za půl roku nezničí.

Špatně navržené schéma se ti připomene přesně ve chvíli, kdy nemáš čas. Ukážu ti pravidla, díky kterým ti databáze poroste s projektem.

PB
Petr Bláha
Autor
16. duben 2026
Publikováno
8 min
Čtení

Schéma databáze je jako základy domu. Když je odflákneš, dlouho to nepoznáš, a pak ti najednou praská zeď. Sám jsem si tím prošel: přepisování datového modelu na běžící aplikaci s živými daty je peklo. V tomhle článku ti dám pravidla, díky kterým navrhneš schéma, co růst projektu ustojí a nebude tě nutit přepisovat půlku aplikace.

Začni u toho, co aplikace dělá, ne u tabulek

Nejčastější chyba je sednout a hned kreslit tabulky. Lepší je nejdřív sepsat obyčejnými větami, co aplikace umí. Uživatel se registruje, vytvoří objednávku, objednávka má položky, položka odkazuje na produkt. Z těchhle vět ti tabulky a hlavně vztahy mezi nimi vypadnou skoro samy, protože podstatná jména se obvykle stanou tabulkami a slovesa akcemi.

Každá tabulka má primární klíč

Primární klíč je jedinečný identifikátor řádku. Doporučuju UUID nebo automaticky rostoucí číslo. Díky němu na řádek vždy spolehlivě ukážeš, i kdyby měl deset uživatelů stejné jméno. Bez primárního klíče se ti databáze rychle stane bažinou, ve které nepoznáš jeden řádek od druhého.

CREATE TABLE users (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  email TEXT NOT NULL UNIQUE,
  name TEXT NOT NULL,
  created_at TIMESTAMPTZ DEFAULT now()
);

Vztahy řeš cizími klíči, ne kopírováním

Když objednávka patří uživateli, neukládej do ní celé jméno a e-mail. Ulož jen user_id a databázi řekni, že odkazuje na tabulku users. Tomu se říká cizí klíč a hlídá ti, že nevznikne objednávka bez existujícího uživatele. Je to pojistka konzistence přímo v databázi.

CREATE TABLE orders (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
  total NUMERIC(10,2) NOT NULL,
  created_at TIMESTAMPTZ DEFAULT now()
);

To ON DELETE CASCADE znamená, že když smažeš uživatele, smažou se i jeho objednávky. Promysli si u každého vztahu, co se má při smazání stát, ať ti v databázi nezůstávají osiřelé řádky odkazující na neexistující záznam.

Normalizace, ale s rozumem

Normalizace zní akademicky, ale je to prosté pravidlo: každý údaj ukládej jen na jednom místě. Když chceš změnit název produktu, měníš ho v tabulce products, ne v tisíci objednávkách, kde byl nakopírovaný. Ušetří ti to nekonzistence, kdy je stejná věc na pěti místech pětkrát jinak.

  • Neopakuj data: jeden zdroj pravdy pro každý údaj.
  • Rozděl, co spolu logicky nesouvisí: adresa zákazníka nepatří do tabulky objednávek, pokud má zákazník adres víc.
  • Ale nepřežeň to: rozsekat všechno na deset tabulek udělá z každého dotazu závod přes pět JOINů. Hledej rovnováhu mezi čistotou a praktičností.

Typy vztahů, které potkáš

Skoro každý datový model stojí na třech typech vztahů. Když je rozeznáš, návrh ti půjde od ruky a přestaneš se u každé tabulky zamýšlet od nuly.

VztahPříkladJak na to
Jeden k jednomuUživatel a jeho profilCizí klíč v jedné z tabulek
Jeden k mnohaUživatel a jeho objednávkyCizí klíč na straně mnoha (v orders)
Mnoho k mnohaČlánky a štítkySpojovací tabulka (article_tags)
-- spojovací tabulka pro vztah mnoho k mnoha
CREATE TABLE article_tags (
  article_id UUID REFERENCES articles(id) ON DELETE CASCADE,
  tag_id UUID REFERENCES tags(id) ON DELETE CASCADE,
  PRIMARY KEY (article_id, tag_id)
);

Mysli na věci, které přijdou později

Pár sloupců, které se skoro vždy vyplatí přidat hned od začátku, ať je nemusíš dolepovat migrací na produkci, kde to bolí víc.

  • created_at a updated_at: kdy řádek vznikl a kdy se naposled změnil. Budeš je chtít dřív, než čekáš.
  • Měkké mazání: sloupec deleted_at místo skutečného DELETE, pokud potřebuješ data umět vrátit nebo auditovat.
  • Stav: objednávka má status (nová, zaplacená, odeslaná). Nepřidávej na každý stav vlastní booleovský sloupec, jeden textový stav stačí.

Pokud stavíš multi-tenant aplikaci, kde má více klientů sdílet jednu databázi, přečti si i článek o Row Level Security. Izolace dat patří do návrhu od první minuty, ne až potom, co ti uniknou cizí data.

Čeho se vyvarovat

Pár klasických pastí, které jsem viděl tolikrát, že je zmíním zvlášť. Každá z nich vypadá na začátku neškodně a každá tě dřív nebo později štípne.

  1. Ukládání seznamu do jednoho textového pole: e-maily oddělené čárkou v jedné buňce. V tu chvíli ses připravil o možnost v nich rozumně hledat a filtrovat.
  2. Žádné cizí klíče: databáze pak nehlídá konzistenci a ty máš objednávky bez zákazníků a komentáře k neexistujícím článkům.
  3. Vše jako TEXT: datum ukládej jako datum, číslo jako číslo. Ušetří ti to chyby, umožní řadit a počítat a zrychlí dotazy.

Nakresli si schéma, než ho začneš psát

Než vytvoříš první tabulku, nakresli si vztahy na papír nebo do jednoduchého nástroje. Krabičky pro tabulky, čáry pro vztahy. Za deset minut tužkou odhalíš víc problémů než za hodinu psaní SQL, protože uvidíš celek najednou. Když ti diagram dává smysl a vztahy do sebe zapadají, teprve potom sahej na klávesnici.

Tenhle krok lidé přeskakují, protože se jim zdá zbytečný, a pak se diví, proč se jim model rozpadá. Mně osobně ušetřil spoustu přepisování. Schéma je jediná část aplikace, kterou opravdu vyplatí promyslet dopředu, protože všechno ostatní z něj vychází.

Časté dotazy

Mám použít UUID nebo číselné ID?

UUID je bezpečnější, protože z něj nikdo neodhadne počet záznamů ani neuhodne cizí ID podle pořadí. Číselné ID je o trochu rychlejší a čitelnější. Pro veřejně viditelné identifikátory volím UUID, čistě interně někdy číslo.

Jak poznám, že je schéma dost dobré?

Dobré schéma zvládne běžné dotazy bez kopírování dat a bez deseti JOINů. Když chceš přidat novou funkci a stačí ti přidat tabulku nebo sloupec, místo abys přepisoval půlku databáze, jsi na dobré cestě.

Můžu schéma změnit později?

Ano, slouží k tomu migrace. Ale platí, že čím lepší základ, tím méně bolestivých migrací tě čeká. Jak je dělat bezpečně, rozebírám v článku o migracích databáze bez paniky.

Návrh schématu je jedna z věcí, kde se zkušenost vyplatí nejvíc, protože chyby tady bolí nejdéle a opravují se nejhůř. Pokud chceš mít jistotu, že tvůj datový model vydrží růst, ozvi se mi přes kontakt a projdeme ho spolu.