Normaalimuodot
Aiemmassa luvussa todettiin, että normalisoidun relaation rakenne vähentää turhaa toisteisuutta sekä auttaa välttämään ristiriitaisuuksien syntymistä datassa. Normalisointi ei kuitenkaan ole kaksiarvoinen käsite: relaatio ei ole vain joko normalisoimaton tai normalisoitu. Mahdollisia poikkeamia ja toisteisuuksia on erilaisia, ja joskus eri tapauksissa voidaan hyväksyä, että jokin relaation rakenne on enemmän "optimoitu" kuin toinen.
Relaatioiden normalisointi määritellään ja suoritetaan tasoittain. Normaalimuoto on joukko relaation rakenteeseen liittyviä vaatimuksia, joita relaatio voi täyttää. Vaatimukset esitellään yleensä funktionaalisten riippuvuuksien kautta. Relaatio, joka täyttää tietyn normaalimuodon vaatimukset, saa normaalimuodon mukaisia takeita poikkeamien sietokyvystä tai olemassaolosta. Normaalimuotoja on useita; tässä luvussa esitellään seuraavat normaalimuodot niiden vahvuusjärjestyksessä:
- Ensimmäinen normaalimuoto (1NF)
- Toinen normaalimuoto (2NF)
- Kolmas normaalimuoto (3NF)
- Boyce/Codd-normaalimuoto (BCNF)
- Neljäs normaalimuoto (4NF)
Korkeammat normaalimuodot tarjoavat vahvempia takeita poikkeamien puuttumisesta tai sietokyvystä. Ensimmäinen normaalimuoto on perusmuoto, jonka jokaisen relaatiomallin mukaisen tietokannan tulee täyttää. Seuraavat muodot määritellään aina edeltävien muotojen kautta: relaatio joka täyttää 3NF täyttää myös 2NF, ja relaatio, joka täyttää 2NF täyttää myös 1NF.

Normalisointi on aina relaatiokohtainen prosessi, eikä kaikkien tietokannan relaatioiden ei tarvitse olla samassa normaalimuodossa. Normalisointi myös yleensä suoritetaan tasoittain: ensin varmistetaan 1NF vaatimukset, sitten 2NF, jne. kunnes relaatio on normalisoitu halutulle tasolle. Jos relaatio ei täytä normaalimuodon vaatimuksia, relaatio voi korjata esimerkiksi jakamalla relaatio kahdeksi tai useammaksi relaatioksi. Jaot ja muutokset tulee kuitenkin tehdä niin, että kohdealueen dataa ei katoa.
Seuraavaksi esitellään neljän ensimmäisen normaalimuotojen vaatimuksia sekä ohjeet relaation saattamiseksi niihin. Mainittakoon, että tässä materiaalissa annetut vaatimukset eivät ole formaaleja, vaan ovat eri kirjailijoiden muotoilujen ([34], [13], [21]) perusteella tehtyjä yhteenvetoja.
Ennen jokaista muotoa esitellään esimerkkitaulu ja pohditaan, millaisia poikkeamia taulussa voi syntyä.
Huomautus
Luvussa 5.1 esiteltiin päättelysääntöjä, joiden perusteella funktionaalisia riippuvuuksia voi yhdistää. Riippuvuuksia, joita voi päätellä päättelysäännöillä ovat triviaaleja. Jos riippuvuutta ei voi keksiä päättelysäännöllä, vaan taulun tai kohdealueen perusteella, se on epätriviaali.
Tässä luvussa esitetyt vaatimukset koskevat aina epätriviaaleja riippuvuuksia ellei toisin sanota.
Ensimmäinen normaalimuoto
Esimerkki
Webbikauppa.com on erilaisia kodintarvikkeita ja sähkölaitteita myyvä yritys. Yritykselle perustetaan verkkosivu, jonka kautta asiakkaat voivat ostaa tuotteita.
Yritys asetti seuraavia vaatimuksia tietokannalle:
- Asiakkaat, jotka haluavat osaa tavaroita verkkosivulta, tekevät itselleen ensin käyttäjätunnuksen.
- Sen jälkeen asiakkaat voivat lisätä tavaroita ostoskoriin ja ostaa niitä.
- Ostoskori tallennetaan tietokantaan, jotta käyttäjä voi helposti katsella ostoskoriaan eri laitteilla (tietokone, puhelin) ja jotta käyttäjä voisi halutessaan käydä tilaamassa tuotteet suoraan kivijalkaliikkeestä.
Sovellusta ja tietokantaa tekevä koodari päättyi tekemään taulun KÄYTTÄJÄ
, joka sisältää käyttäjän tiedot ja hänen ostoskorinsa. Tauluun on saatu seuraavaa mallidataa:
käyttäjätunnus | ostoskori |
---|---|
matti123 | 2 kpl tuote-00000001, 3 kpl tuote-00000002 |
hilda02 | 12 kpl tuote-00000003 |
asko_96 | 3 kpl tuote-00000004, 1 kpl tuote-00000002, 8 kpl tuote-00000003, 1 kpl tuote-00000001 |
Taulua luodessa taululle ei asetettu perusavainta. Tuotteet kuvataan tuotteen EAN-viivakoodilla (muotoa tuote-12345678
), joiden tiedot löytyvät muualta.
Harjoittele
Katsele yllä olevaa taulua ja mallidataa. Pohdi, millaisia poikkeamia tai mahdollista toisteisuutta voi syntyä. Muista, että poikkeama on tiedon lisäyksestä, muokkauksesta tai poistosta johtuva syntyvä ristiriita taulun tiedoissa.
Mitä muita mahdollisia ongelmia voi syntyä, kun yllä olevaa taulua yrittää käyttää?
Kun olet pohtinut asiaa, voit lukea selityksen alla:
Tässä ydinongelmana ovat perusavaimen puuttuminen sekä ostoskorin arvojen moniarvoisuus. Tästä voi syntyä kaikenlaisia poikkeamia:
- Tauluun voidaan lisätä rivejä, joilla on sama käyttäjätunnus. Koska perusavainta ei valittu, voidaan esimerkiksi lisätä esim. 5 riviä, jossa
käyttäjätunnus
on matti123. SQL-speksin mukaan tämä sallitaan, mutta silloin käyttäjällä matti123 olisi montaa ostoskoria. - Jos ostoskorin tyhjentää poistamalla rivi, käyttäjätunnus katoaa kokonaan tietokannasta.
- Tuotteen EAN-koodi saattaa muuttua. Silloin pitäisi käydä käyttäjien kaikki ostoskorit läpi ja korjata EAN-koodi, mikä voi olla virhealtista. Voi vahingossa jättää päivittämättä jonkun ostoskorissa olevan tuotteen EAN-koodia, eikä siitä tule mitään varoitusta.
Ongelmia toisteisuudessa:
- Periaatteessa EAN-koodeja ja kappalemääriä joutuu toistamaan. Toisaalta ilman sitä ei voida esittää ostoskorin tietoja. Toisteisuus voi kuitenkin aiheuttaa yllä mainitun muokkauspoikkeamaan.
Muita ongelmia:
- Ostoskorin tietotyyppi ei ole selkeä. Tarkemmin, ei ole selvää, mihin SQL-kielen mukaisen tietotyyppiin (Luku 4.3) se kuuluisi. Ostoskorissa on nyt kappalemääriä (luku), mutta myös EAN-koodeja (merkkijono tai numero).
- Jatkoksi, on vaikeaa tarkastella useita eri ostoskoreja. Jos haluttaisiin tietää, ketkä ovat tilanneet tuotetta "tuote-0000003" ja ostoskori olisi esim. merkkijono, joutuisi hakemaan EAN-koodeja merkkijonoista. Tämä voi taas olla virhealtista.
- Ostoskoreja on vaikeaa ylläpitää. Jos ne on merkkijonona, ostoskorit "1 kpl tuote-01, 1 kpl tuote-01" sekä "2 kpl tuote-01" tarkoittaisivat samaa asiaa. Tämä ei sinänsä ole toisteisuutta tai poikkeamaa, mutta sama tieto kuvataan kahdella eri tavalla, mikä vaikeuttaa tietokannan ylläpitoa.
Ensimmäinen normaalimuoto liittyy siis Luvussa 3.1 esiteltyihin relaatiomallin perusominaisuuksiin. Esimerkin mukaisiin ongelmiin voidaan päästä ainakin SQL-spesifikaation mukaisissa tietokannoissa, sillä spesifikaatio esimerkiksi ei vaadi, että taululla olisi perusavainta. Lisäksi monen arvon säilyttäminen yhdessä sarakkeessa johtaa helposti tietokannan käytön ongelmiin.
Ensimmäisen normaalimuoto käytännössä vaatii, että taulu on suunniteltu relaatiomallin sääntöjen mukaisesti.
Määritelmä
Relaatio on 1NF:ssä, jos se täyttää relaation vaatimukset:
- Relaatiolle tulee olla valittuna sopiva perusavain.
- Monikoiden (eli rivien) järjestyksellä ei saa olla merkitystä tiedon merkityksen kannalta.
- Jokaisella attribuutilla (eli sarakkeella) tulee olla yksi selkeä tietotyyppi. Erityyppisen datan sekoittaminen samassa attribuutista on kielletty.
- Jokainen attribuutti on atominen, eli ei sisällä montaa arvoa.
Jos relaatio ei täytä 1NF:ää, korjausvaiheet ovat:
- Jaa moniarvoiset attribuutit niin, että jokaiselle arvolle tulee oma rivi.
- Tarvittaessa lisää attribuutteja niin, että jokainen on selkeää tietotyyppiä.
- Valitse sopiva perusavain.
Esimerkki
Nyt selvästi yllä oleva esimerkki ei täytä 1NF.
Korjaukseksi ostoskori tulee jakaa kahteen attribuuttiin: ostoskori_tuote
(EAN-koodi) ja tuote_määrä
(numero). Lisäksi ostoskorin jokainen tuote tallennetaan omalle riville. Saadaan tulokseksi:
käyttäjätunnus | ostoskori_tuote | tuote_määrä |
---|---|---|
matti123 | tuote-00000001 | 2 |
matti123 | tuote-00000002 | 3 |
hilda02 | tuote-00000003 | 12 |
asko_96 | tuote-00000004 | 3 |
asko_96 | tuote-00000002 | 1 |
asko_96 | tuote-00000003 | 8 |
asko_96 | tuote-00000001 | 1 |
Lisäksi valitaan perusavaimeksi {käyttäjätunnus, ostoskori_tuote}
, jolloin jokaisella käyttäjällä voi olla usea tuote ostoskorissa, mutta samalle tuotteelle ei voi vahingossa lisätä toista riviä (vaan pitää päivittää lukumäärä). Lisäksi jos tuotteen tunnuksesta tehtäisiin viiteavain tauluun, joka sisältää kaikkien tuotteiden tieto, käyttäjille ei voisi edes vahingossa lisätä tuotteita, joita ei ole olemassa. Lisäksi RDBMS silloin automaattisesti hoitaisi mahdolliset EAN-koodien muutokset tai tuotteiden poistot.
Nyt taulu on 1NF:ssä. Huomaa, että 1NF ei ratkaise kaikkia poikkeamia: esimerkiksi jos tyhjentää hilda02:n ostoskorin poistamalla rivin, hilda02 häviää koko tietokannasta.
Toinen normaalimuoto
Esimerkki
Jatketaan yllä olevaa esimerkkiä. Yrityksen Webbikauppa.com verkkosivuja on kehitetty eteenpäin. Yritys haluaisi lisätä verkkosivuille erilaisia asiakastyyppejä, joilla olisi erilaisia bonuksia ja alennuksia. Yritys miettii alkuun kolmea asiakastyyppiä: "Perusasiakas", "Bonusasiakas" sekä "VIP-asiakas".
Sovellusta ja tietokantaa tekevä koodari päätti hetken pohdittuaan yksinkertaisesti laajentaa vaatimuksen perusteella KÄYTTÄJÄ
-taulun lisäämällä siihen sarakkeen käyttajatyyppi
. Tuloksena syntyi seuraava päivitetty taulu (perusavainsarakkeet alleviivattu):
käyttäjätunnus | ostoskori_tuote | tuote_määrä | käyttäjätyyppi |
---|---|---|---|
matti123 | tuote-00000001 | 2 | Perusasiakas |
matti123 | tuote-00000002 | 3 | Perusasiakas |
hilda02 | tuote-00000003 | 12 | VIP-asiakas |
asko_96 | tuote-00000004 | 3 | Bonusasiakas |
asko_96 | tuote-00000002 | 1 | Bonusasiakas |
asko_96 | tuote-00000003 | 8 | Bonusasiakas |
asko_96 | tuote-00000001 | 1 | Bonusasiakas |
Harjoittele
Katsele yllä olevaa taulua ja mallidataa. Pohdi, millaisia poikkeamia tai mahdollista toisteisuutta voi syntyä. Muista, että poikkeama on tiedon lisäyksestä, muokkauksesta tai poistosta johtuva syntyvä ristiriita taulun tiedoissa.
Mitä muita mahdollisia ongelmia voi syntyä, kun yllä olevaa taulua yrittää käyttää?
Kun olet pohtinut asiaa, voit lukea selityksen alla:
Tässä tapauksessa taulu on kyllä 1NF:ssä, mutta silti vaaroja poikkeamille on suuria.
Mahdolliset poikkeamat:
- Jos hilda02:n ostoskori tyhjennettäisiin poistamalla hänen rivinsä on kaksi vaaraa: ensiksi, hilda02 katoaa tietokannasta käyttäjänä. Jos haluttaisiin tietää hilda02:n asiakastyypin, se katoaa ostoskorin poiston yhteydessä. Toiseksi, jos hilda02:n rivi poistuisi ja tietokannalta kysyttäisiin, mitä eri asiakastyyppejä on olemassa, tulisi vastauksena vain kaksi asiakastyyppiä kolmen mahdollisen sijaan.
- Jos matti123:n asiakastyyppi päivittyy (esim. VIP-asiakkaaksi), joutuu päivittämään kaikki rivit, jotka kuuluu matti123:lle. Jos vahingossa jonkun matti123 rivin asiakastyyppiä ei päivitetä, matti123:n asiakastyypistä tulee ristiriitainen.
- Vastaavasti jos matti123 lisää uusia tuotteita ostoskoriin, syntyy uusia rivejä. Taas, silloin voi vahingossa lisätä rivi, jossa matti123:n asiakastyyppi on eri kuin matti123:n muilla riveillä. Päädytään jälleen ristiriitaan.
Toisteisuuden ongelmia:
- Kuten jo mainittu, yksittäisen käyttäjän asiakastyyppiä joutuu toistamaan jokaisen ostoskoriin lisätyn tuotteen yhteydessä.
Muita ongelmia:
- Jos meille tulee uusi asiakas, hänen asiakastyyppinsä ei saada tallennettua kunnes asiakas lisää jotain tuotetta ostoskoriin.
Toisen normaalimuotoon tarvitaankin jo funktionaalisia riippuvuuksia. Tarkemmin, 2NF liittyy siihen, miten relaatiossa olevien funktionaalisten riippuvuuksien määräävä joukko ja relaation perusavain liittyvät toisiinsa.
Määritelmä
Relaatio on 2NF:ssä, jos se täyttää seuraavat vaatimukset:
- Relaatio on 1NF:ssä.
- Mikään relaation perusavaimeen kuulumaton attribuutti ei riipu ainoastaan perusavaimen osasta, vaan koko perusavaimesta.
Toisin sanoen, jos on voimassa funktionaalinen riippuvuus {X} -> {Y}
, jossa X
:ssä on relaation perusavaimeen kuuluvat attribuutit ja Y
ei kuulu perusavaimeen, niin X
:stä ei voi ottaa pois perusavaimeen kuuluvia attribuutteja niin, että funktionaalinen riippuvuus säilyisi. Tällaista funktionaalista riippuvuutta kutsutaan täydeksi funktionaaliseksi riippuvuudeksi.
Huomaa, että 2NF:ssä tulee tarkastaa vain ne funktionaaliset riippuvuudet, joiden määräävässä joukossa on mukana kaikki perusavaimeen kuuluvat attribuutit. Sellaiset riippuvuudet, jonka määräävässä joukossa on perusavaimeen kuulumattomia attribuutteja, eivät riko 2NF:ää. Seurauksena, jos relaation perusavaimessa on vain yksi attribuutti, relaatio on aina 2NF:ssä.
Toinen tapa ajatella 2NF on: perusavaimen ja perusavaimeen kuulumattomien attribuuttien välisen funktionaalisen riippuvuuden tulee olla epätriviaali (eli ei voi johtaa päättelysäännöistä). Erityisesti perusavainta ei voida jakaa osiin niin, että jotain perusavaimeen ja muiden attribuuttien riippuvuuksia säilyisi.
Jos relaatio ei täytä 2NF, sen saa korjattua tekemälle jokaiselle 2NF:ää rikkovalle funktionaaliselle riippuvuudelle oman relaation, jossa on funktionaaliseen riippuvuuden osapuolina olevat attribuutit. Määräävästä joukosta tulee uuden relaation perusavain.
Esimerkki
Jatketaan yllä olevaa esimerkkiä. Mietitään läpi, mitä kaikkia funktionaalisia riippuvuuksia taulusta löytyy ja miten ne täyttää 2NF:
{käyttäjätunnus, ostoskori_tuote} -> {tuote_määrä}
- "Jos tiedetään tietyn käyttäjän tunnus ja tuote, tiedetään, kuinka monta kappaletta kyseinen käyttäjä on tilannut kyseistä tuotetta"
- 2NF on OK: määräävä attribuuttijoukko on
{käyttäjätunnus, ostoskori_tuote}
, joka on sama kuin relaation perusavain JA jos siitä otettaisiin jompikumpi attribuuteista pois, riippuvuus ei enää voisi päteä.
{käyttäjätunnus} -> {käyttäjätyyppi}
- "Jos tiedetään tietyn käyttäjän tunnus, tiedetään tämän käyttäjän käyttäjätyyppi"
- 2NF FAIL: Määräävä attribuuttijoukko on
{käyttäjätunnus}
, joka on vain osa relaation perusavainta eikä koko perusavain. Toisin sanoin riippuvuus{käyttäjätunnus, ostoskori_tuote} -> {käyttäjätyyppi}
on triviaali.
Jotta 2NF täyttyy, pitää relaatio jakaa. Tehdään kahdelle funktionaaliselle riippuvuudelle omat relaatiot:
KÄYTTÄJÄ_OSTOSKORI(käyttäjätunnus, ostoskori_tuote, tuote_määrä)
, joka vastaa funktionaalista riippuvuutta {käyttäjätunnus, ostoskori_tuote} -> {tuote_määrä}
:
käyttäjätunnus | ostoskori_tuote | tuote_määrä |
---|---|---|
matti123 | tuote-00000001 | 2 |
matti123 | tuote-00000002 | 3 |
hilda02 | tuote-00000003 | 12 |
asko_96 | tuote-00000004 | 3 |
asko_96 | tuote-00000002 | 1 |
asko_96 | tuote-00000003 | 8 |
asko_96 | tuote-00000001 | 1 |
KÄYTTÄJÄ(käyttäjätunnus, käyttäjätyyppi)
, joka vastaa funktionaalista riippuvuutta {käyttäjätunnus} -> {käyttäjätyyppi}
:
käyttäjätunnus | käyttäjätyyppi |
---|---|
matti123 | Perusasiakas |
hilda02 | VIP-asiakas |
asko_96 | Bonusasiakas |
Nyt saatiin kaksi taulua, joissa turha toisteisuus on poistettu sekä ratkaistu poikkeamiin liittyviä murheita:
- Käyttäjille on oma taulu, joten nyt käyttäjä ei häviä tietokannasta vaikka hänellä ei olisi ostoskoria.
- Vastaavasti käyttäjiä voidaan lisätä tietokantaan ilman että niillä olisi mitään ostoskorissa.
- Jokaisella asiakkaalla käyttäjätyyppi ei toistu turhaan, eikä ole mahdollisuutta ristiriitaan.
- Jos vielä määritetään sopivat viiteavaimet (esim.
KÄYTTÄJÄ_OSTOSKORI.käyttäjätunnus -> KÄYTTÄJÄ.käyttäjätunnus
), saataisiin RDBMS:n takaaman viite-eheyden ja sen hienoudet: ostoskoreja ei voisi lisätä olemattomille käyttäjille, käyttäjän tunnuksen muutos tai poisto saisi RDBMS:n automaattisesti käsittelemään ostoskorin muutokset tai poistot.
Periaatteessa jotain pieniä vaaran paikkoja vielä voi löytyä. Esimerkiksi on mahdollista, että tietokannassa olisi vain perusasiakkaita, jolloin muista käyttäjätyypeistä ei olisi tietoa. Tosin kohdealueesta riippuen tämä ei välttämättä ole kriittinen asia. Jos se on, seuraava normaalimuoto ottaa kantaa tähänkin tapaukseen.
Kolmas normaalimuoto
Esimerkki
Webbikauppa.com-yrityksen verkkosivujen kehitys jatkuu. Yritys haluaisi selkeyttää käyttäjätyyppien ansaitsemista lisäämällä sovellukseen numeerisen bonustason. Bonustaso on numero 1-9, ja bonustaso nousee aina, kun asiakas tilaa tuotteita. Jatkossa käyttäjän käyttäjätyyppi määräytyisi bonustason perusteella seuraavasti:
- Bonustaso 1-3: Peruskäyttäjä
- Bonustaso 4-6: Bonusasiakas
- Bonustaso 7-9: VIP-asiakas
Sovellusta ja tietokantaa tekevä koodari toteaa, että käyttäjän bonustaso on selvästi käyttäjän ominaisuus kuten käyttäjätyyppi, ja siten se kuuluu KÄYTTÄJÄ
-tauluun. Siispä koodari lisää tauluun sarakkeen bonustaso
. Taulu näyttää muutoksen jälkeen seuraavalta:
käyttäjätunnus | käyttäjätyyppi | bonustaso |
---|---|---|
matti123 | Perusasiakas | 3 |
hilda02 | VIP-asiakas | 8 |
asko_96 | Bonusasiakas | 5 |
Tällä kertaa koodari on valistunut normalisoinnista. Koodari toteaa, että taulu on selvästi edelleen 1NF:ssä, sillä perusavain ei muuttunut, bonustaso
-sarakkeella on selkeä tietotyyppi (numero), eikä uusi sarake sisällä moniarvoisia arvoja.
Koodari vielä miettii läpi funktionaaliset riippuvuudet. Hän toteaa, että perusavaimesta tulee funktionaalinen riippuvuus
{käyttäjätunnus} -> {käyttäjätyyppi, bonustaso}
Tämä ei riko 2NF, sillä määräävä attribuuttijoukko on koko perusavain eikä sitä voi jakaa osiin enempää. Lisäksi koodari toteaa, että nyt bonustason perusteella voidaan päätellä yksikäsitteisesti käyttäjän tyyppi, eli syntyi uusi funktionaalinen riippuvuus:
{bonustaso} -> {käyttäjätyyppi}
Tämäkään ei kuitenkaan riko 2NF:ää, sillä määräävä attribuuttijoukko {bonustaso}
ei ole ollenkaan perusavaimessa. 2NF:n vaatimukset koskevat vain niitä funktionaalisia riippuvuuksia, joissa määräävässä attribuuttijoukossa on perusavaimeen kuuluvia attribuutteja.
Harjoittele
Katsele yllä olevaa taulua ja mallidataa. Pohdi, millaisia poikkeamia tai mahdollista toisteisuutta voi syntyä. Muista, että poikkeama on tiedon lisäyksestä, muokkauksesta tai poistosta johtuva syntyvä ristiriita taulun tiedoissa.
Mitä muita mahdollisia ongelmia voi syntyä, kun yllä olevaa taulua yrittää käyttää?
Kun olet pohtinut asiaa, voit lukea selityksen alla:
Kuten on todettu, relaatio täyttää 2NF:n vaatimukset. Poikkeamia voi silti syntyä.
Mahdolliset poikkeamat:
- Jos matti123:n bonustaso kasvatetaan seuraavalle tasolle, hänen käyttäjätyyppinsä pitäisi myös muuttua. Se voi kuitenkin unohtua, jolloin päästään ristiriitaan, jossa matti123:n bonustaso ei vastaa hänen käyttäjätyyppinsä.
- Samaan liittyen, jos joskus käyttäjätyyppejä halutaan muuttaa (esim. määrittää, että VIP-asiakkuuden saa ainoastaan tasolla
9
), joudutaan käymään jokaisen käyttäjän läpi ja päivittämään hänen käyttäjätyyppinsä uusien sääntöjen perusteella. Jos jonkun käyttäjän käyttäjätyyppiä ei päivitetä oikein, joudutaan ristiriitaan uusien sääntöjen kanssa.
Toisteisuuden ongelmia:
- Käyttäjätyyppi nyt toistuu turhaan, sillä käyttäjätyyppi voidaan päätellä suoraan bonustasosta.
Muita ongelmia:
- Vastaavasti kuten aiemminkin, ei ole vielä helppoa tapaa kartoittaa, mitä käyttäjätyyppejä on olemassa ja mitkä ovat niiden vastaavat tasot. Sinänsä ei ongelma, mutta käytännössä joko tietokantaa käyttävän sovelluksen tai tietokannan ylläpitäjän pitää itse tietää, miten bonustasot ja käyttäjätyypit menevät. Tämä ei välttämättä ole ongelma, mutta ristiriitojen välttämiseksi olisi parempi, että bonustason ja käyttäjätyypin vastaavuus olisi määritelty suoraan tietokannan rakenteeseen.
Kolmannen normaalimuodon ytimessä ovat ns. transitiiviset funktionaaliset riippuvuudet, eli riippuvuudet, jotka riippuvat perusavaimesta epäsuorasti toisen attribuutin kautta. Yllä olevassa esimerkissä käyttäjätyyppi
riippuu bonustasosta ({bonustaso} -> {käyttäjätyyppi}
), joka puolestaan riippuu perusavaimesta ({käyttäjätunnus} -> {bonustaso}
). Eli voidaan sanoa, että käyttäjätyyppi riippuu transitiivisesti käyttäjätunnuksesta; se voidaan merkitä seuraavasti:
{käyttäjätunnus} -> {bonustaso} -> {käyttäjätyyppi}
Määritelmä
Relaatio on 3NF:ssä, jos kaikki seuraavat vaatimukset pätevät:
- Relaatio on 2NF:ssä.
- Kaikki relaation perusavaimeen kuulumattomat attribuutit riippuvat vain, ainoastaan ja kokonaan perusavaimesta eikä mistään muusta attribuutista.
Poikkeus: Jos funktionaalisen riippuvuuden määräävä attribuuttijoukko on relaation avainehdokas (eli se yksilöi kaikki relaation attribuutit), se ei riko 3NF:ää.
Toisin sanoen 3NF vaatii, että jokainen perusavaimeen kuulumaton attribuutti riippuu vain perusavaimesta. Ainoat funktionaaliset riippuvuudet, jotka 3NF käytännössä sallii, on avainehdokkaista johtuvat riippuvuudet.
Huomaa erityisesti, että vaikka 2NF:n vaatimukset automaattisesti täyttyvät relaatioissa, joissa on vain yhden attribuutin perusavain, 3NF:n vaatimus ei välttämättä päde.
3NF:n vaatimus voi halutessaan muotoilla myös transitiivisen riippuvuuden määritelmän kautta: relaatio on 3NF:ssä, jos mikään relaatioon perusavaimeen kuulumaton attribuutti ei ole transitiivisesti riippuvainen relaation perusavaimesta.
Jos relaatio ei ole 3NF:ssä, se korjataan käytännössä tekemällä rikkoville funktionaalisille riippuvuuksille omat relaatiot. Taas, tässä tapauksessa voi käyttää omaa harkintaa sen mukaan, kuinka monta relaatiota tekee.
Esimerkki
Palataan yllä olevaan esimerkkiin ja korjataan se 3NF:ää täyttäväksi.
Kuten todettiin, uudella taululla KÄYTTÄJÄ(käyttäjätunnus, käyttäjätyyppi, bonustaso)
on kaksi funktionaalista riippuvuutta:
{käyttäjätunnus} -> {bonustaso}
- "Jos tiedetään käyttäjän tunnus, tiedetään hänen bonustaso."
- 3NF OK: Määräävä joukko on
{käyttäjätunnus}
, joka on perusavain.
{bonustaso} -> {käyttäjätyyppi}
- "Jos tiedetään käyttäjän bonustaso, tiedetään hänen käyttäjätyyppi."
- 3NF FAIL: Määräävä joukko on
{bonustaso}
, joka ei ole relaation perusavain.
Tässä perusavaimesta johtuva riippuvuus {käyttäjätunnus} -> {bonustaso, käyttäjätyyppi}
on triviaali, sillä sen voi johtaa transitiivisuussäännöstä.
Ratkaisuna siis poistetaan KÄYTTÄJÄ
-taulusta sarake käyttäjätyyppi
, jolloin lopputulos on:
käyttäjätunnus | bonustaso |
---|---|
matti123 | 3 |
hilda02 | 8 |
asko_96 | 5 |
Lisäksi luodaan rikkovalle funktionaaliselle riippuvuudelle {bonustaso} -> {käyttäjätyyppi}
oma taulu BONUSTASO_TYYPPI
ja syötetään sinne bonustasojen ja käyttäjätyyppien väliset yhteydet:
bonustaso | käyttäjätyyppi |
---|---|
1 | Perusasiakas |
2 | Perusasiakas |
3 | Perusasiakas |
4 | Bonusasiakas |
5 | Bonusasiakas |
6 | Bonusasiakas |
7 | VIP-asiakas |
8 | VIP-asiakas |
9 | VIP-asiakas |
Tällä ratkaisulla oikeastaan kaikki murheet korjautuu:
- Kun käyttäjän bonustaso nousee ei tarvitse samalla päivittää käyttäjätyyppiä, sillä sen saa pääteltyä eri taulusta.
- On helppoa muuttaa bonustasojen sääntöjä: on helppoa lisätä uusia bonustasoja tai muuttaa käyttäjätyyppien nimityksiä muokkaamalla vain
BONUSTASO_TYYPPI
-taulun rivejä - Bonustasosta voidaan tehdä nyt viiteavain
KÄYTTÄJÄ.bonustaso -> BONUSTASO_TYYPPI.bonustaso
, jolloin saadaan kaikki RDBMS:n tarjoamat viite-eheystarkastukset: käyttäjän bonustaso ei voisi enää vahingossa asettaa muuksi numeroksi kuin 1-9; bonustason poisto ja muokkaus välittyy automaattisesti käyttäjiin, joita muutos koskee. - Plussana on, että tietokantaan on koodattu kaikki mahdolliset käyttäjätyypit. On tosi helppoa selvittää, mitä eri käyttäjätyyppejä on olemassa.
Boyce/Codd-normaalimuoto
Boyce/Codd-normaalimuoto on vain pieni muunnelma 3NF:stä, joka karsii pois lopullisetkin "puutteelliset" riippuvuudet. Esitellään heti alkuun BCNF:n määritelmä vertailuksi:
Määritelmä
Relaatio on BCNF:ssä, jos seuraavat vaatimukset pätevät:
- Relaatio on 3NF:ssä.
- Kaikki relaation attribuutit riippuvat vain, ainoastaan ja kokonaan jostain avainehdokkaasta eikä mistään muusta attribuutista.
Toisin sanoen, funktionaaliset riippuvuudet, jossa määräävä joukko ei ole relaation jokin avainehdokas, on kielletty.
Huomaa vaatimusero 3NF:n ja BCNF:n välillä:
- 3NF: "Kaikki relaation perusavaimeen kuulumattomat attribuutit riippuvat vain, ainoastaan ja kokonaan perusavaimesta eikä mistään muusta attribuutista."
- BCNF: "Kaikki relaation attribuutit riippuvat vain, ainoastaan ja kokonaan jostain avainehdokkaasta eikä mistään muusta attribuutista."
Huomaa, että BCNF vaatii, että relaatiosta tunnistetaan kaikki avainehdokkaat (eli attribuuttijoukot, jotka yksilöivät relaation kaikki muut attribuutit). Muista, että perusavain on myös avainehdokas. Toisaalta BCNF:n määritelmä on hieman yleisempi ja voi olla helpompi muistaa.
Käytännössä ero BCNF:n ja 3NF:n välillä on hyvin pieni. Hyvin usein 3NF:ssä olevat relaatiot ovat myös BCNF:ssä. Yleensä BCNF menee rikki tapauksissa, jossa relaatiossa on useampi "päällekkäinen" avainehdokas, eli jotkut attribuutit esiintyvät useassa eri avainehdokkaassa.
Katsotaan kuitenkin eräs esimerkki, jossa 3NF pätee, mutta BCNF ei.
Esimerkki
Eräs tapaus, jossa BCNF ei välttämättä päde, on resurssien tai aikojen samanaikaisuuteen liittyvissä vaatimuksissa.
Webbikauppa.com-yritys haluaa tarjota isoille yritysasiakkaille mahdollisuuden varata aikoja konsultointiin (esim. hintavertailu, isojen ostosten suunnittelu, yms). Konsultointi pidetään tiettynä päivänä ja kellonaikana kaupan kivijalkaliikkeessä ja sitä varten varataan huone ja myyjä. Myyjä varaa huoneensa koko päiväksi. Eri päivinä samalla myyjällä voi olla eri huone, sillä huoneet voivat olla varattu.
Asiakkaalle varataan vain yksi konsultointi samalle päivälle, jotta kaikille asiakkaille olisi tilaa.
Konsultointeja varten tietokantaan tulee lisätä taulu, jossa pidetään kirjaa konsultoinnin tiedoista (aika, asiakas, kuka myyjä hoitaa, huone).
Vaatimusten perusteella koodari loi seuraavan taulun:
käyttäjätunnus | päivä | kellonaika | myyjätunnus | huonenro |
---|---|---|---|---|
matti123 | 2025-01-30 | 12:00 | myyjä01 | huone101 |
matti123 | 2025-02-10 | 12:00 | myyjä02 | huone102 |
hilda06 | 2025-01-30 | 15:00 | myyjä01 | huone101 |
asko_96 | 2025-03-01 | 13:00 | myyjä03 | huone100 |
Taulun ja vaatimusten perusteella koodari päätteli seuraavat funktionaaliset riippuvuudet:
{käyttäjätunnus, päivä} -> {kellonaika, myyjätunnus, huonenro}
- "Jos tiedetään käyttäjän tunnus ja konsultoinnin päivä, tiedetään konsultointiin osallistuvan myyjän, varatun huoneen ja konsultoinnin kellonajan"
- Tämän perusteella
{käyttäjätunnus, päivä}
on avainehdokas, se on myös tässä tapauksessa perusavain
{myyjätunnus, päivä, kellonaika} -> {käyttäjätunnus, huonenro}
- "Jos tiedetään myyjän tunnus ja konsultoinnin päivämäärä, tiedetään konsultointiin tulevan asiakkaan ja varatun huoneen"
- Tämä pätee, sillä myyjä voi olla samaan aikaan vain yhdessä konsultoinnissa
- Huom: tämän perusteella
{myyjätunnus, päivämäärä, kellonaika}
on myös avainehdokas
{huonenro, päivä, kellonaika} -> {käyttäjätunnus, myyjätunnus}
- "Jos tiedetään konsultoinnin päivämäärä ja huone, tiedetään, mikä asiakas ja myyjä tulevat paikalle"
- Tämä pätee, sillä solussa voi olla samaan aikaan vain yksi konsultointi, eli solun numerosta ja päivämäärästä tiedetään, ketkä konsultointiin osallistuu
- Huom: tämän perusteella
{huonenro, päivä, kellonaika}
on myös avainehdokas
{myyjätunnus, päivä} -> {huonenro}
- "Jos tiedetään myyjän tunnus ja päivä, tiedetään, minkä huoneen hän on varannut päiväksi"
- Huom:
{myyjätunnus, päivä}
ei ole avainehdokas! Myyjän tunnuksella ja päivällä voidaan vaatimusten perusteella tietää vain myyjän varaaman huoneen numero, muttei sitä, kuka asiakas on tulossa.
Huomaa, että 1NF, 2NF ja 3NF pätevät:
- 1NF on OK, sillä perusavain on valittu, attribuuteilla on selkeä tietotyyppi eikä moniarvoisia attribuutteja ole.
- 2NF on OK, sillä riippuvuus
{käyttäjätunnus, päivämäärä} -> {kellonaika, myyjätunnus, huonenro}
on täysi.kellonaika
riippuu täysin perusavaimesta{käyttäjätunnus, päivämäärä}
eikä vaan sen osasta; ilman käyttäjätunnusta tai päivämäärää ei tiedetä yksikäsitteisesti, mihin aikaan konsultointi voisi ollamyyjätunnus
riippuu täysin perusavaimesta{käyttäjätunnus, päivämäärä}
eikä vain sen osasta; ilman käyttäjätunnusta tai päivämäärää ei tiedetä, mikä myyjä osallistuu konsultointiinhuonenro
riippuu täysin perusavaimesta{käyttäjätunnus, päivämäärä}
eikä vaan sen osasta; ilman käyttäjätunnusta tai päivämäärää ei tiedetä, missä solussa konsultointi on
- 3NF on OK poikkeussäännön takia: kaikissa funktionaalisissa riippuvuuksissa määräävä joukko on joko perusavain tai avainehdokas.
Nyt kuitenkin BCNF vaatii, että jokaisen attribuutin tulee riippua vain ja ainoastaan relaation avainehdokkaista. Funktionaaliselle attribuutille {myyjätunnus, päivä} -> {huonenro}
näin ei päde, sillä {myyjätunnus, päivä}
ei ole avainehdokas.
Ratkaisuna on siirtää rikkova funktionaalinen riippuvuus omaksi tauluksi. Lopuksi siis päädytään tauluun KONSULTOINTI_VARAUS
, joka kertoo varatun konsultoinnin ajan ja myyjän:
käyttäjätunnus | päivä | kellonaika | myyjätunnus |
---|---|---|---|
matti123 | 2025-01-30 | 12:00 | myyjä01 |
matti123 | 2025-02-10 | 12:00 | myyjä01 |
hilda06 | 2025-01-30 | 15:00 | myyjä02 |
asko_96 | 2025-03-01 | 13:00 | myyjä03 |
Lisäksi funktionaaliselle riippuvuudelle {myyjätunnus, päivä} -> {huonenro}
tehdään taulu HUONE_VARAUS
, joka kertoo, minkä huoneen myyjä varasi itselleen päiväksi:
myyjätunnus | päivä | huonenro |
---|---|---|
myyjä01 | 2025-01-30 | huone101 |
myyjä01 | 2025-02-10 | huone102 |
myyjä02 | 2025-01-30 | huone101 |
myyjä03 | 2025-03-01 | huone100 |
Neljäs normaalimuoto
Yleensä BCNF riittää tosi hyvin normalisointiin. Tietyille tauluille, jotka mallintavat moniarvoista dataa, funktionaalisen riippuvuuden määritelmä ei tunnistamaan poikkeamia.
Esimerkki
Katsotaan viimeistä kertaa Webbikauppa.com-yrityksen tietokantaa.
Webbikauppa.com haluaa alkaa myydä omia valmiiksi rakennettuja pöytätietokoneita. Pöytätietokoneista tarjotaan erilaisia malleja, ja jokaisella mallilla on joukko muokattavia ominaisuuksia, kuten keskusmuistin määrä, prosessorin malli ja näytönohjaimen malli. Tietokantaan haluttaisiin mallintaa, mitä ominaisuuksia on tarjolla eri malleille.
Vaatimuksen perusteella tehtiin taulu PC_MALLI_VERSIOT
:
malli | keskusmuisti | prosessorimalli | näytonohjainmalli |
---|---|---|---|
Palikka | 4 GB | AMD Ryzen 3 | NVIDIA GeForce GTX 1080 |
Palikka | 8 GB | AMD Ryzen 3 | NVIDIA GeForce GTX 1080 |
Palikka | 4 GB | AMD Ryzen 5 | NVIDIA GeForce GTX 1080 |
Palikka | 8 GB | AMD Ryzen 5 | NVIDIA GeForce GTX 1080 |
Palikka | 4 GB | AMD Ryzen 3 | NVIDIA GeForce RTX 2060 |
Palikka | 8 GB | AMD Ryzen 3 | NVIDIA GeForce RTX 2060 |
Palikka | 4 GB | AMD Ryzen 5 | NVIDIA GeForce RTX 2060 |
Palikka | 8 GB | AMD Ryzen 5 | NVIDIA GeForce RTX 2060 |
Möykky | 32 GB | Intel Core i7 | NVIDIA GeForce GTX 1080 |
Möykky | 64 GB | Intel Core i7 | NVIDIA GeForce GTX 1080 |
Möykky | 32 GB | Intel Core i9 | NVIDIA GeForce GTX 1080 |
Möykky | 64 GB | Intel Core i9 | NVIDIA GeForce GTX 1080 |
Möykky | 32 GB | Intel Core i7 | NVIDIA GeForce RTX 2060 |
Möykky | 64 GB | Intel Core i7 | NVIDIA GeForce RTX 2060 |
Möykky | 32 GB | Intel Core i9 | NVIDIA GeForce RTX 2060 |
Möykky | 64 GB | Intel Core i9 | NVIDIA GeForce RTX 2060 |
Taulun perusteella tarjolla on kaksi PC-mallia: Palikka ja Möhkäle. Jokaisella mallilla on kaksi eri keskusmuisti-, prosessori- ja näytönohjainvaihtoehtoa.
Huomaa, että perusavaimessa on kaikki sarakkeet {malli, keskusmuisti, prosessorimalli, näytönohjainmalli}
. Funktionaalisia riippuvuuksia ei tässä ole, sillä mikään sarake ei yksilöi mitään toista saraketta: mallilla voi olla monta eri ominaisuusvaihtoehtoa, ja yksittäinen ominaisuus (esim. prosessorin malli) voi olla käytössä eri mallilla.
Funktionaalisia riippuvuuksia ei ole, eikä siis ole mitään, mikä rikkoisi BCNF:n vaatimuksia.
Harjoittele
Katsele yllä olevaa taulua ja mallidataa. Pohdi, millaisia poikkeamia tai mahdollista toisteisuutta voi syntyä. Muista, että poikkeama on tiedon lisäyksestä, muokkauksesta tai poistosta johtuva syntyvä ristiriita taulun tiedoissa.
Mitä muita mahdollisia ongelmia voi syntyä, kun yllä olevaa taulua yrittää käyttää?
Kun olet pohtinut asiaa, voit lukea selityksen alla:
Taulussa on toisteisuutta, mutta perusavaimen valinnan takia sitä ei voida välttää. Kuten todettiin, funktionaalisia riippuvuuksia ei ole, mutta taulu on silti mahdollista pilata huolimattomuudella:
Mahdollisia poikkeamia:
- Jos nyt Palikka-mallille haluttaisiin lisätä suuremman keskusmuistin omaavan vaihtoehdon (esim. 16 GB), pitäisi lisätä peräti 8 riviä jokaiselle prosessori- ja näytönohjainvaihtoehdolle, joka on nyt tarjolla Palikka-mallille. Jos yhden vaihtoehdon unohtaa lisätä, jäädään ristiriitaiseen tilaan, jossa 16 GB:n mallille ei ole tarjolla kaikkia prosessori- ja näytönohjainvaihtoehtoja.
Muuta:
- Toisteisuus on erittäin suuri! Jokaiselle uudelle ominaisuudelle pitäisi lisätä niin monta riviä, että kaikki mahdolliset yhdistelmät saa lueteltua.
Yllä olevassa esimerkissä sarakkeilla ei ole funktionaalisia riippuvuuksia. Voidaanko silti jotain sanoa siitä, miten sarakkeet liittyvät toisiinsa? Esimerkkitaulusta voidaan todeta, että:
- Palikka-mallilla on vain tasan kaksi keskusmuistivaihtoehtoa: 4 GB tai 8 GB. Keskusmuistivaihtoehdot eivät riipu prosessorimallista tai näytönohjainmallista.
- Samoin Möykky-mallilla on tasan kaksi keskusmuistivaihtoehtoa: 32 GB 64 GB eikä sekään riipu prosessorista tai näytönohjaimesta.
- Vastaavasti malleilla on tasan kaksi vaihtoehtoa
prosessorimalli
-sarakkeen arvolle, samoinnäytönohjainmalli
-sarakkeelle eivätkä niiden arvot riipu suoraan toisistaan.
Funktionaalisessa riippuvuudessa attribuutti voi yksilöidä jonkun toisen attribuutin yksittäisiä arvoja. Tässä esimerkissä ei ole niin, mutta ollaan lähellä: attribuutti malli
yksilöi attribuutin keskusmuisti
arvojoukon. Toisin sanoen, jos tiedetään malli, voidaan tietää, mitä keskusmuistin vaihtoehtoja sillä on. Yleistetään tämä huomio määritelmään:
Määritelmä
Jos joukko relaation R
attribuutteja X
yksilöi jonkun toisen joukon saman relaation attribuuttien Y
arvojoukon riippumatta relaation muista attribuuteista, sanotaan, että relaatiossa pätee moniarvoinen riippuvuus {X} ->> {Y}
.
Toisin sanoen: Jos relaatio sisältää attribuuttijoukot X
, Y
ja Z
, niin jokaisella monikolla, jolla attribuuttijoukon X
arvo on a
, on attribuuttijoukon Y
arvojen joukko aina b
riippumatta attribuuttijoukon Z
arvoista.
Lisäksi, jos määräävä ja riippuva attribuuttijoukko yhdessä {X, Y}
sisältävät relaation kaikki attribuutit, sanotaan, että riippuvuus {X} ->> {Y}
on triviaali. Muussa tapauksessa riippuvuus on epätriviaali.
Yllä olevan esimerkin perusteella voidaan siis todeta, että {malli} ->> {keskusmuisti}
, {malli} ->> {prosessorimalli}
ja {malli} ->> {näytönohjain}
. Toisin sanoen, keskusmuistin (tai prosessorimallin tai näytönohjaimen) mahdolliset arvovaihtoehdot riippuvat vain mallista eikä mistään muusta attribuutista.
Tämän määritelmän pohjalta voidaan esittää neljännen normaalimuodon vaatimukset:
Määritelmä
Relaatio on 4NF:ssä, jos kaikki seuraavat vaatimuksen täyttyvät:
- Relaatio on BCNF:ssä.
- Relaation kaikkien epätriviaalien moniarvoisten riippuvuuksien määräävä attribuuttijoukko tulee olla relaation avainehdokas.
Toisin sanoen, jos relaatiossa on moniarvoinen riippuvuus {X} ->> {Y}
, niin attribuuttijoukon X
tulee olla relaation avainehdokas.
Huomaa, että 4NF koskee vain epätriviaaleja riippuvuuksia. Tämän johdosta 4NF soveltuu käytännössä vain tauluihin, joissa on enemmän kuin kaksi saraketta. Rajoituksen takia myös binääristä N-M -suhdetta mallintavat taulut täyttävät 4NF.
Käytännössä 4NF voi mennä rikki vain, jos tietokannan suunnitteluvaiheessa samalla taululla yritetään mallintaa monta N-M -suhdetta samanaikaisesti. Jos transformoinnin suorittaa oikein ja sääntöjen mukaan, taulussa ei pitäisi esiintyä epätriviaaleja moniarvoisia riippuvuuksia lainkaan.
Esimerkki
Normalisoidaan vielä lopuksi esimerkkitaulu. Kuten todettiin, löydettiin seuraavat moniarvoiset riippuvuudet:
{malli} ->> {keskusmuisti}
- "Jos tiedetään malli, tiedetään yksikäsitteisesti kaikki keskusmuistivaihtoehdot"
{malli} ->> {prosessorimalli}
- "Jos tiedetään malli, tiedetään yksikäsitteisesti kaikki prosessorimallivaihtoehdot"
{malli} ->> {näytönohjainmalli}
- "Jos tiedetään malli, tiedetään yksikäsitteisesti kaikki näytönohjainvaihtoehdot"
Kaikki kolme riippuvuutta ovat epätriviaaleja: määräävän ja riippuvan joukon yhdiste ei ole koko taulu.
Lisäksi {malli}
ei ole taulun perusavain {malli, keskusmuisti, prosessorimalli, näytönohjainmalli}
, joten 4NF ei päde. Ratkaisuna tehdään kullekin riippuvuudelle oma taulu:
MALLI_KESKUSMUISTIT
:
malli | keskusmuisti |
---|---|
Palikka | 4 GB |
Palikka | 8 GB |
Möykky | 32 GB |
Möykky | 64 GB |
MALLI_PROSESSORIMALLIT
:
malli | prosessorimalli |
---|---|
Palikka | AMD Ryzen 3 |
Palikka | AMD Ryzen 5 |
Möykky | Intel Core i7 |
Möykky | Intel Core i9 |
MALLI_NÄYTÖNOHJAINMALLIT
:
malli | näytönohjain |
---|---|
Palikka | NVIDIA GeForce GTX 1080 |
Palikka | NVIDIA GeForce RTX 2060 |
Möykky | NVIDIA GeForce GTX 1080 |
Möykky | NVIDIA GeForce RTX 2060 |
Huomaa erityisesti, että rivien määrä on saatu vähennettyä: alkuperäisestä 16 rivistä on saatu 12 riviä. Lisäksi nyt malleille on nyt helpompaa lisätä uusia ominaisuuksia: uuden näytönohjainvaihtoehdon lisäys tarkoittaa vain parin rivin lisäämistä tauluun.
Loppusanat normalisoinnista
Tätä materiaalia varmasti lukee kolme eri selkeää joukkoa: tulevat ohjelmoijat sekä tietokantojen ylläpitäjät, tulevat tutkijat sekä tietokantojen suunnittelijat ja niitä, jotka mahdollisesti pysyttelevät kaukana tietokannoista tämän materiaalin jälkeen (toivottavasti ei kuitenkaan tämän materiaalin johdosta).
Ohjelmoijat ja ylläpitäjät: Normalisoinnin tarkastelu voi olla hankalaa, ja se vaatii kohdealueen hyvää ymmärtämistä. Normalisointia voisi oikeastaan tiivistää pitkälti kahteen pääsääntöön, jonka perusteella tauluja kannattaa suunnitella:
- Tauluun tulee tallentaa vain kohteeseen välittömästi liittyviä tietoja.
- Esimerkiksi opiskelijan postitoimipaikka on postinumeroon liittyvä tieto, ei opiskelijan. Jos taulu tallentaa eri kohteiden tietoja, se viestii tarpeesta luoda oma taulu.
- Kunkin tiedon päivitys tulee tapahtua vain yhteen paikkaan.
- Esimerkiksi jos opiskelijalle vaihtuu postinumero, ja sen takia pitää päivittää sekä postinumero että postitoimipaikka kahdesta eri sarakkeesta, se viestii tarpeesta luoda oma taulu.
Seuraamalla näitä kahta sääntöä pääsee hyvin pitkälle tietokannan suunnittelussa.
Tutkijat ja suunnittelijat: Käsitteellinen mallintaminen on erittäin kannattavaa. Jos ER-kaavio on tehty kohdealueen tarpeita huomioiden ja transformointi suoritettu oikeaoppisesti, tuloksena on usein 3NF tai jopa BCNF toteuttavia relaatioita.
Mainittakoon, että normaalimuotoja on todellisuudessa muitakin. Jotkin niistä ovat vaativuudeltaan käsiteltyjen normaalimuotojen välissä, mutta useimmat ovat käsiteltyjä normaalimuotoja vahvempia. Erityisesti viides normaalimuoto vaatisi formaalia tarkastelua.
Normalisointi on pyritty tässä luvussa esittämään mahdollisimman yksinkertaisesti menemättä liian formaaleihin määritelmiin. Tässä luvussa esitellyt määritelmät eivät ole suinkaan virallisia; tarkasta määritelmästä kiinnostuneet voivat jatkaa aiheeseen tutustumista esimerkiksi Elmasrin ja Navathen kirjassa [15].
Muut lukijat: Toivottavasti tämä ei ollut liian raskas luku 😅. Olennaisin asia, joka pitäisi jäädä mieleen on, että taulujen rakenteella voidaan vaikuttaa tietokannan helppokäyttöisyyteen sekä "idioottivarmuuteen". Toisaalta mitä enemmän tietoja jakaa eri tauluihin, sitä hitaammat tietokantakyselyt ovat. Lisäksi nykyisissä tietokannanhallintajärjestelmissä normalisointi 2NF:n yli voi jopa lisätä levytilan käyttöä [36]. Siispä voi todeta, että oikeastaan normalisointi ei ole aina edes kannattavaa. Nykyään tallennustila on varsin halpaa, ja käyttäjät haluavat yhä nopeampia sovelluksia. Siinä mielessä voi olla perusteltua lisätä vähän toisteisuutta dataan jos se vähentää tietokantakyselyiden määrää ja monimutkaisuutta. Puhutaankin jopa tietokantojen denormalisoinnista: tauluja jätetään normalisoimatta tai muutetaan alempaan normaalimuotoon jotta kyselyt olisivat nopeampia.
These are the current permissions for this document; please modify if needed. You can always modify these permissions from the manage page.