TakéFK, foreign key constraint, referenční omezeníPokročilý

Definice

Cizí klíč je databázové omezení, které ukládá, že hodnota ve sloupci nebo skupině sloupců musí odpovídat existujícímu řádku v jiné, případně téže tabulce. Relační databáze tím chrání vazby mezi záznamy, například aby objednávka neodkazovala na neexistujícího zákazníka a data zůstala konzistentní.

Kategorie: DatabázeAktualizováno

Proč cizí klíč hlídá smysl dat

Cizí klíč vzniká tam, kde jedna tabulka ukládá odkaz na řádek v jiné tabulce. Typický příklad je sloupec customer_id v tabulce objednávek, který míří na sloupec id v tabulce zákazníků. Bez databázového omezení by aplikace mohla omylem uložit objednávku se zákazníkem, který nikdy neexistoval nebo byl mezitím smazán.

Cizí klíč je součást referenční integrity. Databáze při zápisu kontroluje, zda odkazovaná hodnota existuje v cílovém klíči. Cílový klíč bývá primární klíč, ale může jít i o jiný sloupec nebo kombinaci sloupců s unikátním omezením. Odkazovat lze také do stejné tabulky, například u stromové struktury kategorií se sloupcem parent_id.

Co se kontroluje při vložení a změně řádku

Cizí klíč neukládá kopii rodičovského řádku. Cizí klíč ukládá jen hodnotu odkazu a databázový systém při operaci ověří, zda je vztah platný. Vložení položky faktury selže, pokud položka odkazuje na neexistující fakturu. Změna identifikátoru rodiče zase může selhat, pokud na původní hodnotu stále odkazují potomci.

Hodnota NULL má zvláštní význam. Pokud je sloupec cizího klíče povolený jako NULL, databáze obvykle chápe chybějící hodnotu jako žádný odkaz, nikoli jako špatný odkaz. Povinný vztah se proto vyjadřuje kombinací NOT NULL a cizího klíče.

Mazání rodiče bez osiřelých záznamů

Cizí klíč musí řešit otázku, co se stane při smazání nebo změně odkazovaného řádku. Volba RESTRICT nebo podobné výchozí chování operaci zablokuje, pokud existují závislé řádky. Volba CASCADE provede související změnu i u potomků, například smaže komentáře při smazání článku. Volba SET NULL odkaz pouze vyprázdní, pokud to schéma dovoluje.

Kaskádové mazání je pohodlné, ale vyžaduje opatrný návrh. Jediný příkaz může zasáhnout velkou část datového modelu. U účetních, auditních nebo právně významných dat bývá bezpečnější smazání zakázat a místo něj používat stav, archivaci nebo soft delete.

Cena kontroly a indexy

Cizí klíč má provozní cenu, protože databáze musí kontrolovat existenci rodiče a závislé řádky při změnách. Index na odkazovaném klíči je nutný nebo prakticky vynucený tím, že cílový klíč musí být unikátní. Index na sloupci cizího klíče u potomků ale v některých databázích nevzniká automaticky.

Index na potomkovi často výrazně pomáhá při joinu, při mazání rodiče i při kontrole závislostí. Databázový návrh proto obvykle doplňuje cizí klíč indexem na sloupcích, které odkaz ukládají. Výjimkou mohou být malé tabulky nebo vztahy, které se téměř nikdy nevyhledávají.

Příklady z praxe

  1. Objednávka musí patřit existujícímu zákazníkovi

    E-shop ukládá objednávky do tabulky orders a zákazníky do tabulky customers. Cizí klíč zabrání tomu, aby se do databáze dostala objednávka s neexistujícím zákazníkem. Chyba se projeví už při zápisu, takže aplikace nemusí neplatný vztah hledat až později v reportech.

    CREATE TABLE customers (
      id BIGINT PRIMARY KEY,
      email TEXT NOT NULL UNIQUE
    );
    
    CREATE TABLE orders (
      id BIGINT PRIMARY KEY,
      customer_id BIGINT NOT NULL REFERENCES customers(id),
      created_at TIMESTAMP NOT NULL
    );
    
    -- Selže, pokud zákazník s id 42 neexistuje.
    INSERT INTO orders (id, customer_id, created_at)
    VALUES (1001, 42, CURRENT_TIMESTAMP);
  2. Komentáře se smažou spolu s článkem

    Redakční systém má články a komentáře. Pokud administrátor smaže článek, komentáře bez článku by nedávaly smysl, proto cizí klíč používá kaskádové mazání. Databáze provede úklid konzistentně v rámci stejné operace.

    CREATE TABLE articles (
      id BIGINT PRIMARY KEY,
      title TEXT NOT NULL
    );
    
    CREATE TABLE comments (
      id BIGINT PRIMARY KEY,
      article_id BIGINT NOT NULL,
      body TEXT NOT NULL,
      FOREIGN KEY (article_id)
        REFERENCES articles(id)
        ON DELETE CASCADE
    );
    
    DELETE FROM articles WHERE id = 10;
    -- Databáze smaže i komentáře k článku 10.

Časté omyly

MýtusCizí klíč je jen sloupec s názvem něco_id.
Ve skutečnostiCizí klíč není pouhá konvence pojmenování. Skutečný cizí klíč je databázové omezení, které databáze vynucuje při zápisu, změně a mazání dat.
MýtusCizí klíč automaticky smaže související záznamy.
Ve skutečnostiCizí klíč sám o sobě obvykle smazání rodiče spíš zablokuje. Kaskádové mazání nastane jen tehdy, když je ve schématu výslovně nastavené například pomocí ON DELETE CASCADE.
MýtusCizí klíče se v moderních aplikacích nepoužívají, protože kontrolu řeší ORM.
Ve skutečnostiORM může kontrolovat vztahy v aplikační vrstvě, ale databázové omezení chrání data i při ručních opravách, migracích, více aplikacích nad stejnou databází a chybách v kódu.

Časté dotazy

Musí být cizí klíč vždy indexovaný?
Cizí klíč nemusí být v každé databázi automaticky indexovaný na straně potomka. Index je ale často prakticky nutný, protože zrychluje joiny, mazání rodičovských řádků i kontroly závislostí. Bez indexu může databáze při změně rodiče procházet velkou tabulku potomků. Správná volba závisí na velikosti tabulky, četnosti zápisů a dotazech, které vztah používají.
Může cizí klíč obsahovat hodnotu NULL?
Cizí klíč může obsahovat NULL, pokud sloupec nebo některý sloupec ve vztahu není označený jako NOT NULL a databázový systém takové schéma dovoluje. Hodnota NULL obvykle znamená, že vztah není vyplněný, ne že odkaz míří na konkrétní rodičovský řádek. Povinný vztah proto nestačí vyjádřit samotným cizím klíčem, ale vyžaduje také NOT NULL.
Proč vložení řádku selže kvůli cizímu klíči?
Cizí klíč brání vložení řádku tehdy, když hodnota odkazu neodpovídá žádnému povolenému rodičovskému klíči. Častou příčinou je špatné pořadí importu dat: potomek se vkládá dřív než rodič. Další možností je chyba v mapování identifikátorů, odkaz do jiné databázové instance nebo pokus uložit prázdnou hodnotu do povinného vztahu.
Kdy dává smysl odložená kontrola cizího klíče?
Cizí klíč se někdy u hromadných importů nebo složitých transakcí vyhodnocuje odloženě, pokud to databázový systém podporuje. Odložená kontrola dovolí dočasný stav, který je během transakce neúplný, ale na konci musí být konzistentní. Tento přístup se hodí například při cyklických vztazích nebo obnově záloh, neměl by však zakrývat trvalé chyby v datovém modelu.

Zdroje

  1. SQLite Foreign Key Support(otevře se v novém okně)SQLite Consortium
  2. PostgreSQL: Documentation: 18: 5.5. Constraints(otevře se v novém okně)PostgreSQL Global Development Group
  3. Primary and foreign key constraints(otevře se v novém okně)Microsoft
  4. Foreign key(otevře se v novém okně)Wikipedia

Související pojmy

Potřebujete to vyřešit v praxi?

Poradíme, jak na to ve vašem projektu

Vysvětlit pojem je jedna věc, navrhnout kolem něj funkční řešení druhá. Ozvěte se a probereme, co dává smysl u vás.