1. normální forma - syntakticky i sémanticky

2. 8. 2019 11:40 (aktualizováno) Michal Šalko

Jednou jeden kolega na jedné své přednášce řekl, že nejhorším sloupcem v tabulkách je sloupec poznámka. Často se stává, že do poznámky se pak ukládají jinde neumístitelná data, protože s datovým modelem se různých důvodů nechce hýbat. Pak pro výběr dat z takovéhoto sloupce musíte používat složitou heuristiku.

Mohu to potvrdit z vlastní praxe.  Normalizace datového modelu databáze pomůže těmto i mnohým jiným problémům lehce předcházet.  Dnes bychom se podívali na to, jak by se s takovou poznámkou mělo zacházet. Neboli 1. normální forma. V tomto článku čerpám ze svých materiálů z přednášky „Entitno relační modelování“ (březen 2016).

Abstrakce : 1. Normální forma

Tabulka je v 1. NF právě když :

  1. Každý sloupec má jedinečný název v rámci tabulky.
  2. Každý sloupec má jeden datový typ.
  3. Žádné dva řádky nemohou obsahovat stejné hodnoty (primární klíč tabulky).
  4. Každý sloupec musí obsahovat jednu hodnotu (atomičnost).

Příklad Dodržíme syntax

V Excelu jsme vytvořili seznam členů spolku a protože jich máme již hodně, rozhodneme se je převést do relační databáze. Tak vytvoříme kopií excelovského souboru v datbáze a dostaneme toto :

Seznam členů spolku
jmeno
(text)
adresa
(text)
Michal Pohnoutka starší Brno, Květná 25, Bohunice, 612 04
Linda Krásná Litoval, náměstí míru, 487 00
Móric Beňovský gróf Slovakia, Kláštor na riekou, 82, 945 32
Michal Pohnoutka mladší Brno, Květná 25, Bohunice, 612 04

Krásná tabulka, která není v 1. normální formě. Ptáte se proč? Pojďme společně ověřit si data.

1. Každý sloupec má jedinečný název v rámci tabulky.

        Splněno OK. Máme jméno a pak adresu

2. Každý sloupec má jeden datový typ.

       Splněno OK

3. Žádné dva řádky nemohou obsahovat stejné hodnoty (primární klíč tabulky).

   Splněno. Od katastorfy nás v případě Michala Pohnoutky zachránilo pouze to že za příjmení jsme dali přívlastek. Asi bychom mněli zavést něco jako osobní číslo, které by bylo pro každého člena jedinečné.

4. Každý sloupec musí obsahovat jednu hodnotu (atomičnost).

Splněno ? Ne určitě ne. Sloupec jméno obsahuje vlastní jméno, příjmení, titul- nebo přívlastek. Adresa taky obsahuje Stát, Město,ulici, číslo popisném, část obce a psč. Ptáme se jak bychom pomocí SQL jazyka např: vzbrali seznam všech měst. Asi těžko.

Proto bychom měli udělat následující úpravu uložení dat v tabulce.

Uložení dat v 1. normální formě
osobni_cislo
(integer)
jmeno
(text)
prijemni
(text)
titul
(text)
mesto
(text)
ulice
(text)
cislo
(text)
cast_obce
(text)
psc
(char(6))
stat
(text)
1 Michal Pohnoutka st. Brno Květná 25 Bohunice 61204 Česko
2 Linda Krásná Litovel náměstí Míru 12 48700 Česko
3 Moric Beňovský gróf Kláštor nad riekou 82 94532 Slovensko
4 Michal Pohnoutka ml. Brno Květná 25 Bohunice 61204 Česko

Takhle jsem zabespečili, že jednak jsou data uložená v atomické podobě a jednak jsou jednoduše identifikovaná pomocí jedinečného klíče osobní číslo.

Příklad Jak dodržením syntaxe nedodržíme sémanitku

Mnozí z nás se naučili pracovat a myslet v intentcích tabulkových procesorů jako je Excel. První věcí když nepostačuje sílá Excelu, je uložit data do databáze. V PostgreSQL to jde velice jednoduše. Data uložíte do .cvs formátu (uložit jako) a buďto příkazy copy nebo /copy nebo přes fdw dostaneme data do databáze. Pak můžeme dostat tuhle tabulku.

Příklad převodu dat z Excelu do databáze
kategorie 1_mesic hodnota 2_mesic hodnota2 3_mesic hodnota3
Kategorie A 11.1.1998 12,3 14.2.1998 56,25 7.3.1998 2,3
20.1.1998 45 20.2.1998 12,8 15.3.1998 5,12
30.1.1998 0,5 24.2.1998 14,2

Pro jistotu jednotlivé sloupce jsou v datovém typu text.

Takáto tabulka v databáze není v 1. normální formě. Proč ?

1. Podmínka je splněna. Každý sloupec má jedinečný název. I kdybychom se pokusili vytvořit tabulku se stejným pojmenováním sloupců, tak PostgreSQL nám to neumožní.

2. Podmínka je splněna. Každý sloupec má jeden datový typ. V našem případě text, protože ten je univerzální. Zde bych se zastavil. Zvolili jsme datový typ text, ale sami vidíme, že některé sloupce jsou skutečně textové jako kategorie. Ale jiná jsou datumová např. 1_měsíc a jiná typu number. Tak to zase tak s 2. podmínkou není úplně v pořádku. Později si vysvětlíme proč.

3. Žádné dva řádky nemohou obsahovat stejné hodnoty. OK splněno i když tabulce je to v tomto případě jedno, ale nám by nemnělo. Výběr dat v relační datbáze je pomocí řádku a položme si otázku. Jak bychom zde vybrali 2. řádek pomocí SQL dotazu?

4. Každý sloupec musí obsahovat jednu hodnotu. OK splněno. Do opravdy ? Např 2. řádek tabulky je jaké kategorie ? Excelovské myšlení nám říká přece Kategorie A, ale relační databázové myšlení nám říká null. Kdybych vybrali data pomocí SQL příkazu, tak hodnota by byla null.

Řekneme si tak co, splněno. Ano máme pravdu splněno z pohledu syntaxe. Ale z pohledu sémantiku ne. Jak bychom například spočetli průměrnou hodnotu ? No museli bychom spočítat průměrnou hodnotu za jednotlivé měsíce a ty pak zprůměrovat.  Zbytečně složité i když proveditelné. Jednoduše je to možné upravit datovou strutktúru tak, aby odpovídala sématice 1. NF.

Zkusme se ji zformulovat :

Sématická pravidla 1. normální formy

1. Každá tabulka musí obsahovat jedinečný primární klíč, pomocí kterého se dají identifikovat a měnit popisná data daného konkrétního řádku tabulky.

2. Každý řádek tabulky musí obsahovat izolovaná data tak, aby byla nezávislá oproti jiným řádkům tabulky.

3. Sloupce musí být redukovány tak, aby obsahovala sématicky (významově) stejná data aniž by se narušila jejich jedinečnost v rámci tabulky.

4. Každý sloupec musí obsahovat sémanticky jeden druh dat.

V praxi to znamená, že zavedeme primární klíč tabulky. V tomto případě abstraktní id.

Pak kategorii všude doplníme v každém řádku tabulky.

Sloučime sloupce 1_mesic,2_mesic,3_mesic do sloupce datum a hodnota, hodnota2, hodnota3 do sloupce hodnota. Zvětší se nám sice počet řádků, ale zmenší se šířka tabulky.

Výslední podoba tabulky bude :

Tabulka dat v 1.NF syntakticky i sémanticky
id kategorie datum hodnota
1 Kategorie A 11.1.1998 12,3
2 Kategorie A 20.1.1998 45
3 Kategorie A 30.1.1998 0,5
4 Kategorie A 14.2.1998 56,25
5 Kategorie A 20.2.1998 12,8
6 Kategorie A 24.2.1998 14,2
7 Kategorie A 7.3.1998 2,3
8 Kategorie A 15.3.1998

Nebo kdyby Jste si nepamatovali to hroznou sématickou definici, vzpomente si na tento příklad.

Dodatek EAV tabulky

EAV tabulky (entita – atribut – hodnota) nejsou taky v 1. NF. Protože nesplňují 4. pravidlo , že jeden sloupec obsahuje jednu kategorii dat a syntaktické pravidlo,každý sloupec má jeden datový typ.

(V budoucnu přidám příklad)

Dodatek JSON a XML a obecně kompositní datové typy

Podle mně taky nejsou v 1.NF, protože nedodržují minimálně pravidlo atomičnosti. V PostgreSQL se to obchází pomocí funkcí, pomocí kterých je možné separovat data a vracet je do atomické podoby v 1.NF.

A co datový typ datum. Hm. Posudte samy … :)

Sdílet

  • 6. 6. 2016 12:26

    podlesh

    Hnidopišská poznámka: sloupčky "titul" a "cislo" pořád nejsou atomické, protože mohou (a budou) obsahovat více údaje. U čísla je to jasné: jedná se o číslo popisné, nebo číslo orientační? Běžně tam budou lidi zadávat jedno nebo druhé, nebo oboje. Tady je to naprosto jasné, rozdělit na dva sloupečky.

    V případě titulu je jediná plná normální forma vazební tabulka na tabulku titulů (m:n), a to možná dokonce dvě (jeden titul před jménem, jeden za jménem). Zda je to rozumné, to už nechávám na každém.

    No a ještě k těm adresám - v závislosti na použití se ještě může stát že budou potřeba další sloupečky, např. pro číslo bytu, stát/region, PO box a tak dále a tak dále. No a nakonec tam stejně nakonec vznikne sloupeček pro poznámku, protože kam jinam dát informaci "mimo pracovní dobu zvoňte dvakrát"?

    Ale jinak dobrý blogpost, jen tak dál.