Normalizacja i denormalizacja baz danych — postacie normalne na przykładzie
Normalizacja baz danych krok po kroku: anomalie, 1NF, 2NF, 3NF i BCNF na jednym przykładzie. Do tego denormalizacja — kiedy ma sens i jak ją robić w SQL.

W skrócie
- Normalizacja to podział danych na tabele tak, by każdy fakt był zapisany w jednym miejscu — usuwa redundancję i anomalie aktualizacji, wstawiania i usuwania.
- 1NF: wartości atomowe i brak powtarzających się grup; 2NF: brak zależności od części klucza złożonego; 3NF: brak zależności przechodnich.
- W praktyce projektuje się bazy transakcyjne (OLTP) do 3NF lub BCNF — wyższe postacie normalne są potrzebne rzadko.
- Denormalizacja to celowe powielenie danych dla szybszych odczytów: kolumny wyliczane, tabele podsumowań, widoki zmaterializowane, schemat gwiazdy w hurtowniach.
- Denormalizuj dopiero po zmierzeniu problemu i zawsze zaplanuj, kto i jak utrzymuje spójność kopii.
Spis treści
Normalizacja baz danych to proces projektowania tabel tak, aby każda informacja była zapisana dokładnie w jednym miejscu. Dzięki temu zmiana adresu klienta czy ceny produktu wymaga aktualizacji jednego wiersza, a baza nie może „rozjechać się” na sprzeczne wersje tych samych danych. Robi się to, doprowadzając tabele do kolejnych postaci normalnych: 1NF, 2NF, 3NF i ewentualnie BCNF.
Denormalizacja jest procesem odwrotnym: świadomie powielasz dane albo łączysz tabele, żeby przyspieszyć odczyty. Oba podejścia nie są konkurencyjne — dobrze zaprojektowany system zwykle jest znormalizowany, a denormalizację stosuje punktowo tam, gdzie zmierzono problem z wydajnością. Poniżej pokazujemy obie techniki na jednym, prostym przykładzie sklepu internetowego.
Punkt wyjścia: tabela, która ma wszystkie problemy
Wyobraź sobie, że sklep zapisuje zamówienia w jednej tabeli, jak w arkuszu kalkulacyjnym:
| nr_zam | data | klient | miasto_klienta | produkty | ceny |
|---|---|---|---|---|---|
| 101 | 2026-09-01 | Anna Nowak | Kraków | Mysz, Klawiatura | 89, 199 |
| 102 | 2026-09-02 | Jan Kowalski | Gdańsk | Monitor | 899 |
| 103 | 2026-09-05 | Anna Nowak | Kraków | Mysz | 89 |
Taki układ działa przez chwilę, ale szybko pojawiają się trzy klasyczne anomalie:
- Anomalia aktualizacji. Anna Nowak przeprowadza się do Warszawy. Trzeba zmienić miasto we wszystkich jej zamówieniach; jeśli pominiesz jedno, baza zawiera dwie sprzeczne informacje.
- Anomalia wstawiania. Nie da się zapisać nowego produktu ani klienta, dopóki ktoś czegoś nie zamówi, bo nie istnieje dla nich wiersz.
- Anomalia usuwania. Usunięcie zamówienia 102 kasuje jedyną informację o tym, że Jan Kowalski w ogóle był klientem i mieszka w Gdańsku.
Do tego dochodzi redundancja: nazwa i miasto klienta powtarzają się w każdym zamówieniu, a ceny produktów w każdym wierszu. Normalizacja usuwa te problemy krok po kroku.
Zależności funkcyjne — pojęcie, na którym opiera się normalizacja
Zależność funkcyjna A → B oznacza, że znając wartość A, jednoznacznie znasz wartość B. W naszym przykładzie id_klienta → miasto_klienta (klient ma jedno miasto), a id_produktu → nazwa_produktu.
Kilka pojęć, które pojawiają się w definicjach:
- klucz kandydujący — minimalny zestaw kolumn jednoznacznie identyfikujący wiersz,
- klucz główny — wybrany klucz kandydujący,
- atrybut kluczowy — kolumna należąca do któregoś klucza kandydującego; pozostałe to atrybuty niekluczowe.
Każda postać normalna mówi, jakich zależności funkcyjnych w tabeli być nie może.

Pierwsza postać normalna (1NF): wartości atomowe
Tabela jest w 1NF, gdy:
- każda komórka zawiera jedną, niepodzielną wartość (w kolumnie
produktynie może być listy „Mysz, Klawiatura”), - nie ma powtarzających się grup kolumn typu
produkt1,produkt2,produkt3, - każdy wiersz da się jednoznacznie zidentyfikować kluczem.
Rozbijamy listy na osobne wiersze. Każdy produkt w zamówieniu to teraz jeden wiersz, a kluczem jest para (nr_zam, id_produktu):
| nr_zam | id_produktu | data | id_klienta | klient | miasto | nazwa_produktu | cena | ilosc |
|---|---|---|---|---|---|---|---|---|
| 101 | 1 | 2026-09-01 | 7 | Anna Nowak | Kraków | Mysz | 89 | 1 |
| 101 | 2 | 2026-09-01 | 7 | Anna Nowak | Kraków | Klawiatura | 199 | 1 |
| 102 | 3 | 2026-09-02 | 8 | Jan Kowalski | Gdańsk | Monitor | 899 | 1 |
Tabela jest w 1NF, ale redundancja wręcz wzrosła — data i klient powtarzają się w każdym wierszu zamówienia.
Druga postać normalna (2NF): cała zależność od całego klucza
Tabela jest w 2NF, gdy jest w 1NF i żaden atrybut niekluczowy nie zależy tylko od części klucza złożonego.
U nas klucz to (nr_zam, id_produktu), tymczasem:
data,id_klientazależą tylko odnr_zam,nazwa_produktuzależy tylko odid_produktu.
To zależności częściowe. Wydzielamy je do osobnych tabel:
zamowienia(nr_zam, data, id_klienta, klient, miasto),produkty(id_produktu, nazwa, cena_katalogowa),pozycje_zamowienia(nr_zam, id_produktu, ilosc, cena_jednostkowa).
Zwróć uwagę na cena_jednostkowa w pozycjach. To nie jest błąd ani denormalizacja: cena w chwili zakupu to inny fakt niż aktualna cena katalogowa. Gdyby pozycje odwoływały się tylko do produkty.cena_katalogowa, podwyżka ceny zmieniłaby wartość historycznych zamówień.
Tabela z kluczem jednokolumnowym jest w 2NF automatycznie, jeśli spełnia 1NF — zależność częściowa wymaga klucza złożonego.
Trzecia postać normalna (3NF): bez zależności przechodnich
Tabela jest w 3NF, gdy jest w 2NF i żaden atrybut niekluczowy nie zależy od innego atrybutu niekluczowego. Inaczej mówiąc: kolumny opisują klucz, cały klucz i nic poza kluczem.
W tabeli zamowienia mamy łańcuch nr_zam → id_klienta → klient, miasto. Miasto zależy od zamówienia tylko pośrednio, przez klienta — to zależność przechodnia. Wydzielamy klientów:
CREATE TABLE klienci (
id_klienta INT PRIMARY KEY,
imie_nazwisko VARCHAR(100) NOT NULL,
miasto VARCHAR(80)
);
CREATE TABLE produkty (
id_produktu INT PRIMARY KEY,
nazwa VARCHAR(120) NOT NULL,
cena_katalogowa NUMERIC(10,2) NOT NULL
);
CREATE TABLE zamowienia (
nr_zam INT PRIMARY KEY,
data DATE NOT NULL,
id_klienta INT NOT NULL REFERENCES klienci(id_klienta)
);
CREATE TABLE pozycje_zamowienia (
nr_zam INT REFERENCES zamowienia(nr_zam),
id_produktu INT REFERENCES produkty(id_produktu),
ilosc INT NOT NULL CHECK (ilosc > 0),
cena_jednostkowa NUMERIC(10,2) NOT NULL,
PRIMARY KEY (nr_zam, id_produktu)
);
Teraz przeprowadzka klientki to jedna zmiana w jednym wierszu tabeli klienci, nowy produkt można dodać bez zamówienia, a usunięcie zamówienia nie kasuje danych klienta. Wszystkie trzy anomalie zniknęły.

BCNF, 4NF i 5NF — kiedy iść dalej
Postać normalna Boyce’a-Codda (BCNF) to zaostrzona 3NF: dla każdej nietrywialnej zależności X → Y, X musi być nadkluczem. Różnica wychodzi tylko w tabelach z kilkoma nakładającymi się kluczami kandydującymi — np. gdy prowadzący jednoznacznie wyznacza przedmiot, a para (student, przedmiot) wyznacza prowadzącego. Szczegółowy przykład znajdziesz w artykule o postaci normalnej Boyce’a-Codda.
Czwarta postać normalna (4NF) usuwa zależności wielowartościowe — sytuację, gdy w jednej tabeli trzymasz dwie niezależne listy (np. języki i umiejętności pracownika), co wymusza sztuczne kombinacje wierszy. Piąta (5NF) dotyczy zależności złączeniowych i w praktyce projektowania aplikacji pojawia się rzadko.
Dla typowej bazy transakcyjnej rozsądnym celem jest 3NF lub BCNF. Jeśli podczas projektowania trzymasz się zasady „jedna tabela = jeden rodzaj obiektu lub relacji”, najczęściej trafiasz tam naturalnie.
Denormalizacja: kiedy celowo łamać zasady
Znormalizowana baza wymaga złączeń. Raport „sprzedaż według miast w ostatnim roku” łączy cztery tabele i przelicza miliony pozycji. Przy dużym ruchu odczytów to może być za wolne — i wtedy wchodzi denormalizacja.
Najczęstsze techniki:
- Kolumna z zapisaną wartością wyliczaną — np.
wartosc_zamowieniaw tabelizamowieniazamiast sumowania pozycji przy każdym wyświetleniu listy zamówień. - Powielenie kolumny z innej tabeli — np.
miastokopiowane do zamówienia, żeby raport geograficzny nie wymagał złączenia. Uwaga: to ma sens tylko wtedy, gdy chcesz zachować miasto z chwili zakupu albo akceptujesz opóźnioną synchronizację. - Tabele podsumowań i widoki zmaterializowane — wynik ciężkiego zapytania zapisany jako tabela, odświeżany cyklicznie.
- Schemat gwiazdy w hurtowni danych — tabela faktów (sprzedaż) i zdenormalizowane tabele wymiarów (klient, produkt, czas). Standard w systemach analitycznych.
- Dokumenty JSON — w bazach dokumentowych i kolumnach
JSONBdane odczytywane zawsze razem trzyma się w jednym dokumencie.
Przykład widoku zmaterializowanego w PostgreSQL:
CREATE MATERIALIZED VIEW sprzedaz_wg_miast AS
SELECT k.miasto,
date_trunc('month', z.data) AS miesiac,
SUM(p.ilosc * p.cena_jednostkowa) AS przychod
FROM zamowienia z
JOIN klienci k ON k.id_klienta = z.id_klienta
JOIN pozycje_zamowienia p ON p.nr_zam = z.nr_zam
GROUP BY k.miasto, date_trunc('month', z.data);
CREATE UNIQUE INDEX ON sprzedaz_wg_miast (miasto, miesiac);
-- odświeżanie bez blokowania odczytów (wymaga indeksu unikalnego)
REFRESH MATERIALIZED VIEW CONCURRENTLY sprzedaz_wg_miast;
Raport czyta teraz gotową, małą tabelę, a dane źródłowe pozostają znormalizowane. To zwykle najbezpieczniejsza forma denormalizacji, bo kopia jest jawnie oznaczona jako pochodna i wiadomo, jak ją odtworzyć.
Ważne: Każda zdenormalizowana kopia musi mieć właściciela: wyzwalacz, zadanie cykliczne albo kod aplikacji, który ją aktualizuje. Kopia bez mechanizmu synchronizacji to prosta droga do anomalii, przed którymi chroniła normalizacja.
Normalizacja czy denormalizacja — jak zdecydować
| Kryterium | Normalizacja | Denormalizacja |
|---|---|---|
| Typ systemu | Transakcyjny (OLTP): sklep, CRM, system księgowy | Analityczny (OLAP), raporty, wyszukiwarki, cache |
| Przewaga operacji | Dużo zapisów i aktualizacji | Dużo odczytów, rzadkie zapisy |
| Spójność | Gwarantowana strukturą i kluczami | Wymaga dodatkowych mechanizmów |
| Zajętość miejsca | Mniejsza | Większa |
| Złożoność zapytań | Więcej złączeń | Prostsze, szybsze odczyty |
| Koszt zmian schematu | Niższy | Wyższy — zmiana w wielu miejscach |
Praktyczna kolejność działań:
- Zaprojektuj schemat w 3NF.
- Dodaj indeksy pod rzeczywiste zapytania — często rozwiązują problem bez żadnej denormalizacji.
- Zmierz wydajność (
EXPLAIN ANALYZEw PostgreSQL,EXPLAINw MySQL) na realistycznej ilości danych. - Dopiero gdy konkretne zapytanie jest za wolne, zdenormalizuj punktowo — najlepiej widokiem zmaterializowanym lub tabelą podsumowań.
Normalizacja a wybór i utrzymanie bazy danych
Zasady normalizacji są takie same w PostgreSQL, MySQL/MariaDB, SQL Server czy Oracle — to część teorii modelu relacyjnego, a nie funkcja konkretnego produktu. Różnią się narzędzia do denormalizacji: nie każdy silnik ma widoki zmaterializowane, a bazy NoSQL, jak MongoDB, z założenia zachęcają do modelowania pod wzorce odczytu. Jeśli dopiero wybierasz silnik, zajrzyj do przeglądu najpopularniejszych systemów baz danych.
Dobra struktura to dopiero początek. W produkcyjnej bazie potrzebujesz też kontroli dostępu, kopii zapasowych i monitorowania — zarówno wydajności, jak i tego, kto i jakie dane odczytuje. O ochronie i monitorowaniu aktywności baz danych piszemy w tekście co to jest DAM (Database Activity Monitoring), a o ograniczaniu uprawnień użytkowników w artykule o RBAC i ABAC.
Najczęściej zadawane pytania
Co to jest normalizacja bazy danych?
To proces projektowania tabel relacyjnej bazy danych tak, aby usunąć nadmiarowe dane i zależności, które prowadzą do niespójności. Realizuje się go przez doprowadzanie tabel do kolejnych postaci normalnych: 1NF, 2NF, 3NF, BCNF.
Czym różni się normalizacja od denormalizacji?
Normalizacja dzieli dane na więcej tabel, by każdy fakt był zapisany raz. Denormalizacja robi odwrotnie: celowo powiela dane lub łączy tabele, aby przyspieszyć odczyty kosztem trudniejszego utrzymania spójności.
Do której postaci normalnej normalizować bazę?
Dla typowych baz transakcyjnych celem jest trzecia postać normalna lub postać Boyce’a-Codda. Czwarta i piąta postać normalna dotyczą rzadkich przypadków zależności wielowartościowych i złączeniowych.
Kiedy stosuje się denormalizację?
Gdy odczyty dominują nad zapisami, a zmierzone zapytania z wieloma złączeniami są za wolne: w raportach, hurtowniach danych, cache’ach i systemach analitycznych. W bazach transakcyjnych stosuje się ją punktowo.
Czy normalizacja danych to to samo co normalizacja bazy danych?
Nie zawsze. W statystyce i uczeniu maszynowym normalizacja danych oznacza skalowanie wartości liczbowych, np. do przedziału 0–1. W bazach danych chodzi o strukturę tabel i zależności między kolumnami.
Autor
Założyciel i redaktor XAD.pl. Pisze o sieciach, bezpieczeństwie IT, administracji systemami Windows i Linux oraz o sprzęcie, który sprawia ludziom problemy na co dzień.


