1/60Normalizacja baz danych – 1NF: Pierwsza postać normalna

Od danych surowych do dobrze zorganizowanej struktury – krok pierwszy

Prezentacja przeznaczona dla studentów I roku kierunków IT. Zakładamy zerowy poziom wiedzy o bazach danych – zaczniemy od absolutnych podstaw.

Ta prezentacja jest pierwszą z cyklu o normalizacji. W kolejnych częściach omówimy 2NF, 3NF, BCNF, 4NF i 5NF – wszystkie na tym samym przykładzie systemu bibliotecznego.

Normalizacja to proces organizacji danych w bazie w celu minimalizacji redundancji i zależności
Slajd tytułowy – przejście od chaotycznych danych do uporządkowanej struktury 1NF z trzema złotymi zasadami

Proces normalizacji danych można porównać do porządkowania obszernego repozytorium informacji, w którym każdy element ma ściśle określone miejsce. W praktyce inżynierii oprogramowania normalizacja stanowi fundamentalną technikę projektowania schematów relacyjnych baz danych, mającą na celu wyeliminowanie nadmiarowości oraz zabezpieczenie integralności przechowywanych informacji. Zrozumienie tego procesu wymaga systematycznego podejścia i stopniowego przyswajania kolejnych reguł, poczynając od najprostszych.

Pierwsza postać normalna, którą dziś poznasz, jest najłatwiejszym i zarazem najważniejszym krokiem w całej ścieżce normalizacji. Bez solidnego opanowania 1NF nie będziesz w stanie poprawnie zastosować wyższych postaci normalnych, takich jak 2NF czy 3NF. W trakcie dzisiejszych zajęć przekonasz się, że reguły 1NF są intuicyjne i wynikają wprost z logicznego myślenia o strukturze danych. Każdy przykład, który przeanalizujemy, został dobrany tak, aby zilustrować konkretny problem projektowy.

Pamiętaj, że umiejętność normalizowania tabel przychodzi z praktyką; im więcej samodzielnie przeanalizujesz przypadków, tym łatwiej będziesz dostrzegać błędy w istniejących schematach. Celem dzisiejszego spotkania jest wyposażenie Cię w narzędzia pozwalające świadomie oceniać jakość projektu relacyjnej bazy danych.

2/60Agenda prezentacji – plan podróży przez 60 slajdów

Siedem części – od podstaw po praktykę

Prezentacja składa się z siedmiu części:

  • Część 0: Absolutne podstawy (slajdy 1-10) – co to są bazy danych, tabele, SQL, klucz główny
  • Część I: Wprowadzenie do normalizacji (slajdy 11-16) – definicje, historia, korzyści
  • Część II: Problemy bez normalizacji (slajdy 17-24) – przykład biblioteczny, anomalie, redundancja
  • Część III: Definicja 1NF (slajdy 25-33) – trzy złote zasady: atomowość, brak grup, klucz główny
  • Część IV: Przykład krok po kroku (slajdy 34-43) – analiza tabeli bibliotecznej, ćwiczenia
  • Część V: Praktyka – implementacja (slajdy 44-52) – CREATE TABLE, INSERT, SELECT, UPDATE, DELETE
  • Część VI: Podsumowanie (slajdy 53-60) – 1NF w pigułce, ćwiczenia, zapowiedź 2NF
Nie martw się, jeśli nic nie rozumiesz – zaczniemy od zera. Każde pojęcie wyjaśnimy po drodze.
Mapa prezentacji – siedem części połączonych strzałkami, od podstaw przez teorię po praktykę

Prezentacja została zaprojektowana według sprawdzonej metody dydaktycznej: od ogółu do szczegółu, z systematycznym narastaniem poziomu skomplikowania. Taki układ nie jest przypadkowy – każda kolejna część opiera się na wiedzy zdobytej w częściach poprzednich, co pozwala na płynne przejście od podstaw do zaawansowanych zagadnień. W trakcie sześćdziesięciu slajdów zbudujesz kompletną mapę pojęciową dotyczącą pierwszej postaci normalnej.

Zwróć uwagę na to, że agenda zawiera zarówno część teoretyczną, jak i praktyczną – od definicji, przez analizę problemów, aż po implementację w języku SQL. Taka struktura gwarantuje, że nie tylko zrozumiesz reguły 1NF, ale także nauczysz się je stosować w rzeczywistych projektach. W każdej chwili możesz wrócić do agendy, aby sprawdzić, na jakim etapie się znajdujesz i co jeszcze przed Tobą.

Zachęcam Cię do aktywnego śledzenia prezentacji i notowania pytań, które pojawią się w trakcie poszczególnych części. Systematyczne utrwalanie materiału, szczególnie w przypadku tak fundamentalnych zagadnień jak normalizacja, znacząco przyspiesza proces uczenia się i pomaga uniknąć typowych błędów na późniejszych etapach projektowania baz danych.

3/60Co to jest baza danych?

Definicja – zorganizowany zbiór informacji

Baza danych to zorganizowany zbiór informacji przechowywany w komputerze, którym zarządza specjalny program (DBMS).

Analogia: baza danych to jak cyfrowa szafka na dokumenty – wszystko posegregowane, opisane i łatwe do znalezienia.

Przykłady baz danych w życiu codziennym:

  • Spis klientów w sklepie internetowym (Allegro, Amazon)
  • Katalog książek w bibliotece
  • System ocen w szkole (dziennik elektroniczny Librus, Vulcan)
  • Rejestr pacjentów w przychodni
  • System rezerwacji w kinie lub teatrze
Każda aplikacja, której używasz – Facebook, Allegro, WhatsApp – korzysta z bazy danych. Bez nich nie działałby internet.
Schemat – baza danych jako centralne repozytorium pomiędzy aplikacjami a użytkownikami

W informatyce baza danych to znacznie więcej niż tylko przechowalnia informacji – to zaawansowany system umożliwiający sprawne przechowywanie, wyszukiwanie i modyfikowanie danych przy zachowaniu ich spójności. Każda współczesna aplikacja webowa, system bankowy czy platforma e-commerce opiera swoje działanie na bazie danych, która stanowi warstwę persystencji dla całego rozwiązania. Bez tej warstwy jakiekolwiek trwałe przechowywanie stanu aplikacji byłoby niemożliwe.

Warto uświadomić sobie, że baza danych to nie tylko zbiór tabel, ale przede wszystkim logicznie uporządkowana struktura, w której obowiązują ściśle określone reguły dotyczące relacji pomiędzy danymi. Dzięki temu możliwe jest wykonywanie złożonych zapytań łączących informacje z wielu tabel, co stanowi podstawę funkcjonowania systemów informacyjnych. Wyobraź sobie system biblioteczny, w którym musisz znaleźć wszystkie książki wypożyczone przez czytelnika z danego miasta – bez odpowiednio zaprojektowanej bazy danych takie zapytanie byłoby niezwykle trudne do zrealizowania.

Bazy danych relacyjne, takie jak MariaDB, którą wykorzystujemy w naszym kursie, stanowią standard w przemyśle informatycznym od kilkudziesięciu lat i są niezmiennie jednym z najważniejszych obszarów wiedzy każdego inżyniera oprogramowania.

4/60Tabela – podstawowa jednostka przechowywania danych

Wiersze i kolumny – jak arkusz kalkulacyjny

Baza danych składa się z tabel (ang. tables). Tabela to jak arkusz w Excelu – ma wiersze i kolumny.

  • Wiersz (ang. row, rekord, krotka) – jeden kompletny zestaw danych, np. dane jednego klienta
  • Kolumna (ang. column, atrybut, pole) – jeden rodzaj informacji, np. nazwisko lub miasto

Przykład tabeli Czytelnicy:

IDImięNazwiskoMiasto
1JanKowalskiKraków
2AnnaNowakWarszawa
3PiotrWiśniewskiGdańsk
Wyobraź sobie tabelę w Excelu. Baza danych działa podobnie, ale z o wiele większymi możliwościami i bezpieczeństwem.
Wizualizacja tabeli z wierszami i kolumnami – trzy wiersze danych (Jan, Anna, Piotr) i cztery kolumny (ID, Imię, Nazwisko, Miasto)

Tabela w relacyjnej bazie danych jest odpowiednikiem zbioru encji w modelu związków encji (ang. entity-relationship model). Każda tabela powinna reprezentować jeden typ obiektu z rzeczywistości, który chcemy modelować w systemie informatycznym. Na przykład w systemie bibliotecznym naturalnymi encjami są czytelnicy, książki oraz wypożyczenia, a każda z nich powinna znaleźć się w osobnej tabeli.

Projektowanie tabel wymaga przemyślenia, jakie atrybuty (kolumny) są potrzebne do opisania danej encji oraz jakie typy danych będą tym atrybutom przypisane. Kluczową decyzją jest wybór klucza głównego, który będzie jednoznacznie identyfikował każdy wiersz. W bazach danych MariaDB najczęściej stosuje się sztuczny klucz numeryczny z automatycznym zwiększaniem wartości, co gwarantuje unikalność i wydajność przy indeksowaniu.

Pamiętaj, że struktura tabeli nie jest sztywna na zawsze – w trakcie rozwoju projektu baza danych ewoluuje, a tabele mogą być modyfikowane za pomocą polecenia ALTER TABLE. Mimo to staranne zaprojektowanie schematu na początku oszczędza wiele czasu i wysiłku w przyszłości.

5/60Baza danych vs arkusz kalkulacyjny (Excel)

Kiedy Excel nie wystarcza?

CechaExcelBaza danych (DB)
Pojemność~1 mln wierszyMiliardy wierszy
Wielu użytkownikówJeden na razWielu jednocześnie
BezpieczeństwoHasło na plikSzczegółowe uprawnienia
WyszukiwanieFiltry, VLOOKUPSQL – język zapytań
Spójność danychRęcznaAutomatyczne więzy
Kopie zapasoweRęczneAutomatyczne
Excel jest świetny do prostych list zakupów. Do prowadzenia sklepu internetowego potrzebujesz bazy danych.
Porównanie ikon – Excel vs baza danych z zaznaczeniem różnic w pojemności i współbieżności

Porównanie arkusza kalkulacyjnego z relacyjną bazą danych unaocznia fundamentalne różnice w podejściu do przechowywania i przetwarzania informacji. Excel sprawdza się doskonale w przypadku niewielkich zbiorów danych, analiz jednorazowych oraz raportowania ad hoc, jednak nie jest zaprojektowany do obsługi wielu jednoczesnych użytkowników ani do zapewnienia integralności referencyjnej pomiędzy powiązanymi tabelami. Bazy danych relacyjne oferują mechanizmy transakcyjne, które gwarantują, że nawet przy równoczesnym dostępie setek użytkowników dane pozostaną spójne.

Kolejną istotną różnicą jest język dostępu do danych. Excel korzysta z formuł i makr, podczas gdy bazy danych udostępniają SQL – potężny, deklaratywny język zapytań, który pozwala w kilku linijkach kodu uzyskać złożone zestawienia danych z wielu tabel jednocześnie. Bezpieczeństwo również stoi na wyższym poziomie: w bazie danych można precyzyjnie określić, kto i jakie operacje może wykonywać na poszczególnych tabelach, czego Excel nie oferuje w tak zaawansowanej formie.

W praktyce inżynierskiej Excel i bazy danych często współistnieją – arkusz służy do wstępnej analizy i raportowania, a baza danych stanowi źródło prawdy (ang. single source of truth) dla całej organizacji.

6/60Co to jest DBMS?

System Zarządzania Bazą Danych

DBMS (Database Management System) to program, który zarządza bazą danych: przechowuje, wyszukuje, aktualizuje i chroni dane.

Popularne DBMS:

  • MariaDB (rozwidlenie MySQL) – darmowy, open-source, bardzo popularny w internecie
  • PostgreSQL – zaawansowany, darmowy, bogaty w funkcje
  • Microsoft SQL Server – komercyjny, często używany w firmach
  • Oracle – dla wielkich korporacji
  • SQLite – mały, wbudowany w aplikacje (np. w telefonie)

W naszym kursie używamy MariaDB – jest darmowy, prosty w obsłudze i doskonały do nauki.

DBMS to jak menedżer bazy danych – on zajmuje się całym magazynowaniem, my tylko mówimy, co chcemy zrobić.
Schemat – DBMS jako warstwa pośrednia między aplikacją/użytkownikiem a fizycznymi danymi na dysku

System zarządzania bazą danych (DBMS) stanowi kluczową warstwę oprogramowania pośredniczącą między fizycznym przechowywaniem danych a aplikacjami korzystającymi z tych danych. Do głównych zadań DBMS należy nie tylko przechowywanie danych, ale także zarządzanie dostępem, realizacja transakcji, tworzenie kopii zapasowych oraz optymalizacja zapytań. Bez DBMS każda aplikacja musiałaby samodzielnie implementować te złożone mechanizmy, co prowadziłoby do ogromnych powtórzeń kodu i ryzyka błędów.

Wybór odpowiedniego DBMS dla projektu to jedna z ważniejszych decyzji architektonicznych. MariaDB, której używamy w kursie, jest doskonałym wyborem do nauki ze względu na prostotę konfiguracji i szeroką dostępność dokumentacji. W środowisku produkcyjnym wybór pomiędzy MariaDB, PostgreSQL, Oracle czy SQL Server zależy od wielu czynników, takich jak przewidywana skala danych, wymagana wydajność, budżet oraz dostępne zasoby ludzkie.

Niezależnie od wybranego systemu, koncepcje normalizacji, które poznajesz w tym kursie, pozostają niezmienne – to uniwersalna wiedza, którą zastosujesz w każdym relacyjnym DBMS.

7/60Ekosystem MariaDB i jak z niego korzystać

Relacyjny system open-source

MariaDB to system relacyjny – dane są przechowywane w tabelach powiązanych ze sobą za pomocą kluczy. Jest darmowy, open-source i działa na Windows, Linux i macOS.

Popularne narzędzia:

  • phpMyAdmin – interfejs webowy
  • DBeaver – uniwersalny klient baz danych
  • HeidiSQL – lekki klient dla Windows
  • Konsola mysql – interfejs tekstowy

Przykład połączenia z konsoli:

-- Logowanie do MariaDB przez terminal
mysql -u root -p
-- Po podaniu hasła widzisz: MariaDB [(none)]>
Nie musisz instalować MariaDB, żeby zrozumieć tę prezentację – pokażemy wszystko na przykładach.
Zrzut ekranu konsoli MariaDB z przykładowym logowaniem i zapytaniem

MariaDB, jako rozwidlenie MySQL-a, zachowuje pełną kompatybilność z tym systemem, jednocześnie wprowadzając liczne usprawnienia wydajnościowe i dodatkowe silniki składowania. Decyzja o rozwidleniu MySQL-a była podyktowana obawami społeczności open-source o przyszłość tego popularnego systemu po przejęciu przez korporację Oracle. Dziś MariaDB jest rozwijana przez fundację MariaDB Foundation i cieszy się wsparciem wielu firm technologicznych na całym świecie.

W codziennej pracy z MariaDB możesz korzystać z różnorodnych narzędzi interfejsu użytkownika. Dla początkujących polecany jest phpMyAdmin, który udostępnia graficzny interfejs webowy umożliwiający wykonywanie operacji na bazie danych bez znajomości składni SQL. Bardziej zaawansowani użytkownicy często wybierają DBeaver lub bezpośrednią konsolę tekstową mysql, która daje pełną kontrolę nad systemem.

Nawet jeśli nie masz zainstalowanej MariaDB, możesz śledzić prezentację i analizować zaprezentowane przykłady SQL – składnia poleceń CREATE TABLE, INSERT, SELECT jest na tyle intuicyjna, że zrozumienie jej nie wymaga bezpośredniego uruchamiania poleceń na serwerze.

8/60Wprowadzenie do SQL – najważniejszy język baz danych

SQL – Structured Query Language

SQL to język używany do komunikacji z bazą danych. Mówisz BAZIE DANYCH, co ma zrobić, a DBMS wykonuje polecenie.

Cztery podstawowe operacje – CRUD:

OperacjaMariaDBOpis
Create (tworzenie)INSERTDodawanie nowych danych
Read (odczyt)SELECTOdczyt danych
Update (aktualizacja)UPDATEModyfikacja danych
Delete (usuwanie)DELETEUsuwanie danych

SQL jest językiem deklaratywnym – mówisz CO chcesz zrobić, a nie JAK to zrobić.

SQL to język, którym 'rozmawiasz' z bazą danych. W tej prezentacji poznasz go od podstaw.
Schemat – cztery operacje CRUD z ikonami: INSERT (dodawanie), SELECT (lupa), UPDATE (edycja), DELETE (kosz)

Język SQL (Structured Query Language) jest fundamentem komunikacji z relacyjnymi bazami danych i stanowi jeden z najdłużej utrzymujących się standardów w informatyce. Mimo że powstał w latach siedemdziesiątych ubiegłego wieku, wciąż pozostaje niekwestionowanym językiem dostępu do danych w systemach relacyjnych. Znajomość SQL jest jedną z najbardziej poszukiwanych umiejętności na rynku pracy dla inżynierów oprogramowania i administratorów baz danych.

Deklaratywny charakter SQL oznacza, że programista formułuje zapytanie w kategoriach oczekiwanego wyniku, nie zaś w kategoriach algorytmu jego uzyskania. To optymalizator zapytań wbudowany w DBMS decyduje o tym, czy dane zostaną pobrane za pomocą indeksu, sekwencyjnego skanowania tabeli, czy też innej strategii dostępu. Dzięki temu nawet skomplikowane zapytania łączące wiele tabel mogą być formułowane w kilku linijkach kodu.

W trakcie kursu nauczysz się czterech podstawowych operacji SQL: INSERT do dodawania danych, SELECT do odczytu, UPDATE do modyfikacji oraz DELETE do usuwania. Te cztery operacje, znane jako CRUD, wystarczą do realizacji większości codziennych zadań związanych z zarządzaniem danymi.

9/60Jak czytać zapytanie SELECT?

SELECT – najczęściej używane polecenie SQL

SELECT służy do odczytywania danych z tabeli. To najważniejsze polecenie SQL.

Składnia podstawowa:

SELECT kolumna1, kolumna2
FROM tabela
WHERE warunek;

Przykład: 'pokaż imiona i nazwiska czytelników z Krakowa'

SELECT Imie, Nazwisko
FROM Czytelnicy
WHERE Miasto = 'Kraków';

Każde zapytanie kończy się średnikiem (;). Wielkość liter w SQL nie ma znaczenia (SELECT = select).

SELECT to najważniejsze polecenie SQL – jeśli umiesz je czytać, umiesz 'rozmawiać' z bazą.
Schemat – strzałki pokazujące przepływ danych: SELECT → kolumny, FROM → tabela, WHERE → filtr

Instrukcja SELECT jest najczęściej używanym poleceniem SQL, a umiejętność jej poprawnego formułowania stanowi podstawę efektywnej pracy z bazami danych. Fundamentalna składnia SELECT ... FROM ... WHERE ... jest z pozoru prosta, ale kryje w sobie ogromną moc wyrazu. Klauzula SELECT określa, które kolumny mają zostać zwrócone w wyniku zapytania, klauzula FROM wskazuje tabelę źródłową, a WHERE nakłada warunki filtrowania wierszy.

Warto od początku wyrobić w sobie nawyk precyzyjnego określania kolumn w SELECT zamiast używania symbolu gwiazdki (*). Wybieranie tylko niezbędnych kolumn zmniejsza ilość danych przesyłanych z serwera do klienta, co przekłada się na wydajność, szczególnie w przypadku dużych tabel. Ponadto jawna lista kolumn ułatwia późniejszą modyfikację zapytania i poprawia czytelność kodu dla innych programistów.

Klauzula WHERE to mechanizm filtrowania, który pozwala wybrać tylko te wiersze, które spełniają określone warunki logiczne. Warunki mogą być łączone za pomocą operatorów AND, OR oraz NOT, a do porównywania wartości służą operatory =, <, >, <> oraz BETWEEN. W późniejszej części kursu nauczysz się również zaawansowanych technik filtrowania z wykorzystaniem podzapytań.

10/60Klucz główny – dowód osobisty wiersza

PRIMARY KEY – jednoznaczna identyfikacja

Klucz główny (ang. PRIMARY KEY) to kolumna (lub zestaw kolumn), która jednoznacznie identyfikuje każdy wiersz w tabeli.

Analogia: PESEL jednoznacznie identyfikuje osobę, numer albumu – studenta.

Właściwości klucza głównego:

  • Unikalność – żadne dwa wiersze nie mają tej samej wartości klucza
  • NOT NULL – klucz nie może być pusty
  • Niezmienność – wartość klucza nie powinna się zmieniać
Klucz główny to jak adres domowy – dzięki niemu wiesz, o który dokładnie wiersz ci chodzi.
Ilustracja – dowód osobisty z numerem PESEL obok schematu tabeli z kolumną ID jako kluczem głównym

Klucz główny (PRIMARY KEY) pełni w relacyjnej bazie danych funkcję jednoznacznego identyfikatora każdego wiersza w tabeli i jest absolutnie niezbędny do zapewnienia integralności danych. Bez klucza głównego dwa wiersze mogłyby zawierać identyczne wartości we wszystkich kolumnach, co uniemożliwiłoby odróżnienie ich od siebie. W teorii relacyjnych baz danych każda tabela powinna posiadać klucz główny – to jeden z fundamentalnych wymogów modelu relacyjnego Codda.

W praktyce projektowej wyróżniamy dwa rodzaje kluczy głównych: naturalne oraz sztuczne (zastępcze). Klucz naturalny wykorzystuje istniejącą kolumnę, która w naturalny sposób jest unikalna – na przykład numer PESEL dla tabeli obywateli. Klucz sztuczny to dodatkowa kolumna, najczęściej typu INTEGER z automatycznym zwiększaniem, która nie ma znaczenia biznesowego, a służy wyłącznie do identyfikacji wierszy. W MariaDB sztuczny klucz definiuje się za pomocą atrybutu AUTO_INCREMENT.

Stosowanie sztucznych kluczy jest powszechnie uznawane za dobrą praktykę, ponieważ wartości biznesowe, takie jak numery PESEL czy adresy email, mogą ulegać zmianie, a klucz główny powinien pozostać niezmienny przez cały cykl życia wiersza.

11/60Co to jest normalizacja?

Definicja i cel

Normalizacja to proces organizacji danych w relacyjnej bazie danych według reguł (postaci normalnych), które minimalizują powtórzenia (redundancję).

Analogia 1 – segregacja w szafie:

Wszystko ma swoje miejsce. Skarpetki są z skarpetkami, koszule z koszulami.

Analogia 2 – przepis na obiad:

Najpierw myjesz warzywa, potem obierasz, potem kroisz – kolejność ma znaczenie. Normalizacja to właśnie taka kolejność: 1NF → 2NF → 3NF → BCNF → 4NF → 5NF.

Normalizacja odpowiedzią na fundamentalne pytanie: jak przechowywać dane, aby nie tracić informacji przy aktualizacji?
Schemat – szafa z segregacją (ubrania posegregowane na półki) jako metafora normalizacji

Normalizacja w kontekście relacyjnych baz danych to proces systematycznego przekształcania schematu tabel w celu wyeliminowania określonych typów anomalii i nadmiarowości. Proces ten opiera się na teorii zależności funkcyjnych, które opisują, jak wartości w jednych kolumnach determinują wartości w innych kolumnach. Zrozumienie normalizacji wymaga więc nie tylko znajomości samych reguł, ale także umiejętności identyfikowania zależności pomiędzy atrybutami w modelowanej rzeczywistości.

Warto podkreślić, że normalizacja nie jest celem samym w sobie, lecz środkiem do osiągnięcia dobrze zaprojektowanej bazy danych. W praktyce inżynierskiej często spotyka się sytuacje, w których świadomie odstępuje się od reguł normalizacji na rzecz wydajności – proces ten nazywamy denormalizacją. Denormalizacja jest jednak dopuszczalna tylko wtedy, gdy w pełni rozumie się konsekwencje takiej decyzji i jest ona podyktowana konkretnymi wymaganiami wydajnościowymi.

Większość systemów produkcyjnych jest znormalizowana do trzeciej postaci normalnej (3NF), która zapewnia dobry kompromis między integralnością danych a wydajnością zapytań. Wyższe postaci normalne, takie jak 4NF czy 5NF, znajdują zastosowanie w specyficznych scenariuszach, głównie w systemach o bardzo złożonych zależnościach między danymi.

12/60Dlaczego normalizacja jest ważna? – przykład z życia

Lista zakupów jako metafora

Wyobraź sobie listę zakupów na kartce:

  • "Kup mleko, chleb, masło od babci"
  • Po tygodniu: "Kup mleko, chleb, ser od babci"
  • Problem: "od babci" – co to znaczy? Które produkty są od babci?

W bazie danych bez normalizacji:

Adres klienta zapisany przy każdym zamówieniu. Klient się przeprowadza – trzeba zmienić adres w 15 miejscach. W 3 miejscach zapominasz – dane są niespójne.

Normalizacja to system zapobiegający bałaganowi – jak reguły segregacji śmieci, tylko dla danych.
Ilustracja – kartka z listą zakupów z przekreśleniami i dopiskami, obok uporządkowana lista w aplikacji

Problem braku normalizacji najlepiej zrozumieć, analizując rzeczywiste konsekwencje dla systemu informatycznego. Wyobraź sobie system biblioteczny obsługujący dziesięć tysięcy czytelników, z których każdy ma średnio dziesięć wypożyczeń. W nieznormalizowanej tabeli dane personalne każdego czytelnika zostałyby powtórzone dziesięciokrotnie, co przy dziesięciu tysiącach czytelników daje sto tysięcy wierszy z nadmiarowymi danymi.

Taka nadmiarowość pociąga za sobą nie tylko zwiększone zapotrzebowanie na przestrzeń dyskową, ale przede wszystkim ryzyko niespójności danych. Jeśli czytelnik zmieni adres zamieszkania, konieczna będzie aktualizacja każdego wiersza zawierającego jego dane. Prawdopodobieństwo, że operator pominie choć jeden wiersz, rośnie proporcjonalnie do liczby wypożyczeń danego czytelnika. W systemie z setkami tysięcy wypożyczeń błędy przy aktualizacji są praktycznie nieuniknione.

Kolejnym praktycznym problemem jest utrudnione wyszukiwanie informacji. Zapytania o wszystkich czytelników mieszkających w danym mieście wymagają przeszukania wszystkich wierszy tabeli wypożyczeń, zamiast szybkiego odczytu z osobnej, znacznie mniejszej tabeli czytelników. To bezpośrednio przekłada się na wydajność systemu i czas odpowiedzi na zapytania użytkowników.

13/60Rys historyczny normalizacji

Od teorii do praktyki – najważniejsze daty

  • 1970 – Edgar F. Codd publikuje 'A Relational Model of Data for Large Shared Data Banks'
  • 1971-1972 – Codd definiuje 1NF, 2NF i 3NF
  • 1974 – Raymond F. Boyce i E.F. Codd opracowują BCNF
  • 1977 – Ronald Fagin definiuje 4NF
  • 1979 – Ronald Fagin definiuje 5NF
  • Dziś – normalizacja to standardowa część projektowania baz danych
Codd nie tylko wynalazł model relacyjny – on też odkrył, że dane trzeba normalizować. To jak wynalezienie pralki i instrukcji prania.
Oś czasu – od 1970 do dziś – z nazwiskami Codd, Boyce, Fagin i kluczowymi datami

Artykuł Edgara Franka Codda z 1970 roku zatytułowany "A Relational Model of Data for Large Shared Data Banks" jest powszechnie uznawany za jeden z najważniejszych tekstów w historii informatyki. Codd, matematyk z wykształcenia, pracujący w laboratoriach IBM w San Jose, zaproponował w nim całkowicie nowe podejście do organizacji danych, oparte na matematycznej teorii relacji. Warto podkreślić, że początkowo koncepcja Codda spotkała się ze sceptycyzmem ze strony praktyków, którzy uważali ją za zbyt teoretyczną i niepraktyczną.

Rozwój normalizacji nastąpił stopniowo na przestrzeni kilkunastu lat. Po opublikowaniu podstaw modelu relacyjnego Codd w latach 1971–1972 zdefiniował pierwsze trzy postaci normalne. W 1974 roku Raymond Boyce, młody badacz współpracujący z Coddem, odkrył silniejszą postać normalną, znaną dziś jako BCNF. Następnie Ronald Fagin z IBM Research kontynuował prace, definiując w 1977 roku czwartą, a w 1979 roku piątą postać normalną.

Dziś normalizacja jest standardowym elementem procesu projektowania baz danych, nauczanym na każdym kierunku informatycznym. Świadczy to o uniwersalności i praktycznej użyteczności koncepcji opracowanych kilkadziesiąt lat temu, które pozostają aktualne również we współczesnych systemach bazodanowych.

14/60Co daje normalizacja? – korzyści

Pięć głównych zalet

  • Mniej powtórzeń – każdy fakt przechowywany dokładnie raz
  • Większa spójność – zmiana w jednym miejscu = zmiana wszędzie
  • Ochrona przed błędami – nie można przypadkiem usunąć ważnych danych
  • Elastyczność – łatwo dodawać nowe rodzaje danych
  • Łatwiejsze wyszukiwanie – dobrze zorganizowane dane są prostsze do przeszukania
Normalizacja to jak sprzątanie biurka – na początku zajmuje czas, ale potem wszystko znajdujesz od ręki.
Pięć ikon reprezentujących korzyści: mniej powtórzeń, spójność, ochrona, elastyczność, szybkość

Korzyści płynące z normalizacji są szczególnie widoczne w systemach o długim cyklu życia, gdzie baza danych funkcjonuje przez wiele lat i jest rozwijana przez różne zespoły programistów. W takim środowisku dobrze znormalizowany schemat znacząco ułatwia zrozumienie struktury danych, wprowadzanie modyfikacji oraz diagnozowanie problemów. Koszt początkowego wysiłku włożonego w normalizację zwraca się wielokrotnie w trakcie całego cyklu życia systemu.

Jednym z często pomijanych aspektów normalizacji jest jej wpływ na jakość danych. Gdy każdy fakt przechowywany jest tylko w jednym miejscu, ryzyko wprowadzenia niespójnych danych znacząco maleje. Ponadto odpowiednio znormalizowana baza danych ułatwia nakładanie więzów integralności, takich jak klucze obce czy ograniczenia CHECK, które dodatkowo zabezpieczają spójność informacji.

W praktyce produkcyjnej decyzje o denormalizacji powinny być zawsze poprzedzone dokładną analizą wydajnościową i profilowaniem zapytań. Denormalizacja bez zrozumienia normalizacji to najczęstsza przyczyna problemów z integralnością danych w systemach produkcyjnych, których naprawa jest znacznie droższa niż poprawne zaprojektowanie schematu na początku projektu.

15/60Nasz przykład: system biblioteczny

Scenariusz na cały cykl normalizacji

Projektujemy system do zarządzania biblioteką. Chcemy przechowywać:

  • Czytelnikach – imię, nazwisko, adres, telefon, email
  • Książkach – tytuł, autor, ISBN, gatunek, rok, wydawnictwo
  • Wypożyczeniach – kto, co i kiedy wypożyczył

Na początek: jedna wielka tabela zawierająca wszystko. W kolejnych prezentacjach będziemy stopniowo normalizować te dane.

Przez cały cykl normalizacji będziemy pracować na tym samym zestawie danych – zobaczysz, jak ewoluuje od chaosu do perfekcyjnego porządku.
Schemat – jedna tabela Wypozyczenia z 17 kolumnami, obok docelowy podział na 3+ tabele

Wybór systemu bibliotecznego jako przykładu do całego cyklu normalizacji nie jest przypadkowy. System biblioteczny zawiera w sobie wszystkie typowe elementy występujące w rzeczywistych aplikacjach biznesowych: encje osobowe (czytelnicy), rzeczowe (książki) oraz zdarzeniowe (wypożyczenia). Taka różnorodność typów encji pozwala zilustrować wszystkie istotne koncepcje normalizacji na jednym, spójnym przykładzie.

Dzięki temu, że przez cały cykl prezentacji pracujemy na tych samych danych, możesz skupić się wyłącznie na zmianach strukturalnych, nie tracąc czasu na poznawanie nowego kontekstu biznesowego przy każdym przejściu do kolejnej postaci normalnej. Zobaczysz, jak ta sama początkowa tabela ewoluuje od jednej, chaotycznej struktury do zestawu wyspecjalizowanych, dobrze zaprojektowanych tabel.

W realnym projekcie proces normalizacji rzadko przebiega tak liniowo, jak w przykładzie dydaktycznym. Projektant bazy danych musi uwzględniać wiele czynników, takich jak przewidywane zapytania, wydajność, czy wymagania dotyczące bezpieczeństwa. Niemniej jednak znajomość reguł normalizacji stanowi niezbędną podstawę do podejmowania świadomych decyzji projektowych.

16/60Czego nauczysz się w tej prezentacji?

Cele dydaktyczne

  • Czym jest 1NF i jakie są jej trzy złote zasady
  • Jak rozpoznać naruszenia 1NF w istniejących tabelach
  • Jak przekształcić złą tabelę do 1NF
  • Dlaczego 1NF to dopiero początek normalizacji
  • Jak praktycznie implementować 1NF w MariaDB
Po tej prezentacji będziesz umiał rozpoznać podstawowe problemy w strukturze tabel – to jak nauka alfabetu przed pisaniem wypracowań.
Mapa celów – ikony: lupa (analiza), klucz (klucz główny), tabela (struktura), baza danych (implementacja)

Ta prezentacja stanowi pierwszą część większego cyklu dydaktycznego poświęconego normalizacji baz danych. Po opanowaniu 1NF, którą dziś poznajesz, będziesz gotów do zgłębienia kolejnych postaci normalnych, z których każda eliminuje kolejne typy problemów projektowych. Wiedza zdobyta podczas tej prezentacji będzie Ci niezbędna do zrozumienia materiału w kolejnych odcinkach cyklu.

Cele dydaktyczne zostały sformułowane tak, aby po zakończeniu prezentacji potrafić nie tylko zdefiniować 1NF, ale przede wszystkim rozpoznać jej naruszenia w istniejących tabelach i samodzielnie przekształcić schemat do postaci zgodnej z 1NF. Umiejętność identyfikacji powtarzających się grup, naruszeń atomowości oraz brakującego klucza głównego to praktyczne kompetencje, które przydadzą Ci się w każdym projekcie bazodanowym.

Nie zniechęcaj się, jeśli na początku reguły 1NF wydadzą Ci się zbyt proste lub oczywiste. W trakcie pracy z rzeczywistymi danymi często okazuje się, że naruszenia pierwszej postaci normalnej są znacznie bardziej subtelne, niż mogłoby się wydawać. Praktyka i analiza konkretnych przypadków to najlepsza droga do osiągnięcia biegłości w normalizacji schematów relacyjnych baz danych.

17/60System biblioteczny – nasze dane wyjściowe

Tabela Wypozyczenia – 17 kolumn

Pracujemy na jednej tabeli Wypozyczenia:

KategoriaKolumny
IDID_Wypozyczenia
Dane czytelnikaCzytelnik, Adres, Miasto, KodPocztowy, Telefon, Email
Dane książkiTytul, Autor, ISBN, Gatunek, RokWydania, Wydawnictwo
Dane wypożyczeniaDataWyp, DataZwrotu, Kara, Status

Każdy wiersz to jedno wypożyczenie. Jeśli czytelnik wypożycza 3 książki → jego dane osobowe pojawiają się 3 razy.

Ta tabela łamie wszystkie reguły normalizacji – ale doskonale pokazuje, dlaczego normalizacja jest potrzebna.
Wizualizacja tabeli z 17 kolumnami i 12 wierszami – dane Jana Kowalskiego powtarzają się w dwóch wierszach

Analizując strukturę tabeli Wypozyczenia, łatwo zauważyć, że łączy ona w sobie trzy odrębne kategorie informacji: dane osobowe czytelnika, metadane książki oraz informacje o zdarzeniu wypożyczenia. W terminologii modelowania danych każda z tych kategorii stanowi odrębną encję, która w dobrze zaprojektowanej bazie powinna znaleźć się w osobnej tabeli. Wymieszanie tych encji w jednej tabeli prowadzi do wszystkich anomalii omawianych w dalszej części prezentacji.

Tabela zawiera siedemnaście kolumn, co samo w sobie nie jest problemem, ale gdy przeanalizujemy, które grupy kolumn są ze sobą powiązane, dostrzeżemy wyraźne struktury. Kolumny dotyczące czytelnika (Czytelnik, Adres, Miasto, KodPocztowy, Telefon, Email) są ze sobą silnie powiązane i wszystkie zależą wyłącznie od tożsamości czytelnika. Podobnie kolumny książki (Tytul, Autor, ISBN, Gatunek, RokWydania, Wydawnictwo) są od siebie wzajemnie zależne.

Z punktu widzenia projektanta baz danych taka struktura jest klasycznym przykładem anty-wzorca (anti-pattern) zwanego "one big table". Rozpoznawanie tego anty-wzorca w istniejących systemach to jedna z kluczowych umiejętności administratora baz danych, pozwalająca na szybką identyfikację obszarów wymagających refaktoryzacji schematu.

18/60Zdenormalizowana tabela – pełny widok danych

12 wypożyczeń, 5 czytelników, 8 książek

Przykładowe dane w tabeli Wypozyczenia:

IDCzytelnikMiastoTytulAutorStatus
1Jan KowalskiKrakówPrzedwiośnieStefan ŻeromskiZwrócone
2Jan KowalskiKrakówLalkaBolesław PrusWypożyczone
3Anna NowakWarszawaPan TadeuszAdam MickiewiczWypożyczone
4Piotr WiśniewskiGdańskLalkaBolesław PrusZwrócone
5Maria ZalewskaWrocławWiedza i Życie 01/2026-Wypożyczone
6Tomasz AdamskiPoznańBazy danychConnollyZwrócone
7Piotr WiśniewskiGdańskFerdydurkeGombrowiczOpóźnione
8Maria ZalewskaWrocławPrzedwiośnieStefan ŻeromskiZwrócone
9Tomasz AdamskiPoznańSQL dla początkującychWójtowiczWypożyczone
10Anna NowakWarszawaLalkaBolesław PrusOpóźnione
11Jan KowalskiKrakówQuo VadisHenryk SienkiewiczWypożyczone
12Piotr WiśniewskiGdańskPan TadeuszAdam MickiewiczZwrócone
To są dane, z którymi będziemy pracować przez cały cykl normalizacji – zapamiętaj je.
Tabela z danymi – 12 wierszy pokazujących wypożyczenia z różnymi statusami

Pełny widok danych w tabeli Wypozyczenia unaocznia skalę problemu redundancji. W dwunastu wierszach przechowujemy informacje o pięciu czytelnikach i ośmiu książkach, ale dane osobowe każdego czytelnika są powielane przy każdym jego wypożyczeniu. Jan Kowalski ma trzy wypożyczenia, więc jego dane (imię, nazwisko, adres, miasto, kod pocztowy, telefon, email) zapisane są trzykrotnie. To jest w skali mikro to, co w rzeczywistym systemie urasta do gigantycznych rozmiarów.

Przeanalizujmy konkretny wiersz: wypożyczenie numer 1. Zawiera ono nie tylko informacje o zdarzeniu (data wypożyczenia, data zwrotu, kara, status), ale także wszystkie dane czytelnika Jana Kowalskiego oraz szczegółowe dane książki Przedwiośnie. Gdybyśmy chcieli wyświetlić listę wypożyczeń, każde zapytanie SELECT musi przetworzyć wszystkie te kolumny, nawet jeśli potrzebujemy tylko kilku z nich. To negatywnie wpływa na wydajność zapytań.

Warto zwrócić uwagę na kolumnę Status, która przyjmuje wartości takie jak "Zwrócone", "Wypożyczone" czy "Opóźnione". W prawdziwym systemie wartości statusu byłyby przechowywane w osobnej tabeli słownikowej, co zapewniłoby spójność i uniemożliwiłoby wprowadzenie błędnych wartości, takich jak literówka "Opóżnione" zamiast poprawnego "Opóźnione".

19/60Problem 1: Anomalia INSERT

Nie można dodać danych, bo nie ma głównego zdarzenia

Anomalia INSERT – problem z dodawaniem nowych danych do tabeli, ponieważ każdy wiersz musi reprezentować konkretne zdarzenie (wypożyczenie).

Przykład:

Do biblioteki przychodzi nowy czytelnik – Maria Nowak. Nie można dodać Marii do tabeli, bo nie wypożyczyła jeszcze książki. Każdy wiersz to wypożyczenie.

Anomalia INSERT – aby dodać czytelnika, musisz najpierw wypożyczyć mu książkę. To jak 'najpierw dziecko, potem rodzice'.
Ilustracja – formularz dodawania nowego czytelnika, który wymaga podania wypożyczenia (paradoks)

Anomalia INSERT to problem, który w praktyce systemowej objawia się w sposób szczególnie frustrujący dla użytkowników. Wyobraź sobie bibliotekarkę, która chce zarejestrować nowego czytelnika w systemie, ale nie może tego zrobić, ponieważ nowy czytelnik nie wypożyczył jeszcze żadnej książki. System wymusza, aby każdy wiersz w tabeli Wypozyczenia reprezentował konkretne zdarzenie wypożyczenia, co uniemożliwia wprowadzenie danych czytelnika niezależnie od faktu wypożyczenia.

W terminologii baz danych mówimy, że tabela Wypozyczenia ma zbyt silne powiązanie pomiędzy encją czytelnika a encją zdarzenia. W modelu encja-zdarzenie (ang. entity-event) encje powinny być przechowywane niezależnie od zdarzeń, które ich dotyczą. Czytelnik może być zarejestrowany w systemie, ale nigdy nie wypożyczyć książki – taka sytuacja jest w pełni poprawna biznesowo, ale niemożliwa do zapisania w obecnej strukturze.

Rozwiązaniem jest dekompozycja tabeli na trzy osobne struktury: tabelę Czytelnicy (z kluczem głównym ID_Czytelnika), tabelę Ksiazki (z kluczem głównym ISBN lub ID_Ksiazki) oraz tabelę Wypozyczenia (z kluczami obcymi odwołującymi się do obu tych tabel). Taka struktura pozwala na dodanie czytelnika bez wypożyczenia oraz książki bez wypożyczenia, co całkowicie eliminuje anomalię INSERT.

20/60Problem 2: Anomalia UPDATE

Zmiana jednego faktu wymaga modyfikacji wielu wierszy

Anomalia UPDATE – problem z modyfikacją danych, gdy jeden fakt jest powtórzony w wielu wierszach.

Przykład:

Jan Kowalski przeprowadza się z Krakowa do Warszawy. Jego adres jest zapisany przy każdym wypożyczeniu (3 razy). Trzeba zaktualizować 3 wiersze.

Ryzyko: zaktualizujesz 2 wiersze, o trzecim zapomnisz → dane są niespójne.

SQL ilustrujący problem:

-- Ryzykowne: trzeba pamiętać o WSZYSTKICH wierszach
UPDATE Wypozyczenia
SET Miasto = 'Warszawa'
WHERE Czytelnik = 'Jan Kowalski';
-- Jeśli jest 20 wypożyczeń Jana → aktualizujesz 20 wierszy
Anomalia UPDATE – jeden fakt (adres) rozsiany po wielu wierszach to proszenie się o błąd.
Ilustracja – trzy wiersze z Janem Kowalskim, dwa zaktualizowane (Warszawa), jeden nie (Kraków) – niespójność

Anomalia UPDATE jest często nazywana "cichym zabójcą" integralności danych, ponieważ system nie ostrzega użytkownika o powstałej niespójności. Gdy pracownik biblioteki aktualizuje adres Jana Kowalskiego tylko w dwóch z trzech wierszy, baza danych nie zgłasza żadnego błędu – uważa, że wszystko jest w porządku. Niespójność zostaje wykryta dopiero wtedy, gdy ktoś zapyta o adres Jana i otrzyma różne odpowiedzi w zależności od tego, które wypożyczenie zostanie sprawdzone.

Skala problemu rośnie proporcjonalnie do liczby wypożyczeń przypadających na jednego czytelnika. W systemie uniwersyteckim, gdzie studenci wypożyczają dziesiątki książek w trakcie semestru, jeden czytelnik może mieć kilkadziesiąt wpisów w tabeli. Ryzyko pominięcia choć jednego wiersza przy aktualizacji adresu jest wtedy bardzo wysokie, szczególnie gdy aktualizacji dokonuje zestresowany pracownik biblioteki w godzinach szczytu.

Rozwiązaniem jest wyniesienie danych czytelnika do osobnej tabeli, gdzie adres przechowywany jest dokładnie jeden raz. Wtedy aktualizacja adresu wymaga modyfikacji jednego wiersza w tabeli Czytelnicy, a wszystkie wypożyczenia automatycznie "widzą" nowy adres poprzez relację klucza obcego. To nie tylko eliminuje ryzyko niespójności, ale także przyspiesza operację UPDATE i zmniejsza obciążenie serwera bazy danych.

21/60Problem 3: Anomalia DELETE

Usunięcie jednego faktu kasuje inne, ważne dane

Anomalia DELETE – problem z usuwaniem danych, gdy usunięcie wiersza kasuje więcej informacji niż zamierzaliśmy.

Przykład:

Jan Kowalski zwraca 'Przedwiośnie' – usuwamy to wypożyczenie. To było ostatnie wypożyczenie Jana → tracimy WSZYSTKIE dane o Janie!

Nie tylko informację o wypożyczeniu, ale też imię, nazwisko, adres, telefon, email.

Anomalia DELETE – chcesz usunąć stare wypożyczenie, a tracisz dane czytelnika. To jak wywalenie całego CV, bo zmieniłeś pracę.
Ilustracja – kosz na śmieci, w którym ląduje nie tylko wypożyczenie, ale też dane czytelnika i książki

Anomalia DELETE jest szczególnie podstępna, ponieważ użytkownik może nawet nie zdawać sobie sprawy z utraty danych. Gdy pracownik biblioteki usuwa ostatnie wypożyczenie czytelnika, system nie ostrzega, że wraz z tym wierszem usunięte zostaną również wszystkie dane osobowe tego czytelnika. Konsekwencje mogą być poważne: utrata historii wypożyczeń, danych kontaktowych, a w systemach medycznych czy finansowych – utrata krytycznych informacji o pacjencie lub kliencie.

W praktyce systemowej anomalia DELETE jest szczególnie niebezpieczna w połączeniu z automatycznymi procesami czyszczenia danych. Jeśli administrator skonfiguruje zadanie cron, które cyklicznie usuwa "stare" wypożyczenia, może nieświadomie doprowadzić do usunięcia wszystkich danych czytelników, którzy od dawna nie wypożyczali książek. Odbudowa takich danych z kopii zapasowej jest kosztowna i czasochłonna.

Mechanizmem zapobiegającym tej anomalii jest wydzielenie osobnych tabel dla każdej encji przy jednoczesnym zastosowaniu kluczy obcych z regułą ON DELETE RESTRICT lub ON DELETE CASCADE. RESTRICT uniemożliwia usunięcie czytelnika, który ma powiązane wypożyczenia, natomiast CASCADE automatycznie usuwa powiązane wypożyczenia przy usunięciu czytelnika. Wybór odpowiedniej reguły zależy od wymagań biznesowych systemu i powinien być starannie przemyślany na etapie projektowania.

22/60Problem 4: Redundancja (nadmiarowość) danych

Te same dane w wielu miejscach

Redundancja to wielokrotne przechowywanie tych samych danych w różnych wierszach tej samej tabeli.

W naszej tabeli:

  • Dane Jana Kowalskiego (adres, telefon, email) powtórzone przy każdym jego wypożyczeniu
  • Dane książki 'Lalka' powtórzone przy każdym wypożyczeniu

Skutki redundancji:

  • Marnotrawstwo miejsca – baza zajmuje więcej pamięci
  • Ryzyko błędów – przy aktualizacji łatwo coś pominąć
  • Niespójność – różne wiersze mogą mieć różne wersje tych samych danych
Redundancja to nie tylko marnowanie miejsca – to przede wszystkim ryzyko, że dane się 'rozjadą'.
Ilustracja – trzy bloki danych, każdy zawiera te same informacje o czytelniku, tylko inne książki

Redundancja danych, czyli wielokrotne przechowywanie tych samych informacji, jest często postrzegana wyłącznie przez pryzmat marnowania przestrzeni dyskowej. W dzisiejszych czasach, gdy ceny dysków twardych są relatywnie niskie, argument o marnowaniu miejsca stracił na znaczeniu. Jednak głównym problemem redundancji nie jest przestrzeń dyskowa, lecz ryzyko wprowadzenia niespójności danych, które może prowadzić do błędnych raportów i decyzji biznesowych opartych na nieaktualnych informacjach.

W naszej tabeli bibliotecznej redundancja objawia się wielokrotnym przechowywaniem adresu, telefonu i emaila każdego czytelnika przy każdym jego wypożyczeniu. W skali całego systemu, zakładając dziesięć tysięcy czytelników i średnio dziesięć wypożyczeń na osobę, nadmiarowość generuje około stu pięćdziesięciu tysięcy dodatkowych komórek danych, które są niczym innym jak kopiami tych samych informacji. Każda z tych kopii jest potencjalnym źródłem błędu przy aktualizacji.

Co więcej, redundancja utrudnia utrzymanie spójności danych również w kontekście integralności referencyjnej. Jeśli ten sam czytelnik pojawia się w dwóch wierszach z różnymi adresami, która wersja jest poprawna? System nie ma mechanizmu, aby to rozstrzygnąć. W normalizacji dążymy do zasady Jeden Fakt – Jedno Miejsce (ang. One Fact – One Place), która gwarantuje, że każda informacja przechowywana jest dokładnie raz i wiadomo, gdzie jej szukać.

23/60Tabela porównawcza – ile razy powtarza się każdy fakt?

Skala redundancji w naszej tabeli

FaktLiczba wystąpieńPowinno być
Adres Jana Kowalskiego31
Tytuł 'Przedwiośnie'21
Autor 'Lalki' (Bolesław Prus)21
Kod pocztowy Krakowa (30-001)31
Numer telefonu Jana31
Adres Piotra Wiśniewskiego31

Im więcej wypożyczeń, tym więcej powtórzeń. W realnym systemie jeden czytelnik może mieć 100+ wypożyczeń.

W dobrze zaprojektowanej bazie każdy fakt przechowujesz DOKŁADNIE RAZ.
Wykres słupkowy – porównanie liczby wystąpień każdego faktu (aktualna vs powinno być)

Tabela porównawcza, którą widzisz na slajdzie, w przejrzysty sposób ilustruje skalę redundancji w naszym zbiorze danych. Każdy fakt dotyczący czytelnika – jego adres, miasto, kod pocztowy, telefon czy email – jest powtarzany tyle razy, ile wypożyczeń ma dany czytelnik. Dla Jana Kowalskiego z trzema wypożyczeniami każdy z tych faktów występuje trzykrotnie, co daje łącznie osiemnaście nadmiarowych komórek tylko dla jednego czytelnika.

W rzeczywistym systemie bibliotecznym obsługującym uczelnię wyższą z pięćdziesięcioma tysiącami studentów skala problemu jest ogromna. Jeśli każdy student ma średnio pięć wypożyczeń, to nadmiarowe dane osobowe zajmują około dwustu pięćdziesięciu tysięcy dodatkowych wierszy. Przy siedemnastu kolumnach w tabeli daje to miliony nadmiarowych komórek danych, które są nie tylko marnowaniem miejsca, ale przede wszystkim źródłem potencjalnych błędów.

Szczególnie interesujący jest przypadek kodów pocztowych, które są takie same dla wszystkich czytelników mieszkających w tym samym mieście. W naszej tabeli kod pocztowy Krakowa (30-001) powtarza się trzy razy dla samego Jana Kowalskiego. Gdyby kod pocztowy dla Krakowa uległ zmianie, konieczna byłaby aktualizacja wszystkich wierszy wszystkich czytelników z Krakowa – to setki tysięcy wierszy w rzeczywistym systemie, co stanowi operację niezwykle kosztowną i ryzykowną.

24/60Podsumowanie problemów – czego chcemy uniknąć?

Cztery rodzaje problemów do rozwiązania

ProblemOpisPrzykład
Anomalia INSERTNie można dodać danychNowy czytelnik bez wypożyczenia
Anomalia UPDATEZmiana wymaga wielu modyfikacjiPrzeprowadzka Jana = 3 aktualizacje
Anomalia DELETEUsunięcie kasuje inne daneUsunięcie wypożyczenia = utrata czytelnika
RedundancjaTe same dane w wielu miejscachAdres Jana w 3 wierszach

Cel normalizacji: wyeliminować te problemy krok po kroku. 1NF rozwiązuje pierwszy zestaw problemów.

Zapamiętaj te cztery problemy – każda postać normalna eliminuje jeden ich rodzaj.
Schemat – cztery ikony problemów (INSERT, UPDATE, DELETE, redundancja) z przekreśleniami

Tabela podsumowująca cztery rodzaje problemów stanowi mapę drogową dla całego procesu normalizacji. Każdy z tych problemów – anomalia INSERT, anomalia UPDATE, anomalia DELETE oraz redundancja – zostanie wyeliminowany na odpowiednim etapie normalizacji. Warto już teraz zapamiętać, że 1NF rozwiązuje problemy strukturalne na poziomie pojedynczej komórki i pojedynczej tabeli, ale nie eliminuje problemów wynikających z mieszania różnych encji w jednej tabeli.

Anomalia INSERT zostanie rozwiązana w 2NF poprzez wydzielenie osobnych tabel dla czytelników i książek. Anomalia UPDATE i redundancja związana z danymi czytelników i książek również zostaną wyeliminowane w 2NF. Anomalia DELETE zostanie rozwiązana dopiero w 3NF lub BCNF, w zależności od konkretnej struktury zależności funkcyjnych. Redundancja na poziomie zależności wielowartościowych będzie eliminowana w 4NF i 5NF.

Świadomość, które problemy są rozwiązywane na którym etapie normalizacji, pozwala projektantowi podejmować świadome decyzje o tym, jak daleko w procesie normalizacji powinien się posunąć. Dla wielu systemów biznesowych osiągnięcie 3NF jest wystarczające, ponieważ eliminuje ono większość praktycznych problemów z integralnością danych przy zachowaniu akceptowalnej wydajności zapytań. Decyzja o dalszej normalizacji powinna być podyktowana konkretnymi wymaganiami analitycznymi systemu.

25/60Co to jest 1NF? Definicja formalna

First Normal Form – Pierwsza Postać Normalna

Definicja: relacja (tabela) jest w 1NF wtedy i tylko wtedy, gdy każdy atrybut (kolumna) zawiera wyłącznie atomowe (niepodzielne) wartości.

Trzy złote zasady 1NF:

  • Zasada 1: Atomowość – każda komórka zawiera dokładnie jedną wartość
  • Zasada 2: Brak powtarzających się grup – nie ma kolumn typu Autor1, Autor2, Autor3
  • Zasada 3: Klucz główny – każdy wiersz musi być unikalnie identyfikowalny
1NF to najprostsza i najbardziej fundamentalna reguła – dane muszą być 'płaskie'.
Trzy ikony: atom (atomowość), grupa z przekreśleniem (brak grup), klucz (klucz główny)

Definicja pierwszej postaci normalnej wydaje się prosta, ale jej implementacja w praktyce wymaga zrozumienia kilku subtelności. Po pierwsze, atomowość nie oznacza, że wartość musi być "jak najmniejsza" w sensie fizycznym – chodzi o to, aby wartość była niepodzielna z punktu widzenia logiki zapytań, które będziemy do tej tabeli kierować. Przykładowo, adres zapisany jako "ul. Polna 10, Kraków, 30-001" jest nieatomowy, ponieważ w zapytaniach często potrzebujemy odrębnie miasta lub kodu pocztowego.

Druga zasada dotycząca powtarzających się grup kolumn jest często mylona z zasadą atomowości. Powtarzająca się grupa to sytuacja, w której projektant tworzy wiele kolumn o tym samym znaczeniu, różniących się tylko indeksem numerycznym – na przykład Autor1, Autor2, Autor3. To fundamentalnie inny problem niż lista wartości w pojedynczej komórce, choć oba naruszają 1NF. Rozwiązaniem w obu przypadkach jest przeniesienie wartości do osobnych wierszy.

Trzecia zasada dotycząca klucza głównego jest często pomijana przez początkujących projektantów, co prowadzi do poważnych problemów z integralnością danych. Klucz główny jest nie tylko wymogiem 1NF, ale także podstawą mechanizmu kluczy obcych, które umożliwiają budowanie relacji pomiędzy tabelami. Bez kluczy głównych niemożliwe jest łączenie tabel za pomocą JOIN, co stanowi jedną z najważniejszych operacji w języku SQL.

26/60Zasada 1: Atomowość – każda komórka to jedna wartość

Niepodzielność wartości

Atomowość (ang. atomicity) oznacza, że każda komórka na przecięciu wiersza i kolumny zawiera DOKŁADNIE jedną wartość.

Czego NIE robić:

  • "Sienkiewicz, Mickiewicz, Prus" w jednej kolumnie Autor
  • "ul. Główna 15, Kraków, 30-001" w jednej kolumnie Adres
  • "123-456-789, 987-654-321" w jednej kolumnie Telefon
Jeśli w komórce widzisz przecinek – prawdopodobnie łamiesz 1NF.
Ilustracja – komórka z listą 'A, B, C' przekreślona, obok trzy oddzielne komórki z pojedynczymi wartościami

Zasada atomowości w praktyce projektowej wymaga od projektanta przewidzenia, jakie zapytania będą najczęściej wykonywane na danej tabeli. Jeśli w systemie bibliotecznym często szukamy czytelników według miasta zamieszkania, to miasto powinno być wydzielone jako osobna kolumna, a nie stanowić część złożonego atrybutu Adres. Analogicznie, jeśli potrzebujemy filtrować według kodu pocztowego, ten również powinien być przechowywany osobno.

Warto zwrócić uwagę na pewną subiektywność w ocenie atomowości. To, co jest atomowe w jednym systemie, może nie być atomowe w innym. Na przykład w systemie przechowującym dane osobowe imię i nazwisko mogą być przechowywane w osobnych kolumnach, ponieważ często potrzebujemy sortować według nazwiska. W systemie, w którym zawsze wyświetlamy pełne imię i nazwisko razem, przechowywanie ich w osobnych kolumnach może być nieuzasadnione.

Decyzja o podziale wartości na kolumny powinna być podyktowana analizą wymagań biznesowych i przewidywanych zapytań. Nie ma jednego uniwersalnego poziomu atomowości – to, co jest odpowiednie dla jednego systemu, może być nieadekwatne dla innego. Dobrą praktyką jest wyjście od bardziej szczegółowego podziału, ponieważ scalenie kolumn jest zawsze łatwiejsze niż ich późniejszy podział w istniejącej bazie danych.

27/60Zasada 1 – przykłady poprawne i niepoprawne

Tabela porównawcza: złe vs dobre praktyki

Złe (nie 1NF)Dobre (1NF)
Autor: "Mickiewicz, Słowacki"Autor: "Mickiewicz" (oddzielny wiersz dla Słowackiego)
Adres: "ul. Polna 10, Kraków, 30-001"Adres: "ul. Polna 10", Miasto: "Kraków", KodPocztowy: "30-001"
Telefony: "123-456-789, 987-654-321"Osobne wiersze dla każdego telefonu
Oceny: "5, 4, 3, 5"Osobne wiersze dla każdej oceny

Zasada: jeśli możesz podzielić wartość na mniejsze części, które mają znaczenie – podziel.

Jeśli wartość w komórce zawiera listę – to znak, że łamiesz pierwszą zasadę 1NF.
Dwie tabele obok siebie – lewa z naruszeniami (lista w komórce), prawa poprawna (osobne wartości)

Tabela porównawcza poprawnych i niepoprawnych przykładów unaocznia, jak wiele powszechnych praktyk przechowywania danych narusza zasadę atomowości. Kolumna Autor zawierająca "Mickiewicz, Słowacki" jest klasycznym przykładem naruszenia – w rzeczywistych systemach takie przechowywanie danych często wynika z lenistwa projektanta lub chęci szybkiego osiągnięcia efektu bez przemyślenia konsekwencji dla przyszłych zapytań.

Konsekwencje braku atomowości są szczególnie widoczne w kontekście wydajności zapytań SQL. Zapytanie z klauzulą WHERE Autor = 'Mickiewicz' działa natychmiastowo, jeśli w każdej komórce znajduje się dokładnie jeden autor. W przypadku przechowywania listy autorów w jednej komórce konieczne jest użycie WHERE Autor LIKE '%Mickiewicz%', które nie może skorzystać z indeksów i wymaga sekwencyjnego przeszukania całej tabeli. W tabelach zawierających miliony wierszy różnica w czasie wykonania może być dramatyczna.

Podobny problem występuje w przypadku adresów. Jeśli adres jest przechowywany jako jeden tekst, zapytanie o wszystkich czytelników z Krakowa wymaga użycia WHERE Adres LIKE '%Kraków%'. Gdybyśmy chcieli dodatkowo sprawdzić, którzy czytelnicy mieszkają na ulicy Polnej, zapytanie staje się jeszcze bardziej skomplikowane i podatne na błędy. Wydzielenie poszczególnych składników adresu do osobnych kolumn rozwiązuje ten problem w elegancki i wydajny sposób.

28/60Zasada 2: Brak powtarzających się grup kolumn

Kolumny z numerami to zły znak

Definicja: kolumny o tej samej semantyce ponumerowane – to powtarzająca się grupa. Przykład: Autor1, Autor2, Autor3.

Problemy:

  • Ile kolumn przygotować? 3? 10? 20? Zawsze za mało
  • Co z książkami z 4 autorami? Nie mieszczą się
  • Co z książkami z 1 autorem? 2 kolumny puste

Rozwiązanie:

Osobne wiersze dla każdej wartości. Zamiast Autor1-Autor3 → jeden wiersz na autora.

Powtarzające się grupy to jak projektowanie mieszkania z 'sypialnia1, sypialnia2, sypialnia3' – nie wiesz, ile potrzeba.
Ilustracja – tabela z kolumnami Autor1, Autor2, Autor3 przekreślona, obok poprawna struktura

Powtarzające się grupy kolumn to jeden z najbardziej charakterystycznych błędów popełnianych przez początkujących projektantów baz danych. Błąd ten wynika z myślenia w kategoriach formularzy papierowych, gdzie często przygotowuje się kilka pustych pól na dodatkowe informacje. Przeniesienie tego myślenia do projektu bazy danych prowadzi do powstania kolumn takich jak Autor1, Autor2, Autor3, które są nie tylko nieeleganckie, ale przede wszystkim niefunkcjonalne.

Główny problem z powtarzającymi się grupami polega na tym, że nigdy nie wiadomo, ile takich kolumn przygotować. Jeśli zaprojektujemy trzy kolumny na autorów, to książka z czterema autorami nie zmieści się w strukturze. Z drugiej strony, jeśli większość książek ma tylko jednego autora, dwie dodatkowe kolumny pozostaną puste, marnując miejsce i komplikując zapytania. W SQL zapytanie, które ma znaleźć książkę danego autora, musi sprawdzić wszystkie trzy kolumny: WHERE Autor1 = 'X' OR Autor2 = 'X' OR Autor3 = 'X'.

W 1NF rozwiązanie tego problemu polega na przekształceniu grup powtarzalnych na dodatkowe wiersze. Zamiast kolumn Autor1, Autor2, Autor3 tworzymy wiele wierszy dla tej samej książki, każdy z jednym autorem. To rozwiązanie, choć wprowadza redundancję innych danych książki, jest akceptowalne w 1NF i zostanie udoskonalone w 2NF poprzez wydzielenie osobnej tabeli dla autorów. Taka struktura umożliwia eleganckie zapytanie: WHERE Autor = 'X', które działa dla dowolnej liczby autorów.

29/60Zasada 2 – więcej przykładów

Jak rozpoznać powtarzające się grupy?

Jeśli kolumny różnią się tylko numerem na końcu nazwy – to prawdopodobnie powtarzająca się grupa.

Przykłady:

  • Tel1, Tel2, Tel3 dla numerów telefonów – ŹLE
  • Ocena1, Ocena2, Ocena3 dla ocen studenta – ŹLE
  • AdresZameldowania, AdresZamieszkania – OK (różne znaczenie)
Jeśli dodajesz numer do nazwy kolumny (Kolumna1, Kolumna2) – zatrzymaj się. To prawdopodobnie błąd projektu.
Lista kolumn: Tel1, Tel2, Tel3 przekreślona, obok poprawny model z osobną tabelą Telefony

Rozpoznawanie powtarzających się grup wymaga wyrobienia w sobie nawyku analitycznego podejścia do nazewnictwa kolumn. Podstawowym testem jest zadanie sobie pytania, czy kolumny o numerowanych nazwach reprezentują ten sam typ informacji. Jeśli kolumny nazywają się Telefon1, Telefon2, Telefon3, to wszystkie trzy reprezentują "numer telefonu" – a zatem powinny być modelowane jako osobne wiersze w tabeli powiązanej, a nie jako osobne kolumny w tej samej tabeli.

Warto jednak zwrócić uwagę na subtelny wyjątek od tej reguły. Jeśli kolumny mają podobne nazwy, ale reprezentują różne rodzaje informacji, nie stanowią powtarzającej się grupy. Przykładem może być AdresZameldowania i AdresZamieszkania, które obie przechowują adres, ale mają odmienne znaczenie biznesowe. Podobnie TelefonDomowy i TelefonKomorkowy to dwie różne kategorie telefonu, które mogą uzasadniać osobne kolumny, ponieważ w zapytaniach często odnosimy się do konkretnego typu telefonu.

Kolejnym pomocnym testem jest wyobrażenie sobie, jak będzie wyglądać zapytanie SQL. Jeśli do znalezienia informacji potrzebujesz warunku OR łączącego wiele kolumn, to prawdopodobnie masz do czynienia z powtarzającą się grupą. Natomiast jeśli potrzebujesz warunku AND na różnych kolumnach lub odwołujesz się do konkretnej kolumny po nazwie, osobne kolumny są uzasadnione. Dobrą praktyką jest konsultacja takich decyzji z analitykiem biznesowym, który pomoże określić, jak dane będą faktycznie wykorzystywane.

30/60Zasada 3: Jednoznaczny klucz główny

Każdy wiersz musi być unikalnie identyfikowalny

W 1NF każdy wiersz w tabeli musi być unikalnie identyfikowalny za pomocą klucza głównego.

Rodzaje kluczy:

  • Klucz prosty – pojedyncza kolumna, np. ID_Wypozyczenia
  • Klucz złożony – wiele kolumn, np. (Czytelnik, ISBN, Data)

Własności:

  • Unikalność – nie ma dwóch wierszy z tą samą wartością
  • Niezmienność – wartość nie powinna się zmieniać
  • Minimalność – najmniejszy możliwy zestaw kolumn
Bez klucza głównego nie możesz wskazać konkretnego wiersza – to jak dom bez adresu.
Mapa miasta z adresami (domy ponumerowane) jako analogia klucza głównego

Zasada trzecia 1NF, dotycząca klucza głównego, jest fundamentalna dla całego modelu relacyjnego. Klucz główny nie tylko zapewnia unikalność wierszy, ale także stanowi podstawę dla mechanizmu kluczy obcych, które umożliwiają budowanie relacji między tabelami. Bez kluczy głównych niemożliwe jest zastosowanie integralności referencyjnej, a co za tym idzie – zapewnienie spójności danych w systemie zawierającym wiele powiązanych tabel.

Wybór odpowiedniego typu klucza głównego jest decyzją projektową o dalekosiężnych konsekwencjach. Klucze naturalne, takie jak numer PESEL czy ISBN, mają tę zaletę, że są znaczące i zrozumiałe dla użytkownika. Jednak są one podatne na zmiany – numer ISBN książki może ulec zmianie przy wznowieniu, a PESEL jest daną wrażliwą. Z tych powodów w praktyce produkcyjnej dominują sztuczne klucze numeryczne, które są generowane automatycznie przez DBMS i nie niosą żadnego znaczenia biznesowego.

W MariaDB mechanizm AUTO_INCREMENT automatycznie nadaje kolejne wartości liczbowe dla każdego nowego wiersza, gwarantując unikalność nawet przy równoczesnym dostępie wielu użytkowników. Istnieje również możliwość użycia typu UUID jako klucza głównego, co daje gwarancję globalnej unikalności kosztem większego rozmiaru indeksu i niższej wydajności przy łączeniu tabel. Wybór między INTEGER a UUID zależy od architektury systemu – w systemach rozproszonych UUID jest często preferowany, w klasycznych aplikacjach webowych INTEGER pozostaje standardem.

31/60Przykład tabeli z kluczem głównym i bez

Dlaczego klucz główny jest konieczny?

Tabela BEZ klucza głównego:

CzytelnikTytułData wypożyczenia
Jan KowalskiPrzedwiośnie2026-02-01
Jan KowalskiPrzedwiośnie2026-02-01

DWA IDENTYCZNE WIERSZE! Nie wiadomo, czy to błąd, czy dwa osobne wypożyczenia.

Tabela Z kluczem głównym:

IDCzytelnikTytułData wypożyczenia
1Jan KowalskiPrzedwiośnie2026-02-01
2Jan KowalskiPrzedwiośnie2026-02-01

Różne ID – to dwa różne wypożyczenia.

Klucz główny to jak numer PESEL – gwarantuje, że nikt inny nie ma takiego samego.
Dwie tabele obok siebie – lewa bez klucza (duplikat), prawa z kluczem (ID rozróżnia wiersze)

Przykład tabeli z duplikującymi się wierszami unaocznia fundamentalny problem braku klucza głównego. Gdy dwa wiersze zawierają identyczne wartości we wszystkich kolumnach, system nie ma możliwości stwierdzenia, czy są to dwa odrębne zdarzenia, czy też ten sam wiersz został przypadkowo wprowadzony dwukrotnie. W kontekście systemu bibliotecznego może to oznaczać, że ta sama książka została wypożyczona dwukrotnie, co prowadzi do błędów w raportach i naliczaniu kar.

Wprowadzenie sztucznego klucza głównego ID rozwiązuje ten problem w elegancki sposób. Nawet jeśli wszystkie pozostałe wartości w dwóch wierszach są identyczne, różne wartości ID informują system, że są to dwa odrębne wiersze. Mechanizm AUTO_INCREMENT w MariaDB dodatkowo zabezpiecza przed pomyłkami, automatycznie nadając każdemu nowemu wierszowi unikalną, rosnącą wartość liczbową, bez konieczności ręcznego wprowadzania numerów.

W projektowaniu baz danych istnieje zasada, że każda tabela powinna mieć klucz główny, a klucz ten powinien być stabilny (niezmienny w czasie) i minimalny (składający się z jak najmniejszej liczby kolumn). Sztuczny klucz INTEGER spełnia oba te kryteria: wartość raz nadana nie ulega zmianie, a pojedyncza kolumna liczbowa jest minimalna zarówno pod względem liczby kolumn, jak i rozmiaru w pamięci. To sprawia, że jest to najczęściej wybierane rozwiązanie w praktyce produkcyjnej.

32/60Co NIE jest 1NF – przykłady naruszeń

Podsumowanie typowych naruszeń 1NF

NaruszeniePrzykładKtóra zasada
Wiele wartości w komórceTelefony: "123-456-789, 987-654-321"Zasada 1 (atomowość)
Powtarzająca się grupaAutor1, Autor2, Autor3Zasada 2 (grupy)
Brak klucza głównegoDwa identyczne wierszeZasada 3 (klucz)
Dane złożoneAdres: "ul. Polna 10, Kraków, 30-001"Zasada 1 (atomowość)
JSON/XML w kolumnieDane: '{"wiek": 25}'Zasada 1 (atomowość)
Lista w komórceOceny: "5,4,3,5"Zasada 1 (atomowość)
W 1NF nie ma miejsca na 'sprytne' struktury – każda komórka to dokładnie jedna wartość.
Tabela naruszeń – każdy wiersz to inny przykład błędu

Tabela zbiorcza typowych naruszeń 1NF stanowi praktyczne narzędzie, które możesz wykorzystać podczas audytu istniejących baz danych. Każde z naruszeń opisanych w tabeli jest łatwe do wychwycenia podczas przeglądu struktury tabel, jeśli tylko wiesz, na co zwracać uwagę. W praktyce inżynierskiej warto przeprowadzać taki audyt regularnie, szczególnie w systemach rozwijanych przez wiele lat przez różnych programistów, gdzie naruszenia 1NF pojawiają się najczęściej.

Szczególną uwagę należy zwrócić na przechowywanie danych w formacie JSON lub XML w kolumnach tekstowych. Choć nowoczesne bazy danych, w tym MariaDB, oferują specjalne typy danych dla JSON, przechowywanie złożonych struktur w jednej kolumnie narusza zasadę atomowości 1NF. W ścisłej interpretacji 1NF wartość w komórce powinna być niepodzielna – JSON jako format zawierający wiele zagnieżdżonych wartości ewidentnie narusza tę zasadę. W praktyce jednak wiele systemów używa kolumn JSON do przechowywania elastycznych danych konfiguracyjnych, co jest akceptowalnym kompromisem.

Innym częstym naruszeniem jest przechowywanie w jednej kolumnie danych złożonych, takich jak adres w formacie "ul. Polna 10, Kraków, 30-001". Nawet jeśli na pierwszy rzut oka wygląda to jak jedna wartość, w rzeczywistości zawiera trzy odrębne informacje: ulicę, miasto i kod pocztowy. Podobne naruszenie występuje przy przechowywaniu daty i godziny w jednej kolumnie tekstowej zamiast użycia dedykowanego typu DATETIME, który zapewnia walidację poprawności daty.

33/60Dlaczego 1NF jest ważna?

Trzy główne powody

1. Umożliwia proste zapytania SQL

  • WHERE Autor = 'Mickiewicz' – działa, bo w każdej komórce jeden autor
  • Gdyby było 'Mickiewicz, Słowacki' – trzeba by używać LIKE

2. Zapewnia integralność danych

  • Każdy fakt ma swoje miejsce i typ
  • Nie można przypadkiem dodać dwóch typów do jednej kolumny

3. Jest warunkiem wstępnym dalszej normalizacji

Bez 1NF nie ma 2NF, 3NF, ani żadnej dalszej postaci normalnej.

1NF to jak fundament domu – nie musisz go widzieć, ale bez niego wszystko się zawali.
Piramida: 1NF u podstawy, na niej 2NF, 3NF, BCNF... – każda wspiera się na poprzedniej

Znaczenie pierwszej postaci normalnej w praktyce projektowania baz danych jest często niedoceniane przez początkujących programistów. Wiele osób uważa, że 1NF to trywialna reguła, która nie ma większego wpływu na działanie systemu. Tymczasem 1NF stanowi fundament, na którym opierają się wszystkie zaawansowane mechanizmy relacyjnych baz danych, takie jak indeksowanie, optymalizacja zapytań czy integralność referencyjna. Bez 1NF te mechanizmy nie mogą działać poprawnie lub ich skuteczność jest znacznie ograniczona.

Indeksy w bazach danych są strukturami przyspieszającymi wyszukiwanie, ale działają one optymalnie tylko wtedy, gdy indeksowana kolumna zawiera atomowe wartości. Indeks na kolumnie przechowującej listę autorów oddzielonych przecinkami byłby praktycznie bezużyteczny, ponieważ zapytanie LIKE '%Mickiewicz%' nie może efektywnie korzystać z indeksu. Podobnie łączenie tabel za pomocą JOIN wymaga, aby kolumny używane w warunku łączenia zawierały pojedyncze, porównywalne wartości – nie listy czy struktury złożone.

Więzy integralności, takie jak klucze obce czy ograniczenia CHECK, również wymagają 1NF. Klucz obcy odwołuje się do konkretnej kolumny w innej tabeli i zakłada, że każda wartość w tej kolumnie jest pojedyncza i jednoznaczna. Jeśli kolumna zawierałaby listę wartości, mechanizm klucza obcego nie mógłby działać. Dlatego też 1NF jest warunkiem koniecznym, aby w ogóle mówić o relacyjnym modelu danych w czystej postaci.

34/60Wracamy do naszej tabeli bibliotecznej

Sprawdźmy krok po kroku, czy jest w 1NF

Mamy tabelę Wypozyczenia z 17 kolumnami.

Plan kontroli:

  • Krok 1: Sprawdzenie atomowości (Zasada 1)
  • Krok 2: Sprawdzenie powtarzających się grup (Zasada 2)
  • Krok 3: Sprawdzenie klucza głównego (Zasada 3)
Weź tabelę biblioteczną i sprawdźmy każdą zasadę 1NF po kolei – jak lista kontrolna przed odlotem.
Lista kontrolna z trzema punktami do odhaczenia – atomowość, brak grup, klucz główny

Powrót do tabeli bibliotecznej po zapoznaniu się z definicją 1NF ma na celu praktyczne zastosowanie zdobytej wiedzy w konkretnym przypadku. Proces weryfikacji zgodności tabeli z 1NF przebiega według ustalonej procedury, którą warto stosować systematycznie w każdym projekcie. Procedura ta składa się z trzech kroków odpowiadających trzem złotym zasadom 1NF i powinna być wykonywana dla każdej tabeli w projektowanej bazie danych.

Przed rozpoczęciem weryfikacji warto przygotować listę wszystkich kolumn w tabeli wraz z przykładowymi danymi, co ułatwi ocenę atomowości. Należy również sprawdzić, czy tabela posiada zdefiniowany klucz główny oraz czy wartości w kolumnach klucza są unikalne. W MariaDB można to zrobić za pomocą polecenia SHOW CREATE TABLE, które wyświetla pełną definicję tabeli, w tym wszystkie ograniczenia i indeksy.

Systematyczne podejście do weryfikacji 1NF jest szczególnie ważne w projektach, gdzie baza danych jest rozwijana iteracyjnie. Często zdarza się, że w trakcie rozwoju projektu dodawane są nowe kolumny bez zachowania zasad normalizacji, co z czasem prowadzi do degradacji jakości schematu. Regularny audyt zgodności z 1NF pozwala wychwycić takie problemy na wczesnym etapie, zanim zdążą one wpłynąć na wydajność i integralność danych.

35/60Krok 1: Sprawdzenie atomowości (Zasada 1)

Analiza każdej kolumny

KolumnaWartośćWynik
ID_Wypozyczeniapojedyncza liczba✔ OK
Czytelnik"Jan Kowalski" – jedna osoba✔ OK
Adres"ul. Polna 10" – jeden adres✔ OK
Miasto"Kraków" – jedno miasto✔ OK
KodPocztowy"30-001" – jeden kod✔ OK
Telefon"123-456-789" – jeden numer✔ OK
Email"jan@example.com" – jeden email✔ OK
Tytul, Autor, ISBN...pojedyncze wartości✔ OK

Wniosek: nasza tabela ma atomowe wartości – każda komórka zawiera dokładnie jedną wartość.

W naszym przykładzie atomowość jest zachowana – każda komórka zawiera dokładnie jedną wartość.
Tabela z lupą – kolumna po kolumnie sprawdzana pod kątem atomowości, wszystkie zielone znaczniki OK

Sprawdzenie atomowości w naszej tabeli bibliotecznej wykazuje, że każda kolumna zawiera dokładnie jedną wartość, co oznacza spełnienie pierwszego warunku 1NF. Proces weryfikacji polegał na przeanalizowaniu każdej kolumny pod kątem tego, czy wartość w komórce może być podzielona na mniejsze, znaczące jednostki. Kolumna Czytelnik zawiera "Jan Kowalski" – jest to jedna osoba, a nie lista osób, zatem jest atomowa. Kolejne kolumny, takie jak Miasto ("Kraków"), Telefon ("123-456-789"), również przechowują pojedyncze wartości.

Szczególnie interesująca jest kolumna Adres, która w naszej strukturze jest już podzielona na osobne składowe: Adres, Miasto, KodPocztowy. To prawidłowa decyzja projektowa, ponieważ każdy z tych składników ma odrębne znaczenie w zapytaniach. Gdybyśmy chcieli znaleźć wszystkich czytelników z kodem pocztowym zaczynającym się od "30", moglibyśmy to zrobić bezpośrednio na kolumnie KodPocztowy, bez konieczności wyodrębniania go z dłuższego tekstu. To znacznie upraszcza zapytania SQL i poprawia ich wydajność.

Warto podkreślić, że atomowość kolumn w naszej tabeli jest wynikiem świadomego projektowania, a nie przypadku. Gdyby projektant nie przemyślał struktury, mógłby stworzyć kolumnę Adres zawierającą cały adres w jednym polu, co byłoby naruszeniem 1NF. Nasza tabela jest jednak zaprojektowana prawidłowo pod względem atomowości, co stanowi dobry punkt wyjścia do dalszych etapów normalizacji.

36/60Dygresja: co gdyby Autor zawierał wielu autorów?

Scenariusz alternatywny

Załóżmy, że książka 'Bazy danych' ma dwóch autorów: 'Connolly, Beg'. W kolumnie Autor mamy 'Connolly, Beg' – to NARUSZENIE atomowości!

Rozwiązanie 1 (w 1NF):

Dwa osobne wiersze dla tej samej książki – powielamy dane, ale każdy autor w osobnym wierszu:

ISBNAutor
978-83-123-4567-9Connolly
978-83-123-4567-9Beg

Rozwiązanie 2 (w 2NF):

Osobna tabela dla autorów – to zrobimy w 2NF.

Gdyby autorów było wielu – musielibyśmy zrobić osobne wiersze.
Książka z dwoma autorami, pod spodem dwa wiersze w tabeli (powielony ISBN)

Dygresja dotycząca książek z wieloma autorami ilustruje pewną niedoskonałość 1NF, która jest świadomym kompromisem. W sytuacji, gdy książka ma dwóch autorów, 1NF wymaga utworzenia dwóch osobnych wierszy dla tej samej książki, co prowadzi do powielenia wszystkich pozostałych danych książki – tytułu, ISBN, gatunku, roku wydania i wydawnictwa. To powielenie jest akceptowalne w 1NF, ale zostanie wyeliminowane w 2NF poprzez wydzielenie osobnej tabeli Ksiazki.

W praktyce systemów bibliotecznych książki z wieloma autorami są bardzo powszechne – prace naukowe, podręczniki akademickie czy antologie często mają od dwóch do kilkunastu autorów. Gdybyśmy chcieli przechowywać takich autorów w osobnych kolumnach (Autor1, Autor2, itd.), musielibyśmy z góry określić maksymalną liczbę autorów, co jest rozwiązaniem niefunkcjonalnym. Dlatego też podejście z osobnymi wierszami, choć generuje redundancję, jest jedynym poprawnym rozwiązaniem w ramach 1NF.

Co istotne, w naszej tabeli bibliotecznej celowo przyjęliśmy założenie, że każda książka ma dokładnie jednego autora, aby uprościć przykład. W rzeczywistej implementacji systemu bibliotecznego należałoby od razu przewidzieć przypadek wielu autorów, stosując normalizację do wyższych postaci normalnych. To pokazuje, że projektowanie baz danych wymaga przewidywania rzeczywistych scenariuszy użycia, a nie tylko implementowania bieżących wymagań.

37/60Krok 2: Sprawdzenie powtarzających się grup (Zasada 2)

Analiza kolumn pod kątem grup

  • Czy mamy kolumny Autor1, Autor2, Autor3? NIE
  • Czy mamy kolumny Tel1, Tel2, Tel3? NIE
  • Czy mamy kolumny Adres1, Adres2? NIE

Każda kolumna jest unikalna i opisuje inny, odrębny atrybut.

Wniosek: ZASADA 2 SPEŁNIONA – nie ma powtarzających się grup kolumn.

W naszej tabeli nie ma powtarzających się grup – każda kolumna ma unikalne znaczenie.
Lista kolumn tabeli z zaznaczeniem, że każda ma inną nazwę i znaczenie (brak Autor1, Autor2)

Weryfikacja drugiej zasady 1NF w naszej tabeli przebiega błyskawicznie, ponieważ lista kolumn nie zawiera żadnych numerowanych pozycji. Analiza wykazuje, że każda kolumna ma unikalną, opisową nazwę i pełni odrębną funkcję w strukturze danych. Nie ma kolumn takich jak Autor1, Autor2, Autor3 ani Telefon1, Telefon2, Telefon3, co oznacza, że projektant nie popełnił typowego błędu powtarzających się grup.

Warto jednak pamiętać, że powtarzające się grupy mogą przyjmować również bardziej subtelne formy niż oczywiste nazwy z numerami. Na przykład kolumny DataWyp1, DataWyp2 opisujące kolejne wypożyczenia tego samego czytelnika również stanowiłyby powtarzającą się grupę, mimo że konkretna nazwa "DataWyp" jest inna niż "Autor". Kluczowym kryterium jest pytanie, czy kolumny reprezentują ten sam rodzaj zdarzenia lub właściwości – jeśli tak, powinny być modelowane jako osobne wiersze.

Dobra praktyka projektowa polega na tym, że jeśli podczas definiowania tabeli pojawia się potrzeba dodania kolumny z numerem lub sufiksem, należy natychmiast przerwać i rozważyć utworzenie osobnej tabeli powiązanej relacją jeden-do-wielu. Tabela powiązana nie tylko eliminuje problem z góry nieznanej liczby wartości, ale także umożliwia efektywne zapytania SQL bez konieczności stosowania złożonych warunków OR na wielu kolumnach.

38/60Krok 3: Sprawdzenie klucza głównego (Zasada 3)

Czy ID_Wypozyczenia jest unikalne?

  • Każde wypożyczenie ma inny numer ID: 1, 2, 3, ..., 12
  • Żadne dwa wiersze nie mają tego samego ID
  • ID_Wypozyczenia jednoznacznie identyfikuje każde wypożyczenie

SQL do weryfikacji:

-- Sprawdzenie unikalności klucza głównego
SELECT ID_Wypozyczenia, COUNT(*)
FROM Wypozyczenia
GROUP BY ID_Wypozyczenia
HAVING COUNT(*) > 1;
-- Wynik: 0 wierszy – klucz jest unikalny
ID_Wypozyczenia gwarantuje, że każdy wiersz jest unikalny – to spełnia trzeci warunek 1NF.
Klucz (ikona) pasujący do zamka, z etykietą ID_Wypozyczenia = PRIMARY KEY

Trzecie kryterium 1NF, dotyczące klucza głównego, jest w naszej tabeli spełnione przez kolumnę ID_Wypozyczenia, która jednoznacznie identyfikuje każdy wiersz. ID_Wypozyczenia to klasyczny przykład sztucznego klucza głównego (ang. surrogate key) – kolumna, która nie ma żadnego znaczenia biznesowego, a służy wyłącznie do technicznej identyfikacji wierszy. Stosowanie sztucznych kluczy jest powszechnie uznawane za dobrą praktykę, ponieważ eliminuje problemy związane ze zmiennością kluczy naturalnych.

Alternatywnym rozwiązaniem byłoby zastosowanie klucza naturalnego, na przykład kombinacji kolumn (Czytelnik, ISBN, DataWyp). Takie rozwiązanie ma jednak kilka poważnych wad. Po pierwsze, klucz złożony z trzech kolumn jest nieporęczny w użyciu – każde odwołanie do tego klucza wymaga podania wszystkich trzech wartości. Po drugie, nic nie stoi na przeszkodzie, aby ten sam czytelnik wypożyczył tę samą książkę dwukrotnie tego samego dnia, co spowodowałoby naruszenie unikalności klucza.

W MariaDB definicja ID_Wypozyczenia jako INT PRIMARY KEY zapewnia, że wartości w tej kolumnie są unikalne i nie mogą być puste. Dodatkowo, jeśli zastosujemy AUTO_INCREMENT, system sam będzie nadawał kolejne numery, eliminując ryzyko błędów przy ręcznym wprowadzaniu. To rozwiązanie jest nie tylko bezpieczne, ale także wydajne – indeks na pojedynczej kolumnie typu INTEGER jest znacznie szybszy niż indeks na złożonym kluczu tekstowo-datowym, co ma znaczenie przy łączeniu tabel.

39/60Weryfikacja: czy nasza tabela jest w 1NF?

Wynik testu: TAK

TestWynik
Test 1 (atomowość): każda komórka zawiera dokładnie jedną wartość
Test 2 (brak grup powtarzalnych): nie ma kolumn Autor1-Autor3
Test 3 (klucz główny): ID_Wypozyczenia jednoznacznie identyfikuje wiersze

Wniosek: tabela Wypozyczenia jest w 1NF.

To ważny moment: tabela spełnia 1NF, choć ma inne problemy (redundancję, anomalie).

Nasza tabela spełnia warunki 1NF – ale wciąż ma poważne problemy. To dowód, że 1NF to dopiero początek.
Trzy zielone znaczniki – tabela przeszła wszystkie testy 1NF

Wynik weryfikacji jest jednoznaczny: tabela Wypozyczenia spełnia wszystkie trzy kryteria pierwszej postaci normalnej. Atomowość jest zachowana – każda komórka zawiera dokładnie jedną wartość. Nie ma powtarzających się grup kolumn – każda kolumna ma odrębną, unikalną funkcję. Klucz główny jest zdefiniowany – ID_Wypozyczenia jednoznacznie identyfikuje każdy wiersz. To oznacza, że tabela jest zgodna z 1NF, co jest pierwszym ważnym kamieniem milowym w procesie normalizacji.

Należy jednak podkreślić, że spełnienie 1NF nie oznacza, że tabela jest poprawnie zaprojektowana. Nasza tabela wciąż boryka się z poważnymi problemami: redundancją danych, anomalią INSERT, anomalią UPDATE i anomalią DELETE. Te problemy nie zostaną rozwiązane przez 1NF, ponieważ dotyczą one relacji pomiędzy wierszami i kolumnami, a nie pojedynczych komórek. Do ich wyeliminowania potrzebne są wyższe postaci normalne.

Mimo tych ograniczeń, przejście przez proces weryfikacji 1NF było wartościowym ćwiczeniem. Nauczyłeś się, jak systematycznie analizować strukturę tabeli i oceniać jej zgodność z podstawowymi regułami normalizacji. Te umiejętności będą niezbędne przy projektowaniu własnych baz danych, a także przy analizie i poprawie istniejących schematów, które często zawierają naruszenia 1NF wynikające z braku wiedzy lub niedbałości projektanta.

40/601NF nie rozwiązuje wszystkich problemów

Co 1NF naprawiła, a co nie?

1NF NAPRAWIŁA:

  • Atomowość – każda komórka to jedna wartość
  • Brak powtarzających się grup – nie ma Autor1-Autor3
  • Klucz główny – każdy wiersz jest unikalnie identyfikowalny

1NF NIE NAPRAWIŁA (wciąż mamy):

  • Redundancję – adres Jana w 3 wierszach
  • Anomalię UPDATE – zmiana adresu = wiele modyfikacji
  • Anomalię DELETE – usunięcie wypożyczenia = utrata danych
  • Anomalię INSERT – nie można dodać czytelnika bez wypożyczenia
1NF to jak oczyszczenie terenu – usunęliśmy chwasty, ale grunt wciąż wymaga pracy.
Dwie kolumny – lewa 'Naprawione' (3 zielone znaczki), prawa 'Do naprawy' (4 czerwone znaczki)

Świadomość ograniczeń 1NF jest równie ważna, jak znajomość jej reguł. Pierwsza postać normalna skupia się wyłącznie na strukturze wewnątrz tabeli – zapewnia, że każda komórka zawiera pojedynczą wartość, eliminuje powtarzające się grupy kolumn i narzuca istnienie klucza głównego. Nie zajmuje się jednak relacjami między wierszami, które są źródłem większości problemów w naszej tabeli bibliotecznej.

Problemy, które pozostają po 1NF, wynikają z faktu, że w jednej tabeli wymieszane są różne encje: czytelnicy, książki i wypożyczenia. To mieszanie powoduje, że dane czytelnika są powielane przy każdym jego wypożyczeniu (redundancja), zmiana adresu wymaga aktualizacji wielu wierszy (anomalia UPDATE), dodanie nowego czytelnika wymaga istnienia wypożyczenia (anomalia INSERT), a usunięcie ostatniego wypożyczenia kasuje dane czytelnika (anomalia DELETE).

Każda z kolejnych postaci normalnych eliminuje jeden typ problemu. 2NF eliminuje anomalie wynikające z częściowych zależności funkcyjnych, 3NF eliminuje anomalie wynikające z zależności przechodnich, BCNF jest wzmocnieniem 3NF dla przypadków z nakładającymi się kluczami kandydującymi, 4NF eliminuje zależności wielowartościowe, a 5NF eliminuje zależności związane z dekompozycją. W kolejnych prezentacjach prześledzisz proces normalizacji aż do osiągnięcia pełnej, optymalnej struktury.

41/60Wizualizacja: tabela przed i po 1NF

Porównanie – co zmieniła 1NF?

AspektPrzed normalizacjąPo 1NF
Każda komórkaMogła zawierać listę wartościZawsze jedna wartość
Powtarzające się grupyMogły występować (Autor1-Autor3)Są zabronione
Klucz głównyMogło nie byćZawsze musi być
RedundancjaWciąż istniejeWciąż istnieje
AnomalieWszystkieWciąż istnieją

1NF to nie koniec – to pierwszy krok. Dalsze kroki: 2NF, 3NF, BCNF, 4NF, 5NF.

1NF to fundament – ale fundament to nie cały dom. Czas na kolejne piętra.
Dwie tabele obok siebie – 'Przed' (z listami w komórkach) i 'Po' (wszystko atomowe, klucz główny)

Wizualizacja porównująca stan tabeli przed i po 1NF unaocznia, że zmiany wprowadzone przez pierwszą postać normalną są stosunkowo subtelne, ale mają fundamentalne znaczenie dla dalszych etapów normalizacji. Przed normalizacją struktura tabeli mogła zawierać komórki z listami wartości, powtarzające się grupy kolumn oraz brakować klucza głównego. Po zastosowaniu 1NF każda komórka zawiera dokładnie jedną wartość, nie ma numerowanych kolumn o tym samym znaczeniu, a każdy wiersz jest jednoznacznie identyfikowalny.

W naszej tabeli bibliotecznej zmiany między stanem "przed" a "po" 1NF nie są drastyczne, ponieważ tabela ta została celowo zaprojektowana jako już zgodna z 1NF, aby skupić się na problemach wyższego rzędu. Gdyby jednak tabela zawierała naruszenia atomowości – na przykład kolumnę Autor z listą autorów – zastosowanie 1NF wymagałoby utworzenia dodatkowych wierszy, co znacząco zmieniłoby wygląd danych.

W następnych prezentacjach zobaczysz znacznie bardziej radykalne zmiany. W 2NF z jednej tabeli powstaną trzy: Czytelnicy, Ksiazki i Wypozyczenia. W 3NF dojdzie czwarta tabela dla miast. W BCNF, 4NF i 5NF struktura będzie się dalej dzielić, aż osiągnie optymalną postać, w której każda tabela reprezentuje jedną encję, a wszystkie zależności między danymi są właściwie modelowane za pomocą kluczy obcych.

42/60Proste ćwiczenie: sprawdź, czy tabela jest w 1NF

Tabela Zamowienia

Przeanalizuj tabelę Zamowienia:

ID_ZamowieniaKlientProduktyCalkowitaCenaAdresDostawy
1Jan KowalskiLaptop, Mysz, Klawiatura4500.00ul. Polna 10, Kraków
2Anna NowakMonitor1200.00ul. Lipowa 5, Warszawa

Pytania:

  • Czy kolumna Produkty jest atomowa? NIE – lista produktów
  • Czy jest klucz główny? TAK – ID_Zamowienia
  • Czy są powtarzające się grupy? NIE

Rozwiązanie: rozbić Produkty na osobne wiersze.

Spróbuj samodzielnie – czy widzisz listę w komórce? Jeśli tak, to naruszenie 1NF.
Tabela Zamowienia z zaznaczoną kolumną Produkty (lista w komórce) jako naruszenie 1NF

Ćwiczenie z tabelą Zamowienia jest klasycznym przykładem naruszenia 1NF, które często pojawia się w rzeczywistych systemach. Kolumna Produkty zawiera listę produktów oddzielonych przecinkami: "Laptop, Mysz, Klawiatura". To narusza zasadę atomowości, ponieważ w jednej komórce przechowywane są trzy odrębne wartości. Gdybyśmy chcieli policzyć, ile zamówień zawiera produkt "Mysz", musielibyśmy zastosować skomplikowane zapytanie z LIKE, które jest wolne i podatne na błędy.

Rozwiązanie polega na przekształceniu struktury tak, aby każde zamówienie mogło mieć wiele wierszy – po jednym dla każdego produktu. Zamówienie numer 1, które zawiera trzy produkty, będzie reprezentowane przez trzy wiersze z tym samym ID_Zamowienia. To rozwiązanie, choć zwiększa liczbę wierszy w tabeli, umożliwia proste i wydajne zapytania: "pokaż wszystkie zamówienia zawierające Mysz" sprowadza się do SELECT * FROM Zamowienia WHERE Produkt = 'Mysz'.

Analogiczny problem występuje często w systemach e-commerce, gdzie w jednej tabeli przechowuje się zamówienia z listą produktów. Prawidłowe rozwiązanie wymaga wydzielenia osobnej tabeli SzczegolyZamowienia z kluczem obcym do tabeli Zamowienia. W ramach 1NF dopuszczamy jednak prostsze rozwiązanie z powieleniem danych zamówienia w osobnych wierszach, co zostanie poprawione na etapie 2NF i wyższych postaci normalnych.

43/60Drugie ćwiczenie: tabela Studenci

Tabela Studenci

Przeanalizuj tabelę Studenci:

ID_StudentaImieNazwiskoOcenyAdres
1JanKowalski5, 4, 3, 5ul. Polna 10, Kraków
2AnnaNowak4, 4, 5, 3ul. Lipowa 5, Warszawa

Co jest źle?

  • Oceny: lista w komórce = naruszenie atomowości (Zasada 1)
  • Adres: złożona wartość = naruszenie atomowości (Zasada 1)

Rozwiązanie: osobna tabela Oceny + podział adresu na kolumny.

Ćwiczenia to klucz do zrozumienia – przeanalizuj samodzielnie, zanim zobaczysz rozwiązanie.
Tabela Studenci z zaznaczonymi naruszeniami – Oceny (lista) i Adres (złożony)

Drugie ćwiczenie z tabelą Studenci jest szczególnie pouczające, ponieważ łączy w sobie dwa różne naruszenia 1NF występujące jednocześnie w jednej tabeli. Kolumna Oceny zawiera listę wartości "5, 4, 3, 5" oddzielonych przecinkami, co stanowi naruszenie zasady atomowości. Kolumna Adres zawiera złożoną wartość "ul. Polna 10, Kraków", która łączy w sobie ulicę i miasto, co również narusza atomowość. Tabela posiada jednak klucz główny ID_Studenta, więc trzecia zasada 1NF jest zachowana.

Rozwiązanie wymaga dwóch kroków. Po pierwsze, należy wydzielić oceny do osobnej tabeli Oceny z kluczem obcym ID_Studenta, co pozwoli na przechowywanie dowolnej liczby ocen dla każdego studenta w osobnych wierszach. Po drugie, adres należy podzielić na osobne kolumny: Adres (ulica), Miasto, KodPocztowy. W praktyce szkolnej często potrzebujemy filtrować studentów według miasta lub wyszukiwać według kodu pocztowego, co uzasadnia taki podział.

Warto zauważyć, że gdyby projektant, zamiast przechowywać oceny jako listę w jednej komórce, dodał kolumny Ocena1, Ocena2, Ocena3, popełniłby inne naruszenie – powtarzających się grup kolumn (Zasada 2 1NF). Oznacza to, że oba podejścia – lista w komórce i numerowane kolumny – są błędne, choć na różne sposoby. Prawidłowym rozwiązaniem w obu przypadkach jest utworzenie osobnej tabeli dla ocen, co jest zgodne z regułami normalizacji i umożliwia elastyczne modelowanie danych.

44/60Tworzymy tabelę w MariaDB – pierwsze kroki

CREATE TABLE z typami danych

Zakładamy, że mamy bazę danych Biblioteka. Tworzymy tabelę Wypozyczenia.

-- Tworzenie bazy danych (jeśli nie istnieje)
CREATE DATABASE IF NOT EXISTS Biblioteka;
USE Biblioteka;

-- Tabela w 1NF – każda kolumna atomowa, klucz główny zdefiniowany
CREATE TABLE Wypozyczenia (
    ID_Wypozyczenia INT PRIMARY KEY AUTO_INCREMENT,
    Czytelnik      VARCHAR(100) NOT NULL,
    Adres          VARCHAR(200),
    Miasto         VARCHAR(50),
    KodPocztowy    VARCHAR(10),
    Telefon        VARCHAR(20),
    Email          VARCHAR(100),
    Tytul          VARCHAR(200) NOT NULL,
    Autor          VARCHAR(100),
    ISBN           VARCHAR(20),
    Gatunek        VARCHAR(50),
    RokWydania     INT,
    Wydawnictwo    VARCHAR(100),
    DataWyp        DATE NOT NULL,
    DataZwrotu     DATE,
    Kara           DECIMAL(10,2),
    Status         VARCHAR(20)
);
Każda kolumna ma określony TYP – to jak nadanie każdemu pudełku etykiety.
Zrzut ekranu konsoli MariaDB z wykonaniem CREATE TABLE

Polecenie CREATE TABLE jest podstawowym narzędziem do definiowania struktury tabel w języku SQL. W przykładowej definicji tabeli Wypozyczenia widzisz siedemnaście kolumn, każda z określonym typem danych oraz ograniczeniami NOT NULL i PRIMARY KEY. Definiowanie tabel w SQL wymaga znajomości typów danych oferowanych przez konkretny DBMS, ponieważ różne systemy mogą mieć odmienne zestawy typów oraz różną składnię.

W naszej definicji tabeli warto zwrócić uwagę na kilka detali. Kolumna ID_Wypozyczenia jest zadeklarowana jako INT PRIMARY KEY, co oznacza, że stanowi klucz główny tabeli. W praktyce często dodaje się również atrybut AUTO_INCREMENT, który automatycznie nadaje kolejne wartości liczbowe przy każdym nowym wierszu. Kolumny takie jak Czytelnik i Tytul mają ograniczenie NOT NULL, co wymusza wprowadzenie wartości przy każdym nowym rekordzie – nie mogą pozostać puste.

Uwaga: nazwy kolumn w SQL bez polskich znaków. W nazwach kolumn (np. Tytul zamiast "Tytuł", KodPocztowy zamiast "kod pocztowy", DataWyp zamiast "DataWypożyczenia") celowo pomijamy polskie znaki diakrytyczne (ą, ć, ę, ł, ń, ó, ś, ź, ż) oraz stosujemy skróty. To standardowa praktyka w SQL – unikanie znaków spoza ASCII w nazwach tabel i kolumn eliminuje problemy z kodowaniem, sortowaniem i przenośnością skryptów między różnymi systemami. Wartości w komórkach (np. "Kraków", "również") mogą i powinny zawierać polskie znaki – nazwy kolumn to techniczne identyfikatory, a nie dane.

Deklaracja typu danych dla każdej kolumny pełni rolę mechanizmu walidacji na poziomie bazy danych. Jeśli ktoś spróbuje wprowadzić wartość tekstową do kolumny RokWydania zadeklarowanej jako INT, MariaDB odrzuci taką operację i zgłosi błąd. Podobnie kolumna DataWyp jako DATE przyjmuje tylko poprawne daty w formacie RRRR-MM-DD. To automatyczne sprawdzanie typów stanowi pierwszą linię obrony przed błędnymi danymi, odciążając programistów aplikacji od implementacji własnych mechanizmów walidacyjnych.

45/60Typy danych w MariaDB – krótki przegląd

Dlaczego typy danych są ważne?

Typy danych zapobiegają błędom: nie można wpisać tekstu w kolumnę liczbową ani daty w kolumnę tekstową.

Najczęściej używane typy:

TypOpisPrzykład
INTLiczba całkowita42, -5, 1000
VARCHAR(n)Tekst o maks. długości n'Jan Kowalski'
DATEData (YYYY-MM-DD)'2026-02-01'
DECIMAL(p,s)Liczba z przecinkiemDECIMAL(10,2) = 12345.67
PRIMARY KEYUnikalny identyfikatorINT PRIMARY KEY
NOT NULLKolumna nie może być pustaVARCHAR(100) NOT NULL
Typy danych to zabezpieczenie – MariaDB sama pilnuje, żebyś nie wrzucił tekstu do kolumny z datami.
Pudełka z etykietami: 'Liczby', 'Tekst', 'Daty' – każdy typ danych do odpowiedniego pudełka

Typy danych w MariaDB stanowią kluczowy element definicji tabeli, ponieważ określają, jakie wartości mogą być przechowywane w danej kolumnie oraz jak są one fizycznie reprezentowane w pamięci. Wybór odpowiedniego typu ma bezpośredni wpływ na wydajność zapytań, rozmiar bazy danych oraz integralność przechowywanych informacji. Na przykład kolumna zadeklarowana jako INT zajmuje zawsze cztery bajty, niezależnie od przechowywanej wartości, podczas gdy VARCHAR zajmuje tyle bajtów, ile wynosi długość przechowywanego tekstu plus jeden bajt na przechowanie długości.

Typ VARCHAR(n) jest najczęściej używanym typem tekstowym w MariaDB. Parametr n określa maksymalną długość przechowywanego tekstu w znakach. Dla kodu pocztowego VARCHAR(10) jest wystarczające – polski kod pocztowy ma format XX-XXX, czyli sześć znaków. Dla adresu email VARCHAR(100) jest rozsądną wartością. Dla tytułu książki VARCHAR(200) powinno pomieścić nawet długie tytuły. W praktyce nie warto przesadnie zawyżać wartości n, ponieważ nie wpływa to na rozmiar danych, ale może mylnie sugerować użytkownikowi, że może wprowadzić bardzo długi tekst.

Typ DECIMAL(p,s) jest przeznaczony do przechowywania liczb zmiennoprzecinkowych z dokładnością stałoprzecinkową, co jest szczególnie ważne dla danych finansowych. W DECIMAL(10,2) pierwszy parametr oznacza całkowitą liczbę cyfr, a drugi liczbę cyfr po przecinku. Oznacza to, że możemy przechowywać wartości do 99.999.999,99. W przeciwieństwie do typu FLOAT lub DOUBLE, DECIMAL przechowuje wartości z dokładnością co do grosza, bez błędów zaokrągleń typowych dla zmiennoprzecinkowej arytmetyki binarnej. Dlatego w systemach finansowych zawsze należy używać DECIMAL, a nie FLOAT.

46/60Wstawiamy dane – INSERT

Pierwsze wstawienie – Jan Kowalski

-- Wstawienie pierwszego wiersza – Jan Kowalski wypożycza Przedwiośnie
INSERT INTO Wypozyczenia VALUES
(DEFAULT, 'Jan Kowalski', 'ul. Polna 10', 'Kraków', '30-001',
 '123-456-789', 'jan@example.com',
 'Przedwiośnie', 'Stefan Żeromski', '978-83-123-4567-1',
 'Powieść', 1924, 'Czytelnik',
 '2026-02-01', '2026-02-15', 0.00, 'Zwrócone');

Wartości w apostrofach (') to teksty. Liczby bez apostrofów. Kolejność wartości musi odpowiadać kolejności kolumn.

INSERT dodaje nowy wiersz. Każda wartość musi pasować do typu kolumny.
Strzałka wskazująca dane Jana Kowalskiego wchodzące do tabeli Wypozyczenia

Instrukcja INSERT służy do dodawania nowych wierszy do tabeli i jest pierwszą z czterech podstawowych operacji CRUD. W przykładzie na slajdzie użyto składni INSERT INTO tabela VALUES (w1, w2, ...), która wymaga podania wartości dla wszystkich kolumn w kolejności, w jakiej zostały zdefiniowane w tabeli. Ta składnia, choć zwięzła, jest podatna na błędy, ponieważ łatwo pomylić kolejność wartości, szczególnie gdy tabela ma dużo kolumn, jak w naszym przypadku.

W praktyce produkcyjnej zaleca się stosowanie jawnego wymieniania kolumn: INSERT INTO Wypozyczenia (Czytelnik, Adres, ...) VALUES ('Jan Kowalski', ...). Ta składnia jest bardziej bezpieczna, ponieważ niezależnie od kolejności kolumn w definicji tabeli, wartości zostaną dopasowane do kolumn po nazwie. Ponadto, jeśli w przyszłości dodamy nową kolumnę do tabeli, zapytania INSERT z jawną listą kolumn nadal będą działać, podczas gdy składnia skrócona może zwrócić błąd.

W przykładzie widać również użycie apostrofów do oznaczania wartości tekstowych oraz wartości numerycznych bez apostrofów. W MariaDB apostrofy są używane dla wszystkich typów tekstowych (VARCHAR, CHAR, TEXT) oraz dla dat (DATE). Wartości liczbowe (INT, DECIMAL) wpisuje się bez apostrofów. Wartość NULL, która oznacza brak danych, również nie wymaga apostrofów. Należy pamiętać, że NULL to nie to samo co pusty tekst '' – NULL oznacza, że wartość jest nieznana lub nieokreślona, podczas gdy pusty tekst to poprawna wartość tekstowa o zerowej długości.

47/60Wstawiamy więcej danych – widać redundancję

Drugie wypożyczenie Jana

-- Drugie wypożyczenie Jana – dane się powtarzają!
INSERT INTO Wypozyczenia VALUES
(DEFAULT, 'Jan Kowalski', 'ul. Polna 10', 'Kraków', '30-001',
 '123-456-789', 'jan@example.com',
 'Lalka', 'Bolesław Prus', '978-83-123-4567-2',
 'Powieść', 1890, 'Czytelnik',
 '2026-03-01', NULL, NULL, 'Wypożyczone');
-- Uwaga: NULL oznacza 'brak danych'

Zwróć uwagę: adres, telefon, email Jana – te same wartości w dwóch wierszach. To jest REDUNDANCJA.

NULL to nie jest 0 ani pusty tekst – NULL oznacza 'nie wiem', 'brak danych'.
Tabela z dwoma wierszami Jana – podkreślone powtarzające się dane (adres, telefon, email)

Drugie wstawienie danych dla Jana Kowalskiego unaocznia w praktyce problem redundancji, o którym mówiliśmy wcześniej. Wprowadzając drugie wypożyczenie, musieliśmy ponownie podać wszystkie dane osobowe Jana – adres, miasto, kod pocztowy, telefon i email – mimo że są one identyczne jak w pierwszym wierszu. W kodzie SQL oznacza to przepisanie tych samych wartości, co nie tylko marnuje czas programisty, ale przede wszystkim stwarza ryzyko błędu przy przepisywaniu.

Gdybyśmy w trakcie wprowadzania danych popełnili literówkę – na przykład wpisali "Krraków" zamiast "Kraków" – tabela zawierałaby dwa różne miasta dla tego samego czytelnika. Z punktu widzenia bazy danych obie wartości są poprawne, ponieważ nie ma mechanizmu wymuszającego spójność adresu w obrębie tego samego czytelnika. To prowadzi do sytuacji, w której ten sam czytelnik ma różne adresy w różnych wierszach, co jest klasycznym objawem braku normalizacji.

Warto również zwrócić uwagę na wartość NULL w kolumnach DataZwrotu i Kara. W MariaDB NULL oznacza brak wartości i jest interpretowany jako "nieznany" lub "nie dotyczy". W kontekście wypożyczeń NULL w kolumnie DataZwrotu oznacza, że książka nie została jeszcze zwrócona. NULL w kolumnie Kara oznacza, że kara nie została jeszcze naliczona. To poprawne użycie NULL, które odróżnia sytuację "książka nie zwrócona" od "książka zwrócona tego samego dnia" (wartość daty równa dacie wypożyczenia).

48/60Odczytujemy dane – SELECT

Podstawowe zapytania SELECT

-- Pokaż wszystkie wypożyczenia
SELECT * FROM Wypozyczenia;
-- * oznacza 'wszystkie kolumny'

-- Pokaż tylko wypożyczenia Jana Kowalskiego
SELECT Tytul, DataWyp, Status
FROM Wypozyczenia
WHERE Czytelnik = 'Jan Kowalski';

Wynik drugiego zapytania:

TytułData wypożyczeniaStatus
Przedwiośnie2026-02-01Zwrócone
Lalka2026-03-01Wypożyczone
SELECT to jak 'pokaż mi dane'. WHERE to jak 'ale tylko te, które spełniają warunek'.
Lupa nad tabelą, strzałki pokazujące, które dane są wybierane przez SELECT ... WHERE

Instrukcja SELECT jest najczęściej używanym poleceniem SQL w codziennej pracy z bazami danych. Przykłady zaprezentowane na slajdzie pokazują dwa podstawowe warianty: SELECT * wybierający wszystkie kolumny oraz SELECT z konkretnymi kolumnami i warunkiem WHERE. W praktyce produkcyjnej użycie SELECT * jest odradzane, ponieważ nieprecyzyjnie określa, jakie dane są pobierane, co utrudnia optymalizację zapytań i może prowadzić do niepotrzebnego przesyłania dużych ilości danych między serwerem a klientem.

Drugie zapytanie – SELECT Tytul, DataWyp, Status FROM Wypozyczenia WHERE Czytelnik = 'Jan Kowalski' – jest przykładem precyzyjnego zapytania, które zwraca tylko trzy kolumny i tylko te wiersze, które spełniają warunek. WHERE działa jak filtr, który przepuszcza tylko wiersze spełniające określony warunek logiczny. W naszym przypadku warunek porównuje kolumnę Czytelnik z wartością 'Jan Kowalski'. Warto zauważyć, że porównanie tekstu w SQL może być wrażliwe na wielkość liter w zależności od ustawień sortowania (collation) bazy danych.

Wynik zapytania to tabela z dwoma wierszami przedstawiającymi wypożyczenia Jana Kowalskiego. Każdy wiersz zawiera trzy kolumny zgodnie z listą w SELECT. Taka struktura wyniku jest czytelna i łatwa do wykorzystania w aplikacji. W praktyce zapytania SELECT często zawierają dodatkowe klauzule, takie jak ORDER BY do sortowania wyników, GROUP BY do grupowania danych czy JOIN do łączenia danych z wielu tabel, które poznasz w kolejnych częściach kursu.

49/60Jak sprawdzić, czy tabela jest w 1NF? – zestaw zapytań kontrolnych

SQL do weryfikacji 1NF

-- 1. Sprawdź, czy klucz główny jest unikalny
SELECT ID_Wypozyczenia, COUNT(*)
FROM Wypozyczenia
GROUP BY ID_Wypozyczenia
HAVING COUNT(*) > 1;
-- Jeśli wynik = 0 wierszy → klucz główny jest OK

-- 2. Sprawdź, czy są puste wartości w kluczowych kolumnach
SELECT COUNT(*) AS braki
FROM Wypozyczenia
WHERE Czytelnik IS NULL OR Tytul IS NULL;
-- Jeśli wynik = 0 → kolumny NOT NULL są wypełnione
Lepiej zapobiegać niż leczyć – sprawdzaj strukturę tabeli zanim zaczniesz wprowadzać dane.
Lista kontrolna z zapytaniami SQL obok, każde odhaczone jako 'przeszło'

Zestaw zapytań kontrolnych do weryfikacji 1NF to praktyczne narzędzie, które każdy projektant baz danych powinien mieć w swoim arsenale. Zaprezentowane zapytania SQL pozwalają w szybki i automatyczny sposób sprawdzić, czy tabela spełnia podstawowe kryteria pierwszej postaci normalnej. Pierwsze zapytanie weryfikuje unikalność klucza głównego poprzez grupowanie wierszy według ID_Wypozyczenia i zliczanie wystąpień – jeśli jakakolwiek wartość pojawia się więcej niż raz, oznacza to naruszenie integralności klucza głównego.

Drugie zapytanie sprawdza, czy w kluczowych kolumnach nie ma wartości NULL. W 1NF klucz główny nie może zawierać NULL, ale również kolumny oznaczone jako NOT NULL w definicji tabeli powinny być wypełnione. W naszym przykładzie sprawdzamy kolumny Czytelnik i Tytul, które zostały zadeklarowane jako NOT NULL. Jeśli zapytanie zwróci liczbę większą od zera, oznacza to, że istnieją wiersze z brakującymi danymi w kolumnach obowiązkowych, co wskazuje na problem z integralnością danych.

W praktyce administracyjnej warto rozszerzyć zestaw zapytań kontrolnych o dodatkowe testy. Można na przykład sprawdzić, czy w kolumnach zdefiniowanych jako UNIQUE nie ma duplikatów, czy wartości w kolumnach numerycznych mieszczą się w dopuszczalnym zakresie, czy daty są logicznie spójne (data zwrotu nie wcześniejsza niż data wypożyczenia). Regularne uruchamianie takich zapytań kontrolnych, szczególnie przed i po większych operacjach na danych, pozwala utrzymać wysoką jakość danych w systemie przez długi czas.

50/60Ćwiczenie praktyczne: wstaw własne dane

INSERT dla Anny Nowak

Wstaw nowe wypożyczenie dla Anny Nowak. Książka: 'Pan Tadeusz', autor: Adam Mickiewicz, ISBN: 978-83-123-4567-3.

-- Uzupełnij brakujące wartości
INSERT INTO Wypozyczenia VALUES
(DEFAULT, 'Anna Nowak', 'ul. Lipowa 5', 'Warszawa', '00-002',
 '...', '...',  -- uzupełnij telefon i email
 '...', '...', '...',  -- tytuł, autor, ISBN
 '...', ..., '...',  -- gatunek, rok, wydawnictwo
 '2026-04-01', NULL, NULL, 'Wypożyczone');

Dane Anny:

Telefon: 987-654-321, Email: anna@example.com. Gatunek: Poemat, Rok: 1834, Wydawnictwo: Czytelnik.

Spróbuj samodzielnie – uzupełnij dane Anny. Jeśli potrafisz, rozumiesz strukturę tabeli.
Formularz INSERT z polami do wypełnienia, niektóre już wypełnione

Ćwiczenie praktyczne z wstawianiem danych dla Anny Nowak ma na celu sprawdzenie, czy rozumiesz strukturę tabeli Wypozyczenia i potrafisz samodzielnie sformułować poprawne zapytanie INSERT. W zadaniu musisz uzupełnić brakujące wartości dla telefonu, emaila, tytułu książki, autora, ISBN, gatunku, roku wydania i wydawnictwa. To wymaga nie tylko znajomości składni SQL, ale także zrozumienia, jakie typy danych są oczekiwane w poszczególnych kolumnach.

Prawidłowe rozwiązanie wymaga podania telefonu w apostrofach jako tekst ('987-654-321'), emaila również jako tekst ('anna@example.com'), tytuł ('Pan Tadeusz'), autora ('Adam Mickiewicz'), ISBN w postaci tekstowej ('978-83-123-4567-3'), gatunek ('Poemat'), roku jako liczby bez apostrofów (1834) oraz wydawnictwa ('Czytelnik'). Warto zwrócić uwagę, że rok wydania jest przechowywany jako liczba całkowita INT, więc nie używamy apostrofów, w przeciwieństwie do pozostałych wartości tekstowych.

Jeśli potrafisz samodzielnie uzupełnić to ćwiczenie, oznacza to, że rozumiesz zarówno strukturę tabeli, jak i podstawowe reguły składni SQL. W rzeczywistej pracy z bazami danych umiejętność poprawnego formułowania zapytań INSERT jest niezbędna, szczególnie przy migracji danych, testowaniu systemu czy tworzeniu skryptów inicjalizacyjnych. Błędy w zapytaniach INSERT mogą prowadzić do utraty danych lub naruszenia integralności bazy, dlatego warto wyrobić sobie nawyk dokładnego sprawdzania składni przed wykonaniem.

51/60Modyfikacja danych – UPDATE

Jan Kowalski zwraca Lalkę

-- Jan Kowalski zwraca Lalkę – aktualizujemy datę zwrotu i status
UPDATE Wypozyczenia
SET DataZwrotu = '2026-03-15',
    Status = 'Zwrócone',
    Kara = 0.00
WHERE ID_Wypozyczenia = 2;
-- WHERE ID_Wypozyczenia = 2 – precyzyjnie wskazujemy, który wiersz

Zasady bezpiecznego UPDATE:

  • ZAWSZE używaj WHERE z UPDATE (inaczej zmienisz WSZYSTKIE wiersze!)
  • WHERE ID_Wypozyczenia = 2 – zmieniamy tylko jeden konkretny wiersz
UWAGA! UPDATE bez WHERE zmienia wszystkie wiersze w tabeli. Zawsze sprawdzaj WHERE przed wykonaniem.
Zaznaczony wiersz ID=2 przed i po UPDATE (DataZwrotu i Status zmienione)

Instrukcja UPDATE służy do modyfikacji istniejących danych w tabeli i jest jedną z najpotężniejszych, ale też najbardziej ryzykownych operacji SQL. Na slajdzie widzimy przykład aktualizacji daty zwrotu i statusu dla konkretnego wypożyczenia o ID równym 2. Kluczowym elementem tego zapytania jest klauzula WHERE ID_Wypozyczenia = 2, która precyzyjnie wskazuje, który wiersz ma zostać zaktualizowany. Bez tej klauzuli UPDATE zostałby zastosowany do wszystkich wierszy w tabeli, co mogłoby mieć katastrofalne skutki.

W praktyce produkcyjnej błąd pominięcia WHERE w UPDATE jest jednym z najczęstszych i najbardziej kosztownych błędów popełnianych przez programistów i administratorów baz danych. Aby zminimalizować ryzyko, zaleca się stosowanie trzystopniowej procedury: najpierw napisz zapytanie SELECT z tym samym WHERE, aby zobaczyć, które wiersze zostaną zmodyfikowane, następnie sprawdź wyniki, a dopiero potem zamień SELECT na UPDATE, zachowując tę samą klauzulę WHERE. Ta prosta procedura może uchronić przed nieodwracalną utratą lub uszkodzeniem danych.

W kontekście normalizacji warto zwrócić uwagę, że w nieznormalizowanej tabeli aktualizacja adresu czytelnika wymagałaby wielu zapytań UPDATE z różnymi warunkami WHERE dla każdego wypożyczenia. W znormalizowanej tabeli wystarczy jedno zapytanie UPDATE na tabeli Czytelnicy z WHERE ID_Czytelnika = odpowiednie_id. To nie tylko upraszcza kod, ale także eliminuje ryzyko pominięcia któregoś z wierszy, co jest główną zaletą normalizacji w kontekście operacji modyfikacji danych.

52/60Usuwanie danych – DELETE

Usunięcie wypożyczenia

-- Usunięcie wypożyczenia o ID = 1
DELETE FROM Wypozyczenia
WHERE ID_Wypozyczenia = 1;
-- Uwaga: usuwamy TYLKO wypożyczenie. Dane Jana i książki giną!
-- To jest anomalia DELETE

Konsekwencje:

  • Po usunięciu ID=1: tracimy informację o wypożyczeniu Przedwiośnia
  • Jan wciąż istnieje w innych wierszach (ID=2 i ID=11)
  • Gdyby to było ostatnie wypożyczenie → stracilibyśmy dane Jana całkowicie
DELETE też wymaga WHERE. I pamiętaj o anomalii – usunięcie wypożyczenia może usunąć więcej, niż chcesz.
Kosz na śmieci z etykietą ID=1, obok lista utraconych danych

Instrukcja DELETE jest ostatnią z czterech podstawowych operacji CRUD i jednocześnie najbardziej destrukcyjną, ponieważ trwale usuwa wiersze z tabeli. W przykładzie na slajdzie usuwamy wypożyczenie o ID równym 1, które dotyczy Przedwiośnia wypożyczonego przez Jana Kowalskiego. Po wykonaniu tego polecenia informacje o tym wypożyczeniu znikają bezpowrotnie, chyba że baza danych jest zabezpieczona odpowiednimi kopiami zapasowymi lub mechanizmami audytu.

Anomalia DELETE w naszej tabeli objawia się tym, że usunięcie wiersza wypożyczenia powoduje również utratę danych czytelnika i książki, jeśli było to jedyne wypożyczenie danego czytelnika lub jedyny egzemplarz danej książki w tabeli. W naszym przykładzie usunięcie ID=1 nie powoduje całkowitej utraty danych Jana Kowalskiego, ponieważ ma on jeszcze dwa inne wypożyczenia. Gdybyśmy jednak usunęli wszystkie jego wypożyczenia, dane osobowe Jana zniknęłyby z systemu całkowicie.

W systemach produkcyjnych często stosuje się tzw. miękkie usuwanie (ang. soft delete) zamiast rzeczywistego usuwania danych. Polega to na dodaniu kolumny logicznej, takiej jak CzyAktywny lub DataUsuniecia, która oznacza, że wiersz jest "usunięty" z perspektywy użytkownika, ale fizycznie pozostaje w tabeli. Dzięki temu możliwe jest przywrócenie danych w przypadku pomyłki oraz zachowanie historii dla celów audytowych. W SQL taki "miękki delete" realizuje się za pomocą UPDATE ustawiającego flagę, a nie za pomocą DELETE.

53/601NF w pigułce – co już wiemy?

Podsumowanie najważniejszych punktów

  • Trzy zasady 1NF: atomowość, brak powtarzających się grup, klucz główny
  • Nasza tabela biblioteczna (Wypozyczenia) JEST w 1NF
  • Mimo to: wciąż ma problemy (redundancja, anomalie)
  • To pokazuje, że 1NF to warunek konieczny, ale NIE wystarczający
  • 1NF = pierwszy krok na drodze do dobrze zaprojektowanej bazy
Pamiętaj: 1NF to fundament. Bez niego nie zbudujesz niczego – ale sam fundament to jeszcze nie dom.
Fundament domu z napisem '1NF', na nim belki '2NF', '3NF', 'BCNF', '4NF', '5NF'

Podsumowując pierwszą postać normalną, warto zapamiętać trzy złote zasady, które stanowią jej istotę. Po pierwsze, atomowość – każda komórka w tabeli musi zawierać dokładnie jedną wartość, niepodzielną z punktu widzenia logiki zapytań. Po drugie, brak powtarzających się grup kolumn – nie wolno tworzyć kolumn o tym samym znaczeniu różniących się tylko numerem w nazwie. Po trzecie, klucz główny – każdy wiersz musi być jednoznacznie identyfikowalny za pomocą klucza głównego, który jest unikalny, niezmienny i minimalny.

W trakcie tej prezentacji przeanalizowaliśmy tabelę Wypozyczenia i stwierdziliśmy, że spełnia ona wszystkie trzy kryteria 1NF. To ważne osiągnięcie, ale pamiętaj, że 1NF to dopiero pierwszy krok na drodze do dobrze zaprojektowanej bazy danych. Mimo zgodności z 1NF, nasza tabela wciąż wykazuje poważne problemy: redundancję danych, anomalię INSERT, anomalię UPDATE oraz anomalię DELETE. Te problemy zostaną wyeliminowane w kolejnych postaciach normalnych.

Wiedza zdobyta podczas tej prezentacji stanowi solidny fundament do dalszej nauki. Znasz już nie tylko reguły 1NF, ale także potrafisz je praktycznie stosować – weryfikować zgodność tabel, identyfikować naruszenia i proponować poprawki. Umiesz również posługiwać się podstawowymi poleceniami SQL: CREATE TABLE, INSERT, SELECT, UPDATE i DELETE. Te umiejętności są niezbędne na kolejnych etapach kursu, gdzie zajmiemy się wyższymi postaciami normalnymi i bardziej zaawansowanymi koncepcjami projektowania baz danych.

54/60Tabela podsumowująca: co 1NF rozwiązuje, a czego nie

10 problemów – 3 rozwiązane, 7 do rozwiązania

ProblemRozwiązany przez 1NF?
Wiele wartości w jednej komórce✔ TAK
Powtarzające się grupy kolumn✔ TAK
Brak identyfikacji wierszy✔ TAK
Redundancja danych✘ NIE
Anomalia INSERT✘ NIE
Anomalia UPDATE✘ NIE
Anomalia DELETE✘ NIE
Zależności funkcyjne od części klucza✘ NIE
Zależności przechodnie✘ NIE
Niezależne listy wartości✘ NIE

1NF naprawia 3 problemy, ale pozostawia 7 innych do rozwiązania.

1NF rozwiązuje około 1/3 problemów. Resztę załatwią 2NF, 3NF, BCNF, 4NF i 5NF.
Dwie kolumny – 'Rozwiązane' (3 pozycje, zielone) i 'Do rozwiązania' (7 pozycji, czerwone)

Tabela podsumowująca, którą widzisz na slajdzie, stanowi kompleksowe zestawienie dziesięciu problemów projektowych i wskazuje, które z nich są rozwiązywane przez 1NF. Z dziesięciu wymienionych problemów 1NF rozwiązuje trzy: wiele wartości w jednej komórce, powtarzające się grupy kolumn oraz brak identyfikacji wierszy. Pozostałe siedem problemów – redundancja, trzy rodzaje anomalii, zależności funkcyjne, zależności przechodnie oraz niezależne listy wartości – pozostają nierozwiązane i będą eliminowane kolejno przez 2NF, 3NF, BCNF, 4NF i 5NF.

Warto zwrócić uwagę na logikę, według której problemy są uporządkowane w tabeli. Kolejność odpowiada hierarchii postaci normalnych – 1NF rozwiązuje najprostsze problemy strukturalne na poziomie pojedynczej tabeli, 2NF i 3NF eliminują problemy związane z zależnościami funkcyjnymi między kolumnami, BCNF zaostrza kryteria dla przypadków z nakładającymi się kluczami, a 4NF i 5NF zajmują się najbardziej subtelnymi problemami związanymi z zależnościami wielowartościowymi i złączeniowymi.

Ta tabela będzie Ci służyć jako mapa drogowa w dalszej części kursu. W każdej kolejnej prezentacji możesz do niej wracać, aby sprawdzić, który problem jest aktualnie rozwiązywany i jakie postępy robisz w procesie normalizacji. Świadomość, że normalizacja to proces stopniowy i hierarchiczny, pomoże Ci zachować cierpliwość i systematyczność w nauce, a także zastosować tę wiedzę w praktyce projektowej, gdzie rzadko osiąga się pełną normalizację za pierwszym razem.

55/60Wprowadzenie do zależności funkcyjnych – zapowiedź 2NF

Co to jest zależność funkcyjna?

W 1NF dopuszczamy, że dane książki (tytuł, autor) zależą od ISBN. A ISBN to tylko CZĘŚĆ informacji o wypożyczeniu.

Zależność funkcyjna: A → B (A wyznacza B jednoznacznie)

  • ISBN → Tytul – ISBN jednoznacznie wyznacza tytuł książki
  • ISBN → Autor – ISBN wyznacza autora
  • ID_Czytelnika → Adres – ID czytelnika wyznacza adres

To jest częściowa zależność funkcyjna – klucz do zrozumienia 2NF.

Jeśli atrybut zależy od części klucza, a nie od całości – to sygnał, że potrzebujesz 2NF.
Schemat – strzałki od ISBN do Tytul i Autor (zależności funkcyjne)

Wprowadzenie do zależności funkcyjnych stanowi pomost pomiędzy 1NF a 2NF i jest kluczowe dla zrozumienia dalszych etapów normalizacji. Zależność funkcyjna to relacja między dwoma zbiorami kolumn w tabeli, oznaczana jako X → Y, która mówi, że jeśli znasz wartość kolumny (lub zestawu kolumn) X, możesz jednoznacznie określić wartość kolumny Y. Innymi słowy, dla każdej wartości X istnieje dokładnie jedna wartość Y, która jest z nią związana. Na przykład w systemie bibliotecznym ISBN → Tytul oznacza, że każdy ISBN jednoznacznie wyznacza tytuł książki.

W naszej tabeli Wypozyczenia mamy do czynienia z kilkoma zależnościami funkcyjnymi. ISBN wyznacza Tytul, Autor, Gatunek, RokWydania i Wydawnictwo. ID_Czytelnika wyznacza Adres, Miasto, KodPocztowy, Telefon i Email. ID_Wypozyczenia wyznacza wszystkie pozostałe kolumny, co jest naturalne, ponieważ jest kluczem głównym. Problem polega na tym, że atrybuty czytelnika i książki zależą od części klucza (odpowiednio od Czytelnik i ISBN), a nie od pełnego klucza głównego ID_Wypozyczenia.

Te częściowe zależności funkcyjne – czyli zależności atrybutów tylko od części klucza głównego – są źródłem anomalii, które 1NF pozostawiła nierozwiązane. 2NF zajmuje się właśnie eliminacją tych częściowych zależności poprzez wydzielenie osobnych tabel dla czytelników i książek. Zrozumienie zależności funkcyjnych jest absolutnie niezbędne do dalszej nauki normalizacji, dlatego zachęcam do dokładnego przestudiowania tej koncepcji przed przystąpieniem do kolejnej prezentacji o 2NF.

56/60Wizualizacja: od jednej tabeli do wielu (zapowiedź normalizacji)

Ewolucja struktury

Obecnie: 1 tabela Wypozyczenia z 17 kolumnami.

Docelowo po wszystkich postaciach normalnych:

EtapTabele
Po 1NF1 tabela: Wypozyczenia (17 kolumn)
Po 2NF3 tabele: Czytelnicy, Ksiazki, Wypozyczenia
Po 3NF4 tabele: + Miasta
Po BCNF5 tabel: + Opiekunowie
Po 4NF7 tabel: + Telefony, Emaile
Po 5NF7+ tabel: dalsze dekompozycje
Z jednej tabeli zrobi się 7+ – każda będzie przechowywać jeden rodzaj faktów.
Diagram – jedna duża tabela 'Przed' rozpadająca się na 7+ mniejszych tabel 'Po'

Wizualizacja ewolucji struktury od jednej tabeli do wielu ilustruje docelowy efekt pełnej normalizacji, do którego będziemy dążyć przez cały cykl prezentacji. Na początku mamy jedną tabelę Wypozyczenia z siedemnastoma kolumnami, która – jak już wiemy – pomimo zgodności z 1NF cierpi na poważne problemy wynikające z wymieszania różnych encji. W miarę postępu normalizacji ta jedna tabela będzie ulegać dekompozycji na coraz mniejsze, wyspecjalizowane tabele.

Po osiągnięciu 2NF z jednej tabeli powstaną trzy: Czytelnicy (z danymi osobowymi), Ksiazki (z metadanymi książek) oraz Wypozyczenia (z informacjami o zdarzeniach). W 3NF może dojść czwarta tabela dla miast lub kodów pocztowych, jeśli zidentyfikujemy zależności przechodnie. W BCNF, 4NF i 5NF struktura będzie się dalej rozwijać, aż osiągnie formę, w której każda tabela reprezentuje dokładnie jeden rodzaj encji lub związku między encjami.

Warto podkreślić, że siedem dobrze zaprojektowanych tabel jest znacznie lepszych niż jedna chaotyczna. Każda z tych tabel ma jasno określoną odpowiedzialność, przechowuje jeden rodzaj faktów i jest powiązana z innymi tabelami za pomocą kluczy obcych. Dzięki temu zapytania SQL są prostsze i bardziej wydajne, aktualizacje danych są bezpieczne i nie prowadzą do niespójności, a dodawanie nowych rodzajów danych nie wymaga przebudowy całego schematu. To właśnie te korzyści sprawiają, że warto poświęcić czas na naukę normalizacji.

57/60Ćwiczenia do samodzielnego wykonania (część 1)

Sprawdź swoją wiedzę

Ćwiczenie 1: Tabela Zamowienia

Zamowienia(ID, Klient, Produkty, Ilosci, Cena, AdresDostawy). Kolumna Produkty zawiera listę produktów.

  • Czy kolumna Produkty jest atomowa? (NIE)
  • Jak naprawić? (osobne wiersze dla każdego produktu)

Ćwiczenie 2: Tabela Studenci

Studenci(Imie, Nazwisko, Oceny (lista), Adres).

  • Zidentyfikuj naruszenia 1NF
  • Zaproponuj poprawioną strukturę
Najlepiej uczymy się przez praktykę – spróbuj samodzielnie przed sprawdzeniem odpowiedzi.
Dwie tabele – Zamowienia i Studenci – z zaznaczonymi polami do przeanalizowania

Ćwiczenia do samodzielnego wykonania są kluczowym elementem procesu uczenia się normalizacji. Ćwiczenie 1 dotyczy tabeli Zamowienia, w której kolumna Produkty zawiera listę produktów – to naruszenie atomowości, które należy naprawić poprzez rozbicie na osobne wiersze. Ćwiczenie 2 dotyczy tabeli Studenci, gdzie mamy dwa naruszenia jednocześnie: Oceny (lista) i Adres (złożony). Samodzielne rozwiązanie tych ćwiczeń wymaga nie tylko znajomości reguł 1NF, ale także umiejętności ich praktycznego zastosowania.

W przypadku Ćwiczenia 1 prawidłowym rozwiązaniem jest przekształcenie tabeli tak, aby każde zamówienie było reprezentowane przez tyle wierszy, ile produktów zawiera. Zamówienie z laptopem, myszą i klawiaturą będzie reprezentowane przez trzy wiersze z tym samym ID_Zamowienia. To rozwiązanie, choć zwiększa liczbę wierszy, umożliwia proste zapytania SQL: znalezienie wszystkich zamówień zawierających dany produkt wymaga jedynie WHERE Produkt = 'Mysz'.

W przypadku Ćwiczenia 2 rozwiązanie wymaga dwóch kroków: utworzenia osobnej tabeli Oceny dla przechowywania ocen w osobnych wierszach oraz podziału adresu na osobne kolumny. Można również rozważyć, czy kod pocztowy nie powinien być wydzielony jako osobna kolumna, jeśli planujemy filtrowanie według kodu pocztowego. Zachęcam do samodzielnego zaprojektowania rozwiązań i porównania ich z tymi, które zostaną przedstawione na kolejnych slajdach.

58/60Ćwiczenia do samodzielnego wykonania (część 2)

Kolejne wyzwania

Ćwiczenie 3: Rezerwacje_biblioteczne

Zaprojektuj tabelę Rezerwacje_biblioteczne w 1NF. Rezerwacje książek przez czytelników. Jakie kolumny? Jaki klucz główny?

Ćwiczenie 4: Kary

Napisz CREATE TABLE dla bibliotecznej tabeli Kary. Kto zapłacił, za co, ile, kiedy.

Ćwiczenie 5 (dla zaawansowanych):

Znajdź w internecie przykładową bazę danych (np. sklepu) i sprawdź, czy jej tabele są w 1NF.

Ćwiczenia to klucz do zrozumienia – uruchom MariaDB i przetestuj na danych.
Trzy karty zadań: Rezerwacje, Kary, Analiza istniejącej bazy

Kolejna partia ćwiczeń rozwija umiejętności projektowania tabel od podstaw. Ćwiczenie 3 polega na zaprojektowaniu tabeli Rezerwacje_biblioteczne w 1NF. Przy projektowaniu należy zastanowić się, jakie informacje są niezbędne do obsługi rezerwacji: który czytelnik rezerwuje, którą książkę, na kiedy, czy rezerwacja została zrealizowana. Klucz główny powinien jednoznacznie identyfikować każdą rezerwację, a wszystkie kolumny powinny być atomowe. Warto również rozważyć, czy nie należy wprowadzić kolumny statusu rezerwacji.

Ćwiczenie 4 dotyczy zaprojektowania tabeli Kary, która przechowuje informacje o karach nałożonych na czytelników. Należy określić: kto zapłacił karę, za co (za które wypożyczenie), jaka jest kwota kary, kiedy została nałożona i czy została zapłacona. To ćwiczenie wymaga doboru odpowiednich typów danych: DECIMAL dla kwoty, DATE dla dat, VARCHAR dla opisu. Warto również przemyśleć, czy tabela Kary powinna być powiązana z tabelą Wypozyczenia za pomocą klucza obcego.

Ćwiczenie 5, przeznaczone dla zaawansowanych, polega na znalezieniu w internecie przykładowej bazy danych i sprawdzeniu zgodności jej tabel z 1NF. To doskonałe ćwiczenie rozwijające umiejętność analizy istniejących schematów. W praktyce zawodowej często spotyka się bazy danych projektowane bez znajomości reguł normalizacji, a umiejętność szybkiej identyfikacji naruszeń 1NF jest niezwykle cenna podczas audytów i refaktoryzacji systemów.

59/60Bibliografia i źródła

Kluczowe publikacje i materiały

  • E.F. Codd (1970) – 'A Relational Model of Data for Large Shared Data Banks'
  • E.F. Codd (1971) – 'Further Normalization of the Data Base Relational Model'
  • C.J. Date – 'An Introduction to Database Systems' (8th ed.)
  • R. Connolly, T. Begg – 'Database Systems: A Practical Approach'
  • Dokumentacja MariaDB – https://mariadb.com/kb/en/
  • Wikipedia – 'First normal form' – https://en.wikipedia.org/wiki/First_normal_form
Jeśli chcesz zgłębić temat – zacznij od oryginalnego artykułu Codda z 1970 roku.
Półka z książkami (okładki podręczników) i ikona przeglądarki z dokumentacją MariaDB

Bibliografia zamieszczona na slajdzie zawiera najważniejsze źródła wiedzy o normalizacji baz danych, zarówno klasyczne pozycje książkowe, jak i zasoby internetowe. Oryginalny artykuł Edgara Codda z 1970 roku "A Relational Model of Data for Large Shared Data Banks" to fundamentalna praca, która zapoczątkowała całą dziedzinę relacyjnych baz danych. Mimo upływu ponad pięćdziesięciu lat od publikacji, artykuł ten pozostaje aktualny i stanowi doskonałe wprowadzenie do teorii, na której opiera się współczesna inżynieria bazodanowa.

Podręcznik C.J. Date'a "An Introduction to Database Systems" jest powszechnie uznawany za "biblię" baz danych. Date, który był współpracownikiem Codda, w przystępny sposób wyjaśnia zarówno podstawowe koncepcje, jak i zaawansowane zagadnienia, w tym normalizację. Książka Connolly'ego i Begga "Database Systems: A Practical Approach" kładzie większy nacisk na praktyczne aspekty projektowania baz danych i zawiera wiele przykładów oraz ćwiczeń, które pomogą Ci utrwalić zdobytą wiedzę.

Z zasobów internetowych szczególnie polecam dokumentację MariaDB dostępną na stronie mariadb.com/kb/en/, która zawiera wyczerpujące informacje o składni SQL, typach danych i zaawansowanych funkcjach. Wikipedia, choć nie jest źródłem naukowym, oferuje dobre wprowadzenie do terminologii normalizacji w różnych językach, w tym w języku polskim. Zachęcam również do korzystania z forów dyskusyjnych, takich jak Stack Overflow, gdzie można znaleźć odpowiedzi na konkretne pytania dotyczące normalizacji od praktyków z całego świata.

60/60Dziękuję / Pytania / Zapowiedź 2NF

Podsumowanie i co dalej?

Gratulacje! Poznałeś pierwszą postać normalną (1NF).

Co dalej? – kolejne postaci normalne:

  • 1NF: atomowość, brak grup, klucz główny ✔ (już za nami)
  • 2NF: eliminacja częściowych zależności funkcyjnych
  • 3NF: eliminacja zależności przechodnich
  • BCNF: silniejsza wersja 3NF
  • 4NF: eliminacja zależności wielowartościowych
  • 5NF: eliminacja zależności złączeniowych

Zapowiedź: '2NF: Druga postać normalna – eliminacja częściowych zależności funkcyjnych'

Normalizacja to nie teoria – to praktyczne narzędzie, które oszczędzi Ci godzin debugowania przy zapytaniach SQL.
Metafora drogi: 'Jesteś tutaj' na etapie 1NF, przed nami dalsze etapy normalizacji

Gratuluję dotarcia do końca pierwszej prezentacji z cyklu o normalizacji baz danych! Przebyliśmy razem długą drogę od absolutnych podstaw, przez definicję i zasady 1NF, aż po praktyczną implementację w MariaDB. Masz już solidne podstawy, które pozwolą Ci świadomie projektować struktury tabel zgodne z pierwszą postacią normalną i identyfikować naruszenia w istniejących schematach. To ważny krok w Twojej edukacji bazodanowej.

Zachęcam Cię do samodzielnej praktyki – uruchom MariaDB, stwórz bazę danych i poeksperymentuj z przedstawionymi przykładami. Najlepszym sposobem na utrwalenie wiedzy jest samodzielne tworzenie tabel, wypełnianie ich danymi i formułowanie zapytań SQL. Pamiętaj, że normalizacja to umiejętność praktyczna, którą rozwija się poprzez wielokrotne stosowanie reguł w różnych kontekstach, a nie tylko przez czytanie o nich.

W następnej prezentacji pt. "2NF: Druga postać normalna – eliminacja częściowych zależności funkcyjnych" zrobimy kolejny krok w procesie normalizacji. Dowiesz się, jak identyfikować częściowe zależności funkcyjne i jak dzielić tabele, aby je wyeliminować. Do zobaczenia na kolejnym spotkaniu, gdzie nasza tabela Wypozyczenia zacznie się dzielić na mniejsze, wyspecjalizowane struktury.