Czym jest encja i jej atrybuty, jak wybrać klucz główny, po co jest klucz obcy i jak dobrać typ danych do każdej kolumny, gdy projektujesz tabelę od zera. Lekcja 7.2 kursu INF.03.
Encja (entity) to typ obiektu, o którym baza przechowuje dane: klient, produkt, zamówienie. W bazie relacyjnej encji odpowiada tabela, pojedynczemu wystąpieniu encji rekord, a atrybutowi encji kolumna. Na egzaminie pisemnym wracają z tego trzy pytania: przez co jest reprezentowana encja, co jednoznacznie identyfikuje rekord i jaki typ danych pasuje do opisanej kolumny. W zadaniu praktycznym ta sama wiedza jest potrzebna dwa razy: przy czytaniu schematu relacji z arkusza i przy poleceniu ALTER TABLE ... ADD, w którym typ nowej kolumny podano słowami.
Encja i atrybut
Encja to rzeczownik z opisu zadania, o którym trzymamy WIELE wystąpień o tym samym zestawie cech. Atrybut to jedna taka cecha. Projekt bazy zaczyna się od zdań zamawiającego:
Biuro turystyczne prowadzi listę wycieczek: nazwa, kraj, cena,
liczba dni, termin wyjazdu.
Encją jest tutaj wycieczka, bo wycieczek jest wiele i każda ma nazwę, kraj, cenę, liczbę dni i termin. Te pięć rzeczy to atrybuty: opisują wycieczkę, ale nie są obiektami, o których zbieramy osobne dane. Kraj bywa granicznym przypadkiem: jeżeli zamawiający chce trzymać przy kraju walutę i wymagane dokumenty, kraj staje się osobną encją. Definicję encji z atrybutami zapisuje się w projekcie skrótowo, z kluczem głównym na pierwszym miejscu:
wycieczka(id, nazwa, kraj, cena, dni, termin)
Dziedzina (domain) atrybutu to zbiór wartości, które wolno w nim zapisać. Dziedziną atrybutu dni są liczby całkowite dodatnie, dziedziną atrybutu cena liczby nieujemne z dwoma miejscami po przecinku. Dziedzina jest tym, co potem przekłada się na typ danych i na ograniczenia kolumny.
Atrybut prosty ma jedną, niepodzielną wartość: cena, termin. Atrybut złożony rozpada się na części, jak adres na ulicę, kod pocztowy i miasto. W tabeli zapisuje się go jako osobne kolumny ulica, kod, miasto, bo wyszukiwanie po samym mieście z jednej wspólnej kolumny jest niewykonalne. Atrybut jednowartościowy trzyma jedną wartość na wystąpienie, a atrybut wielowartościowy wiele: klient z dwoma numerami telefonu. Kolumna telefon z wpisem 600100200, 601200300 to sygnał, że brakuje osobnej tabeli. Dlaczego tak jest, tłumaczy lekcja o normalizacji. Klucz główny
Klucz główny (PRIMARY KEY, w pytaniach nazywany też kluczem podstawowym) to kolumna albo zestaw kolumn, które jednoznacznie identyfikują rekord w tabeli. Po jego wartości baza trafia w dokładnie jeden wiersz i po niej wskazują go inne tabele.
›
Wartość jest unikalna: dwa rekordy nie mogą mieć tej samej.
›
Wartość nigdy nie jest pusta, więc kolumnę zabezpiecza się atrybutem NOT NULL.
›
Tabela ma dokładnie jeden klucz główny.
›
Dobry klucz jest krótki i niezmienny, bo powtarza się w kluczach obcych innych tabel.
Klucz kandydujący (candidate key) to każda kolumna albo zestaw kolumn, które mogłyby pełnić tę rolę. W tabeli klient kandydatami są numer PESEL i adres e-mail, a imię nie, bo Anna może być druga. Z kandydatów wybiera się jeden i on zostaje kluczem głównym.
Klucz naturalny pochodzi z danych, które i tak opisują obiekt: PESEL, numer indeksu, ISBN książki. Klucz sztuczny (surrogate key) to numer dodany tylko po to, żeby identyfikować rekord, w MySQL najczęściej id INT AUTO_INCREMENT. W praktyce prawie zawsze wybiera się sztuczny: naturalny bywa długi, bywa nieznany w chwili dodawania rekordu, potrafi się zmienić (nazwisko po ślubie, adres e-mail), a PESEL to dodatkowo dana osobowa, której nie chce się kopiować do połowy tabel. Dlatego w bazie sklepu klient, produkt i zamowienie mają zwykłe id.
Klucz złożony (composite key) to klucz główny zbudowany z dwóch albo więcej kolumn, które dopiero razem są unikalne. Typowo w tabeli łączącej: w tabeli uczen_przedmiot kolumny uczen_id i przedmiot_id osobno powtarzają się wiele razy, ale para uczeń plus przedmiot występuje raz.
Pułapka: klucz główny nie musi być liczbą i nie musi nazywać się id, ale musi być unikalny i różny od NULL. Właśnie to nazywa się integralnością encji.
Klucz obcy
Klucz obcy (FOREIGN KEY) to kolumna, która przechowuje wartość klucza głównego innej tabeli. Tworzy się go po to, żeby zdefiniować relację jeden do wielu: klucz obcy stoi zawsze po stronie „wiele". Jeden klient ma wiele zamówień, więc to zamowienie dostaje kolumnę klient_id, a nie odwrotnie. W bazie sklepu wygląda to tak: klient(id, imie, miasto) z kluczem głównym id oraz zamowienie(id, klient_id, produkt_id, ilosc, kwota), w którym klient_id wskazuje na klient.id.
W tabeli klient kluczem głównym jest kolumna id, a w tabeli zamowienie kolumna klient_id jest kluczem obcym wskazującym na tę wartość, przy czym zamówienie 13 ma w niej NULL
Klucz obcy pilnuje integralności referencyjnej: każdej wartości klucza obcego musi odpowiadać istniejąca wartość klucza głównego w tabeli powiązanej. Baza nie pozwoli dodać zamówienia klienta o numerze 99, skoro takiego klienta nie ma, ani skasować klienta, do którego zamówienia się odwołują. Sam klucz obcy może być pusty, jeżeli kolumna nie ma NOT NULL: zamówienie 13 z rysunku ma NULL w klient_id, bo powstało bez konta. NULL nie łamie integralności, bo nie wskazuje na nic.
Typ kolumny z kluczem obcym musi być identyczny z typem klucza głównego, na który wskazuje. Jeżeli klient.id jest INT UNSIGNED, to zamowienie.klient_id też, inaczej baza odrzuci definicję relacji.
CREATE TABLE zamowienie (
id INT AUTO_INCREMENT PRIMARY KEY,
klient_id INT,
produkt_id INT NOT NULL,
ilosc INT NOT NULL,
FOREIGN KEY (klient_id) REFERENCES klient (id)
);
Gdy klucz główny wskazywanej tabeli jest złożony, klucz obcy też obejmuje kilka kolumn i zapisuje się go z nazwą ograniczenia. Tak wygląda odwołanie tabeli ocena do złożonego klucza tabeli uczen_przedmiot:
CONSTRAINT fk_ocena FOREIGN KEY (uczen_id, przedmiot_id)
REFERENCES uczen_przedmiot (uczen_id, przedmiot_id)
Typ danych dla atrybutu
Typ danych mówi, co wolno wpisać do kolumny. Dobiera się go do dziedziny atrybutu, a nie do tego, jak wartość wygląda na papierze.
Atrybut
Typ w MySQL
Dlaczego
imię
VARCHAR(40)
tekst zmiennej długości, 40 znaków wystarczy
nazwisko
VARCHAR(60)
dłuższe niż imię, nazwiska dwuczłonowe
kod pocztowy
CHAR(6)
zawsze 6 znaków z myślnikiem, stała długość
PESEL
CHAR(11)
zera wiodące zostają, a na numerze się nie liczy
cena
DECIMAL(8,2)
pieniądze liczone dokładnie, bez zaokrągleń
liczba sztuk
INT albo SMALLINT
liczba całkowita, mała wartość mieści się w SMALLINT
ocena od 1 do 6
TINYINT UNSIGNED
najmniejszy typ, który mieści zakres 0 do 255
data urodzenia
DATE
sama data, bez godziny
moment złożenia zamówienia
DATETIME
data razem z godziną
czy aktywny
BOOLEAN
dwie wartości, prawda albo fałsz
opis wycieczki
TEXT
tekst dłuższy niż kilkaset znaków
status zamówienia
ENUM('nowe','wyslane')
wartość z zamkniętej listy
płeć
ENUM('K','M') albo CHAR(1)
jeden znak z ustalonego zbioru
identyfikator rekordu
INT AUTO_INCREMENT
klucz sztuczny, numer nadaje baza
Cztery zasady, które rozstrzygają większość przypadków:
›
Wybierz najmniejszy typ, który mieści wszystkie dopuszczalne wartości.
›
Tekst stałej długości to CHAR, tekst zmiennej długości to VARCHAR.
›
Pieniądze to DECIMAL, nigdy FLOAT.
›
Ciąg cyfr, na którym się nie liczy (telefon, kod pocztowy, PESEL, numer konta), to tekst.
Ćwiczenie: biuro turystyczne
Opis z arkusza brzmi tak: biuro turystyczne oferuje wycieczki, każda ma nazwę, kraj, cenę i liczbę dni; klient ma imię, nazwisko i adres e-mail; klient zapisuje się na wycieczki. Zaprojektuj encje, wskaż klucze i dobierz typy.
Rozwiązanie zaczyna się od wypisania encji. Rzeczowniki z wieloma wystąpieniami to wycieczka i klient. Zdanie „klient zapisuje się na wycieczki" opisuje powiązanie: jeden klient jeździ na wiele wycieczek, a na jedną wycieczkę zapisuje się wielu klientów, więc powstaje trzecia encja, tabela łącząca zapis.
wycieczka(id, nazwa, kraj, cena, dni)
klient(id, imie, nazwisko, email)
zapis(wycieczka_id, klient_id, data_zapisu)
Tabela
Klucz główny
Klucze obce
wycieczka
id, sztuczny
brak
klient
id, sztuczny; email był kandydatem, ale bywa zmieniany
brak
zapis
złożony: wycieczka_id plus klient_id
wycieczka_id, klient_id
wycieczka.nazwa
VARCHAR(100) NOT NULL
wycieczka.kraj
VARCHAR(50)
wycieczka.cena
DECIMAL(8,2) NOT NULL
wycieczka.dni
TINYINT UNSIGNED
Na egzaminie
Pytania pisemne z tej lekcji są krótkie i powtarzalne. „Encja jest reprezentowana przez" z odpowiedzią tabela. „Jednoznacznemu identyfikatorowi rekordu odpowiada" z odpowiedzią klucz główny. „Klucz obcy tworzy się po to, aby zdefiniować relację 1..n wiążącą go z kluczem głównym innej tabeli", i to zdanie warto znać w tej właśnie postaci, bo dystraktory mówią o sortowaniu albo o przyspieszaniu zapytań, czym zajmuje się indeks, a nie klucz obcy. „Klucz główny zabezpiecza się przed pustą wartością atrybutem" z odpowiedzią NOT NULL. „Typ dla kodów pocztowych o stałej długości" z odpowiedzią CHAR. Osobne pytanie pokazuje zapis CONSTRAINT nazwa FOREIGN KEY (a, b) REFERENCES tabela (a, b) i pyta, co on robi: definiuje klucz obcy złożony z dwóch kolumn.
W zadaniu praktycznym arkusz daje gotowy plik .sql i rysunek schematu. Kolumny z końcówką _id to prawie zawsze klucze obce i to one wyznaczają, po czym łączyć tabele w kwerendzie „wykorzystującej relację". Drugie miejsce to ALTER TABLE ... ADD z kolumną opisaną słowami: „pole na ocenę od 1 do 6" to TINYINT UNSIGNED, „pole na cenę" to DECIMAL(8,2), „pole na kod pocztowy" to CHAR(6). Najczęstszy błąd na tym poleceniu to INT wszędzie tam, gdzie w treści pojawia się cyfra.
Ściąga
encja
typ obiektu, w bazie relacyjnej tabela
wystąpienie encji
jeden rekord, czyli wiersz tabeli
atrybut
cecha encji, czyli kolumna
dziedzina
zbiór wartości dopuszczalnych w atrybucie
klucz główny
unikalny, NOT NULL, jeden na tabelę
klucz kandydujący
każdy kandydat na klucz główny; wybiera się jeden
klucz sztuczny
id INT AUTO_INCREMENT, domyślny wybór
klucz naturalny
PESEL, ISBN; bywa zmienny i długi
klucz złożony
dwie kolumny razem, typowy w tabeli łączącej
klucz obcy
wskazuje na klucz główny innej tabeli, stoi po stronie „wiele"
integralność referencyjna
każdej wartości klucza obcego odpowiada istniejący klucz główny
typ klucza obcego
identyczny z typem klucza głównego
kod pocztowy, PESEL
CHAR, bo stała długość i zera wiodące
cena
DECIMAL(m,d), nigdy FLOAT
wartość 0 do 255
TINYINT UNSIGNED
Pobierz ściągę PDF