Algne tabel (1NK)
Alustame tabeliga Kliendi_rentimine
. Tabel on esimesel normaalkujul (1NK), kuna kõik veerud sisaldavad atomaarseid väärtusi ja igal real on unikaalne identifikaator (primaarvõti).
Jälgime edaspidi esimese rea (roheliselt tähistatud) andmete liikumist normaliseerimise käigus.
Tabel: Kliendi_rentimine
Kliendi_rentimine (
kliendi_nr,
vara_nr, -- PK osa
kliendi_eesnimi,
kliendi_perenimi,
vara_aadress,
rendi_algus, -- PK osa
rendi_lopp,
rent,
omaniku_nr,
omaniku_eesnimi,
omaniku_perenimi
)
Primaarvõti (PK): (vara_nr, rendi_algus)
(PK) - tähistab primaarvõtme osa tabeli päises.
Näidisandmed
kliendi_nr | vara_nr(PK) | kliendi_eesnimi | kliendi_perenimi | vara_aadress | rendi_algus(PK) | rendi_lopp | rent | omaniku_nr | omaniku_eesnimi | omaniku_perenimi |
---|---|---|---|---|---|---|---|---|---|---|
K001 | V001 | Jaan | Tamm | Pikk tn 1, Tartu | 2023-01-15 | 2024-01-14 | 520€ | O001 | Peeter | Kask |
K002 | V002 | Mari | Maasikas | Lai tn 5, Tallinn | 2023-03-01 | 2024-02-29 | 750€ | O002 | Anna | Mänd |
K001 | V003 | Jaan | Tamm | Mere pst 8, Pärnu | 2023-05-10 | 600€ | O003 | Mari | Maasikas | |
K003 | V001 | Eva | Kuusk | Pikk tn 1, Tartu | 2024-02-01 | 2025-01-31 | 520€ | O001 | Peeter | Kask |
K002 | V004 | Mari | Maasikas | Metsa tee 3, Viljandi | 2024-04-01 | 480€ | O002 | Anna | Mänd |
Märkad endiselt, et Jaani nimi ja Mari nimi korduvad eri ridades ja rollides.
Tabelite viimine teisele normaalkujule (2NK)
Funktsionaalsed sõltuvused (1NK tabelis)
Esmalt tuvastame funktsionaalsed sõltuvused algses tabelis Kliendi_rentimine
. Primaarvõti on liitvõti (vara_nr, rendi_algus).
fs1: (vara_nr, rendi_algus) -> (kliendi_nr, kliendi_eesnimi, kliendi_perenimi, rendi_lopp, vara_aadress, omaniku_nr, omaniku_eesnimi, omaniku_perenimi)
(Rent ei sõltu enam tervikvõtmest, seega eemaldatud siit)
fs2: kliendi_nr -> (kliendi_eesnimi, kliendi_perenimi)
(Kliendi number määrab kliendi nime) - Transitiivne sõltuvus
fs3: vara_nr -> (vara_aadress, rent, omaniku_nr, omaniku_eesnimi, omaniku_perenimi)
(Vara number määrab vara aadressi, rendi ja omaniku info) - Osaline sõltuvus primaarvõtmest (sõltub ainult `vara_nr`-st)
fs4: omaniku_nr -> (omaniku_eesnimi, omaniku_perenimi)
(Omaniku number määrab omaniku nime) - Transitiivne sõltuvus (fs3 kaudu)
2NK Definitsioon ja Põhjendus
Tabel on teisel normaalkujul (2NK), kui:
- See on esimesel normaalkujul (1NK).
- Iga mitte-primaarvõtme veerg on täielikult funktsionaalselt sõltuv primaarvõtmest.
Meie tabelis rikub 2NK tingimust osaline sõltuvus fs3, kus veerud (vara_aadress, rent, omaniku_nr, omaniku_eesnimi, omaniku_perenimi)
sõltuvad ainult primaarvõtme osast vara_nr
.
Lahendus: Eemaldame osalise sõltuvuse, luues uue tabeli Vara_omanik
nende veergude jaoks, mille determinantiks on vara_nr
.
Tulemuseks olevad tabelid (2NK)
Selle tulemusena jagame algse tabeli kaheks. Jälgime roheliselt märgitud andmete liikumist. Pange tähele, et `rent` on nüüd tabelis `Vara_omanik`.
Tabel 1: Vara_rentimine
Vara_rentimine (
kliendi_nr (FK -> ?), -- Viitab tulevasele Klient tabelile
kliendi_eesnimi,
kliendi_perenimi,
vara_nr (PK osa) (FK -> Vara_omanik),
rendi_algus (PK osa),
rendi_lopp
)
Primaarvõti (PK): (vara_nr, rendi_algus)
Välisvõti (FK): vara_nr -> Vara_omanik(vara_nr)
(Rendi summa eemaldati siit, kuna see sõltub osaliselt PK-st)
Tabel 2: Vara_omanik
Vara_omanik (
vara_nr (PK),
vara_aadress,
rent, -- Rent kuulub nüüd siia, kuna sõltub ainult varast
omaniku_nr (FK -> ?), -- Viitab tulevasele Omanik tabelile
omaniku_eesnimi,
omaniku_perenimi
)
Primaarvõti (PK): vara_nr
Näidisandmed (2NK)
Vara_rentimine tabel
kliendi_nr | kliendi_eesnimi | kliendi_perenimi | vara_nr(PK)(FK) | rendi_algus(PK) | rendi_lopp |
---|---|---|---|---|---|
K001 | Jaan | Tamm | V001 | 2023-01-15 | 2024-01-14 |
K002 | Mari | Maasikas | V002 | 2023-03-01 | 2024-02-29 |
K001 | Jaan | Tamm | V003 | 2023-05-10 | |
K003 | Eva | Kuusk | V001 | 2024-02-01 | 2025-01-31 |
K002 | Mari | Maasikas | V004 | 2024-04-01 |
Vara_omanik tabel
vara_nr(PK) | vara_aadress | rent | omaniku_nr | omaniku_eesnimi | omaniku_perenimi |
---|---|---|---|---|---|
V001 | Pikk tn 1, Tartu | 520€ | O001 | Peeter | Kask |
V002 | Lai tn 5, Tallinn | 750€ | O002 | Anna | Mänd |
V003 | Mere pst 8, Pärnu | 600€ | O003 | Mari | Maasikas |
V004 | Metsa tee 3, Viljandi | 480€ | O002 | Anna | Mänd |
* Märkus: Rent asub nüüd õiges tabelis vastavalt eeldusele, et see sõltub ainult varast.
Tabelite viimine kolmandale normaalkujule (3NK)
Funktsionaalsed sõltuvused (2NK tabelites)
Vaatame nüüd 2NK tabeleid ja nende sõltuvusi:
Tabel: Vara_rentimine
fs1: (vara_nr, rendi_algus) -> (kliendi_nr, kliendi_eesnimi, kliendi_perenimi, rendi_lopp)
(Primaarvõti määrab ülejäänud veerud)
fs2: kliendi_nr -> (kliendi_eesnimi, kliendi_perenimi)
(Kliendi nr määrab nime) - Transitiivne sõltuvus (PK -> kliendi_nr -> nimi)
Tabel: Vara_omanik
fs3: vara_nr -> (vara_aadress, rent, omaniku_nr, omaniku_eesnimi, omaniku_perenimi)
(Primaarvõti määrab ülejäänud veerud)
fs4: omaniku_nr -> (omaniku_eesnimi, omaniku_perenimi)
(Omaniku nr määrab nime) - Transitiivne sõltuvus (PK -> omaniku_nr -> nimi)
3NK Definitsioon ja Põhjendus
Tabel on kolmandal normaalkujul (3NK), kui:
- See on teisel normaalkujul (2NK).
- Mitte ükski mitte-primaarvõtme veerg ei sõltu transitiivselt primaarvõtmest.
Meie tabelites rikuvad 3NK tingimust transitiivsed sõltuvused:
- Tabelis
Vara_rentimine
: fs2, kus kliendi nimi sõltubkliendi_nr
-st. - Tabelis
Vara_omanik
: fs4, kus omaniku nimi sõltubomaniku_nr
-st.
Lahendus: Eemaldame transitiivsed sõltuvused, luues eraldi tabelid Klient
ja Omanik
.
Tulemuseks olevad tabelid (3NK)
Jagame tabelid edasi. Roheliselt on tähistatud endiselt algsest 1NK tabeli esimesest reast pärit andmed. Rent jääb nüüd tabelisse Vara
.
Tabel 1: Klient
Klient (
kliendi_nr (PK),
kliendi_eesnimi,
kliendi_perenimi
)
Primaarvõti (PK): kliendi_nr
Tabel 2: Rentimine
Rentimine (
vara_nr (PK osa) (FK -> Vara),
kliendi_nr (FK -> Klient),
rendi_algus (PK osa),
rendi_lopp
)
Primaarvõti (PK): (vara_nr, rendi_algus)
Välisvõti (FK): kliendi_nr -> Klient(kliendi_nr)
Välisvõti (FK): vara_nr -> Vara(vara_nr)
Tabel 3: Vara
Vara (
vara_nr (PK),
vara_aadress,
rent, -- Rent on nüüd siin
omaniku_nr (FK -> Omanik)
)
Primaarvõti (PK): vara_nr
Välisvõti (FK): omaniku_nr -> Omanik(omaniku_nr)
Tabel 4: Omanik
Omanik (
omaniku_nr (PK),
omaniku_eesnimi,
omaniku_perenimi
)
Primaarvõti (PK): omaniku_nr
Näidisandmed (3NK)
Klient tabel
kliendi_nr(PK) | kliendi_eesnimi | kliendi_perenimi |
---|---|---|
K001 | Jaan | Tamm |
K002 | Mari | Maasikas |
K003 | Eva | Kuusk |
Rentimine tabel
vara_nr(PK)(FK) | kliendi_nr(FK) | rendi_algus(PK) | rendi_lopp |
---|---|---|---|
V001 | K001 | 2023-01-15 | 2024-01-14 |
V002 | K002 | 2023-03-01 | 2024-02-29 |
V003 | K001 | 2023-05-10 | |
V001 | K003 | 2024-02-01 | 2025-01-31 |
V004 | K002 | 2024-04-01 |
Vara tabel
vara_nr(PK) | vara_aadress | rent | omaniku_nr(FK) |
---|---|---|---|
V001 | Pikk tn 1, Tartu | 520€ | O001 |
V002 | Lai tn 5, Tallinn | 750€ | O002 |
V003 | Mere pst 8, Pärnu | 600€ | O003 |
V004 | Metsa tee 3, Viljandi | 480€ | O002 |
Omanik tabel
omaniku_nr(PK) | omaniku_eesnimi | omaniku_perenimi |
---|---|---|
O001 | Peeter | Kask |
O002 | Anna | Mänd |
O003 | Mari | Maasikas |
Tabelite viimine Boyce/Coddi normaalkujule (BCNF)
BCNF Definitsioon
Tabel on Boyce/Coddi normaalkujul (BCNF), kui iga funktsionaalse sõltuvuse (X -> Y) korral on X supervõti.
Praktikas tähendab see, et iga sõltuvuse determinant (vasak pool, X) peab olema kandidaatvõti.
Sõltuvused 3NK tabelites
Klient
fs_k: kliendi_nr -> (kliendi_eesnimi, kliendi_perenimi)
(Determinant on PK. BCNF.)
Rentimine
fs_r: (vara_nr, rendi_algus) -> (kliendi_nr, rendi_lopp)
(Determinant on PK. BCNF.)
Vara
fs_v: vara_nr -> (vara_aadress, rent, omaniku_nr)
(Determinant on PK. BCNF.)
Omanik
fs_o: omaniku_nr -> (omaniku_eesnimi, omaniku_perenimi)
(Determinant on PK. BCNF.)
Järeldus: Kõikides 3NK tabelites on ainsate funktsionaalsete sõltuvuste determinandid primaarvõtmed. Kuna primaarvõti on kandidaatvõti, siis kõik meie 3NK tabelid on juba ka BCNF kujul.
BCNF on rangem kui 3NK ja probleemid tekivad tavaliselt siis, kui tabelis on mitu kattuvat kandidaatvõtit ja sõltuvused ei lähe alati välja terviklikust kandidaatvõtmest.
(Andmete kuvamisel muudatusi ei ole võrreldes 3NK sammuga, seega tabeleid uuesti ei kuvata.)
Arutelu viienda normaalkuju (5NK) ja ortogonaalsuse üle
Viienda normaalkuju (5NK) reeglid
BCNF on sageli piisav. Vaatame siiski 5NK reegleid:
- Reegel 1: Kui tabel on BCNF ja mõni kandidaatvõti on lihtne (1 veerg), siis tabel on 4NK.
- Reegel 2: Kui tabel on 3NK/BCNF ja *iga* kandidaatvõti on lihtne, siis on tabel 5NK.
- Reegel 3: Kui tabel on BCNF ja sel on vähemalt üks atribuut, mis ei kuulu ühtegi kandidaatvõtmesse, siis tabel on 5NK.
Tabelite analüüs 5NK reeglite järgi
- Klient: BCNF, PK lihtne. 5NK (Reegel 2).
- Omanik: BCNF, PK lihtne. 5NK (Reegel 2).
- Vara: BCNF, PK lihtne. Sisaldab mitte-võtmeid (`vara_aadress`, `rent`, `omaniku_nr`). 5NK (Reegel 3 või 2).
- Rentimine: BCNF. PK pole lihtne. Sisaldab mitte-võtmeid (`kliendi_nr`, `rendi_lopp`). 5NK (Reegel 3).
Kõik tabelid näivad olevat ka 5NK.
Andmete liiasus ja ortogonaalsus
Probleem: Varjatud andmete liiasus
Vaatamata kõrgetele normaalkujudele, on endiselt probleem: Mari Maasikas.
- Tema nimi on tabelis
Klient
. - Tema nimi on tabelis
Omanik
.
See on liiasus. Nime muutmine nõuaks muutust kahes kohas.
Ortogonaalse andmebaasi disaini printsiip
Ortogonaalne disain: iga fakt ühes kohas. Tabelid sõltumatud. Praegu rikutud.
Lahendus: Isiku tabel
Lahendus: Loome keskse Isik
tabeli.
Tabel: Isik
Isik (isik_id (PK), eesnimi, perenimi)
Muudetud tabelid: Klient ja Omanik
Klient (kliendi_nr (PK), isik_id (FK -> Isik) (AK))
Omanik (omaniku_nr (PK), isik_id (FK -> Isik) (AK))
Tulemus: Isikuandmed ühes kohas. Rollid viitavad isikule. Liiasus kaotatud.
Järgmised sammud
Vaatame lõplikke tabeleid. Kollane tähistab Mari Maasika (I02) andmeid/rolle.
Lõplikud tabelid (pärast ortogonaalsuse rakendamist)
Pärast ortogonaalsuse printsiibi rakendamist ja keskse Isik
tabeli loomist on meie andmebaasi struktuur ja andmed järgmised.
Roheline taust näitab algsest 1NK esimesest reast pärit andmeid. Kollane taust näitab isiku I02 (Mari Maasikas) andmeid/rolle tabelites Isik
, Klient
ja Omanik
. Tabelis Vara
on nüüd veerg `rent`.
Lõplik tabelite struktuur
Tabel: Isik
Isik (
isik_id (PK),
eesnimi,
perenimi
)
Primaarvõti (PK): isik_id
Tabel: Klient
Klient (
kliendi_nr (PK),
isik_id (FK -> Isik) (AK)
)
Primaarvõti (PK): kliendi_nr
Alternatiivvõti (AK): isik_id
Välisvõti (FK): isik_id -> Isik(isik_id)
Tabel: Omanik
Omanik (
omaniku_nr (PK),
isik_id (FK -> Isik) (AK)
)
Primaarvõti (PK): omaniku_nr
Alternatiivvõti (AK): isik_id
Välisvõti (FK): isik_id -> Isik(isik_id)
Tabel: Vara
Vara (
vara_nr (PK),
vara_aadress,
rent, -- Vara rendihind
omaniku_nr (FK -> Omanik)
)
Primaarvõti (PK): vara_nr
Välisvõti (FK): omaniku_nr -> Omanik(omaniku_nr)
Tabel: Rentimine
Rentimine (
vara_nr (PK osa) (FK -> Vara),
kliendi_nr (FK -> Klient),
rendi_algus (PK osa),
rendi_lopp
)
Primaarvõti (PK): (vara_nr, rendi_algus)
Välisvõti (FK): kliendi_nr -> Klient(kliendi_nr)
Välisvõti (FK): vara_nr -> Vara(vara_nr)
Lõplikud näidisandmed
Isik tabel
isik_id(PK) | eesnimi | perenimi |
---|---|---|
I01 | Jaan | Tamm |
I02 | Mari | Maasikas |
I03 | Peeter | Kask |
I04 | Anna | Mänd |
I05 | Eva | Kuusk |
Klient tabel
kliendi_nr(PK) | isik_id(FK)(AK) |
---|---|
K001 | I01 |
K002 | I02 |
K003 | I05 |
Omanik tabel
omaniku_nr(PK) | isik_id(FK)(AK) |
---|---|
O001 | I03 |
O003 | I02 |
O002 | I04 |
Vara tabel
vara_nr(PK) | vara_aadress | rent | omaniku_nr(FK) |
---|---|---|---|
V001 | Pikk tn 1, Tartu | 520€ | O001 |
V002 | Lai tn 5, Tallinn | 750€ | O002 |
V003 | Mere pst 8, Pärnu | 600€ | O003 |
V004 | Metsa tee 3, Viljandi | 480€ | O002 |
Rentimine tabel
vara_nr(PK)(FK) | kliendi_nr(FK) | rendi_algus(PK) | rendi_lopp |
---|---|---|---|
V001 | K001 | 2023-01-15 | 2024-01-14 |
V002 | K002 | 2023-03-01 | 2024-02-29 |
V003 | K001 | 2023-05-10 | |
V001 | K003 | 2024-02-01 | 2025-01-31 |
V004 | K002 | 2024-04-01 |
Kokkuvõte
Selle lõpliku struktuuriga on isikuandmed hoitud ühes kohas (Isik
tabel). Vara omadused, sh `rent`, on tabelis Vara
. Konkreetsed rendiperioodid on tabelis Rentimine
.