English version of the materials are work in progress!
Expect bugs, typos, and other issues. The English version is expected to be completed during spring 2026.
Basics of Normalization
In sections 2 and 3, we learned ways to design a relational database according to "good practices". Correct use of the relational model offers protection and some assurances for the data in the database. For example, if we were to design a relation STUDENT(sid, birthdate), where the primary key is {sid}, it is impossible to even accidentally put the table in a state where the same student has two different birthdates:
| sid | birthdate |
|---|---|
| s1 | 1.2.1990 |
| s1 | 2.3.2000 |
This situation is impossible from the relational model's perspective, because every row in the table must be distinguishable based on the primary key, and thus one could not add two rows with the same student ID to the table.
In section 4, we also learned that relational database management systems (RDBMS) automatically ensure the correctness of the database whenever data is added, modified, or deleted. For example, RDBMSs check data types, ensure uniqueness of rows according to the primary key, and also check that referential integrity of foreign keys is maintained.
However, the basic building blocks of the relational model are not always enough to guarantee logical consistency of data. Generally, if a table can be put into a state that is "nonsensical" from the domain's perspective, the fault often lies in the database structure. In this section, we get acquainted with normalization, i.e., how database structure design can protect data consistency and reduce redundancy.
Purpose of Normalization
Normalization has two main purposes:
- to fix database structures prone to data correctness errors, and
- to reduce data redundancy in an otherwise logically correctly designed structure.
In addition to this, normalization facilitates database usage as follows:
- Normalized tables are simpler to understand and do not contain "useless" columns.
- Normalized tables are easy to extend to cover new domain needs. AT best, extending does not require changes to the database structure.
- Normalized tables are better protected against anomalies (i.e., data contradictions) resulting from insert, delete, and update operations.
Structures prone to data correctness errors refer to attribute names that poorly describe the domain; structures with abundant null values; and relation structures where attributes from multiple real-world entities have been unnecessarily combined.
Data redundancy refers to unnecessary repetition of the same values. Data repetition naturally increases storage requirements. Data repetition also leads more easily to anomalies in data correctness.
Esimerkki
Let's examine the following student database, which contains one table. The table keeps track of students, course completions, courses, and grades:
| sid | sname | cid | cname | grade |
|---|---|---|---|---|
| s7111 | Adam Adapter | itka204 | Databases | 5 |
| s7111 | Adam Adapter | itkp113 | Object-Oriented Design | 4 |
| s7111 | Adam Adapter | tjta114 | Information Management | GOOD |
| s6800 | Bertha Blade | itka204 | Databases | 5 |
| s6604 | Cecilia Chirp | tjta114 | Information Management | GOOD |
With the current structure, adding, modifying, or deleting data can easily cause anomalies in the database. An anomaly means that the database information remains contradictory with itself or the domain after an operation targeting the database.
What kind of insert, update, and delete anomalies can occur in this database?
Suppose the following row is added to the table:
sid sname cid cname grade s6800 Bertha Blade tjta114 Basics of Information Management 5 The name of course
tjta114becomes contradictory, as after the insertion the course name can be "Information Management" or "Basics of Information Management".This is an insert anomaly.
Suppose the following row is deleted from the table:
sid sname cid cname grade s7111 Adam Adapter itkp113 Object-Oriented Design 4 Now Adam Adapter's completion of the Object-Oriented Design course was removed, but now information about the existence of the Object-Oriented Design course was also removed from the entire database. Now if we wanted to find out what courses are taught using the database, the Object-Oriented Design course would not be found. This would be in conflict with the domain.
This is a delete anomaly.
Suppose the course code of
itka204changes toitka2004and the name changes to "Basics of Databases". Due to the change, the rowsid sname cid cname grade s7111 Adam Adapter itka204 Databases 5 is changed to form
sid sname cid cname grade s7111 Adam Adapter itka2004 Basics of Databases 5 However, it might have been forgotten that the table also has student
s6800, whose row should also be updated! A contradiction arises in the database data: one student has a completion with the new course code and course name, but another student has a completion with the old, non-existent course code.We ended up with an update anomaly.
The current database is in dire need of normalization!
PS: Generally, if multiple different entities of the domain are modeled into the same table, anomalies are very possible.
Functional Dependency
As noticed, excessive data redundancy causes problems, and minimizing it is at the core of normalization. On the other hand, some redundancy is necessary: in the example above, the student ID and course code belonging to the primary key must be repeated in the table to guarantee row uniqueness ensured by the primary key. In other words, redundancy is error-prone, but not all data repetition can be avoided. How can we then distinguish "allowed" redundancy from redundancy that could be prone to anomalies?
Esimerkki
Let's return for a moment to the table STUDENT(sid, ssn, first_name, last_name, zip_code, city)Example in connection with the primary key in Chapter 3.1.
Let's invent some model data to make examining problems easier:
| sid | ssn | first_name | last_name | zip_code | city |
|---|---|---|---|---|---|
| s101 | 120196-111X | Matthew | Meikäläinen | 33720 | Tampere |
| s102 | 130280-112X | Snow | Zinc | 40520 | Jyväskylä |
| s103 | 130280-321Z | Snow | Zinc | 40520 | Jyväskylä |
| s104 | 121240-111A | Donald | Duck | 60200 | Seinäjoki |
| s105 | 241252-321X | Dave | Duck | 33720 | Tampere |
Recall that a candidate key is an irreducible set of columns that uniquely identifies the entire row. In other words, based on the values of the candidate key columns, one can know exactly the values of all other columns of a row.
In the example of Chapter 3.1, we stated that:
{sid}is a candidate key, because based on the value of thesidcolumn, the values of columns{ssn, first_name, last_name, zip_code, city}are uniquely known.- For example, if we know that a student's
sidis s101, we know that the student'sssnis "120196-111X",first_nameMatthew,last_nameMeikäläinen,zip_code33720 andcityTampere.
- For example, if we know that a student's
{ssn}is also a candidate key with the same reasoning: if we know a student'sssn, we uniquely know the corresponding values{sid, first_name, last_name, zip_code, city}.
Based on the table and the domain, we can make the following interesting observation: even though {zip_code} does not identify all columns of the table, it identifies the city column. If we know that the zip code of a student's address is 33720, we uniquely know that the city is Tampere.
In other words, {zip_code} is not a candidate key of the STUDENT table, but it still behaves similarly regarding the city column: the zip code does not identify all columns of the table, but it still identifies some columns. We also see in the table that there is redundancy in the city column.
Let's try making a table CITY(zip_code, city) and copying data from the STUDENT table:
| zip_code | city |
|---|---|
| 33720 | Tampere |
| 40520 | Jyväskylä |
| 60200 | Seinäjoki |
In this relation {zip_code} is now a candidate key, as it identifies the city column, which is the only column outside the candidate key. It is also the only candidate key, i.e., also the primary key. Specifically, redundancy has been reduced: the same combination of zip code and city is not repeated unnecessarily.
From the example, we can conclude that normalization is closely related to how well the values of columns are identifiable based on the values of other columns. The matter is also partly related to definitions of candidate keys and primary keys, but on a slightly more general level. This brings us to functional dependencies.
Definition of Functional Dependency
The most important concept of normalization is functional dependency. Let's start with a precise definition in the words of the relational model:
Määritelmä
If a set of attributes X of relation R identifies another set of attributes Y of the same relation, it is said that a functional dependency holds in the relation
{X} -> {Y}
In a functional dependency, X is the determinant attribute set. It is said that "Attributes Y are functionally dependent on attributes X" or "Y is functionally dependent on X". More generally, one can say that "X determines Y".
In other words, for every tuple where attributes X have a certain value a, attributes Y have at most one value b.
In practice, functional dependency is very close to the definition of a candidate key. A candidate key is any (irreducible) attribute set of a relation that identifies all other attributes of the tuple. Functional dependency, on the other hand, is any attribute set of a relation that identifies any other set of relation attributes.
Let's take a couple more examples to clarify the concept:
Esimerkki
Let's return again to the table STUDENT(sid, ssn, first_name, last_name, zip_code, city) example in connection with the primary key in Chapter 3.1 and the model data invented in the previous example:
| sid | ssn | first_name | last_name | zip_code | city |
|---|---|---|---|---|---|
| s101 | 120196-111X | Matthew | Meikäläinen | 33720 | Tampere |
| s102 | 130280-112X | Snow | Zinc | 40520 | Jyväskylä |
| s103 | 130280-321Z | Snow | Zinc | 40520 | Jyväskylä |
| s104 | 121240-111A | Donald | Duck | 60200 | Seinäjoki |
| s105 | 241252-321X | Dave | Duck | 33720 | Tampere |
In the previous example, we stated that {sid} is a candidate key. A candidate key identifies all other columns of the table: if the student's student ID is known, the values of the student's other columns are known uniquely. In other words, the following functional dependency holds:
{sid} -> {ssn, first_name, last_name, zip_code, city}
This does not tell anything new that is not known about the candidate key; it is just a different way to denote the matter.
Similarly, since {ssn} is a candidate key, the following functional dependency also holds:
{ssn} -> {sid, first_name, last_name, zip_code, city}
In the previous example, it was noticed that the column zip_code identifies the column city. If it is known that a student's zip_code is 33720, it is uniquely known that the student's city is always Tampere. So with functional dependency notation:
{zip_code} -> {city}
Esimerkki
Consider the following table PERSON(pid, name):
| pid | name |
|---|---|
| 010160-ABCD | Adam Adapter |
| 020270-EFGH | Bertha Blade |
| 030380-IJKL | Adam Adapter |
| 040490-MNOP | Cecilia Chirp |
Even if we knew nothing about the domain, based on the table data we can deduce that the following functional dependency holds:
{pid} -> {name}
It holds because every value of the pid column corresponds to a unique name. For example, if it is known that the row's pid column value is 010160-ABCD, it is known that it corresponds uniquely to the name Adam Adapter.
The following functional dependency does not hold:
{name} -> {pid}
It does not hold, because for example with name column value Adam Adapter there are two different possible pid column values: 010160-ABCD and 030380-IJKL. Functional dependency means that the correspondence is unique: for every name there should be at most one personal ID, but that is not the case here.
It is worth noting that in the examples above, functional dependencies have been derived from example data. Usually, normalization might be done already at the database design stage when there is no data yet. Then functional dependencies must be deduced by understanding the domain. Although mathematical notation is used to represent functional dependencies, the question is not about a mathematical phenomenon but specifically about understanding the meaning of the relation.
| ccode | cname | year | teacher_id | credits |
|---|---|---|---|---|
| itka201 | Algorithms | 2015 | t447 | 4 |
| itka201 | Algorithms | 2014 | t051 | 3 |
| itka204 | Databases | 2014 | t300 | 5 |
| itka204 | Databases | 2015 | t300 | 5 |
| itka204 | Databases | 2013 | t300 | 4 |
| tjta118 | IT Infra | 2014 | t411 | 3 |
| tjta118 | IT Infra | 2015 | t411 | 3 |
Inference Rules
From the definition and examples above, one can deduce that some functional dependencies can be inferred from other dependencies. For example, if it is known that a student's student ID identifies the student's first name, last name, and SSN together:
{sid} -> {ssn, first_name, last_name}
then clearly the student ID also identifies the SSN, first name, and last name separately:
{sid} -> {ssn}
{sid} -> {first_name}
{sid} -> {last_name}
Meaning: if it is known that with sid one can find a certain student's SSN, first name, and last name, then of course with sid one can find each piece of information also individually.
Armstrong [1] has presented a set of rules with which functional dependencies can be inferred via other dependencies. Functional dependencies that can be inferred using these rules are called trivial.
Reflexivity Rule (axiom of reflectivity): if attribute set
Yis a subset of another attribute setX, a trivial functional dependency "XdeterminesY" holds. That is:If
Y⊂X, then{X} -> {Y}.As a consequence of the rule, attribute set
Xalways depends on itself, i.e.:{X} -> {X}Example: Student's first name depends on student's first name, i.e.,
{first_name} -> {first_name}. Student's first name also depends on the combination of first name and last name:{first_name, last_name} -> {first_name}.Transitivity Rule (axiom of transitivity): if functional dependencies "
XdeterminesY" and "YdeterminesZ" hold, then functional dependency "XdeterminesZ" also holds. That is:If
{X} -> {Y}and{Y} -> {Z}, then{X} -> {Z}.Example: From a student's SSN, their first name and last name are known, i.e.,
{ssn} -> {first_name, last_name}. On the other hand, if the first name and last name are known, their initials are known, i.e.,{first_name, last_name} -> {initials}. Therefore, from a student's SSN, their initials are known, i.e.,{ssn} -> {initials}.Union Rule (axiom of union): if functional dependencies "
XdeterminesY" and "XdeterminesZ" hold, then functional dependency "Xdetermines union ofYandZ" also holds. That is:If
{X} -> {Y}and{X} -> {Z}, then{X} -> {Y, Z}.Example: Student's student ID identifies student's first name, i.e.,
{sid} -> {first_name}. On the other hand, student's student ID also identifies student's last name, i.e.,{sid} -> {last_name}. Therefore, student ID identifies student's first name and last name, i.e.,{sid} -> {first_name, last_name}.Decomposition Rule (axiom of decomposition): if functional dependency "
Xdetermines union ofYandZ" holds, then functional dependencies "XdeterminesY" and "XdeterminesZ" also hold. That is:If
{X} -> {Y, Z}, then{X} -> {Y}and{X} -> {Z}.Example: Student's SSN identifies first name and last name, i.e.,
{ssn} -> {first_name, last_name}. Therefore, student's SSN identifies first name ({ssn} -> {first_name}) and last name ({ssn} -> {last_name}).Augmentation Rule (axiom of augmentation): if functional dependency "
XdeterminesY" holds, then functional dependency "combination ofXandZdeterminesY" also holds, i.e. if "XdeterminesY", then "X's supersets also determineY". That is:If
{X} -> {Y}, then{X, Z} -> {Y}.Example: Student's SSN identifies student's last name, i.e.,
{ssn} -> {last_name}. The dependency holds even if, for instance, first name was added to the determinant set, i.e.,{ssn, first_name} -> {last_name}.
Huomautus
One essential consequence of the decomposition rule and union rule is that functional dependency
{X} -> {Y1, Y2, ..., Yn}
is equivalent to the fact that all following dependencies hold simultaneously:
{X} -> {Y1}
{X} -> {Y2}
...
{X} -> {Yn}
and vice versa. In other words, it does not matter if functional dependencies are marked separately or combined into the same dependency.
Huomautus
Note that the rules above are of the form "if (1) then (2)". The rule does not necessarily hold the other way around, meaning "if (2) then (1)" is not always true!
For example, the most common error in reasoning is that the determinant attribute set can always be decomposed while preserving functional dependency!
In the database of study completions at the beginning of this chapter, there is the following dependency according to the primary key:
{sid, cid} -> {sname, cname, grade}
(student ID and course ID together identify student's name, course name, and completion grade).
Here one might want to e.g. leave cid out of the determinant attribute set, because for instance student's name does not depend on the course ID. However, this dependency:
{sid} -> {sname, cname, grade}
does not necessarily hold anymore, because two different students can have the same name, completed course, and even the same grade. For the same reason, student ID cannot be left out of the determinant set.
Instead, in this case, we notice that by taking attributes out of both determinant and dependent sets, we get multiple valid functional dependencies:
{sid} -> {sname}
{cid} -> {cname}
{sid, cid} -> {grade}
So be careful to apply the rules in the "correct direction"!
In the figure below, another way to describe functional dependencies is presented. Here too, the arrow points from the determinant attribute set to the dependent attribute. Functional dependencies are numbered for the multiple choice task.
R with their functional dependencies.Normalisoinnin perusteet
Osissa 2 ja 3 opimme tapoja suunnitella relaatiotietokanta ns. "hyvien oppien" mukaisesti. Relaatiomallin oikeanlainen käyttö tarjoaa suojan ja jotain varmistuksia tietokannassa olevalle datalle. Esimerkiksi jos suunniteltaisiin relaatio OPISKELIJA(optun, syntymäaika), jossa perusavain olisi {optun}, on mahdotonta edes vahingossa saattaa taulu tilanteeseen, jossa samalla opiskelijalla olisi kaksi eri syntymäaikaa:
| optun | syntymäaika |
|---|---|
| o1 | 1.2.1990 |
| o1 | 2.3.2000 |
Tämä tilanne on relaatiomallin kannalta mahdoton, sillä taulun jokainen rivi on voitava erottaa toisistaan perusavaimen perusteella, eikä siis tauluun voisi lisätä kahta riviä, joilla on sama opiskelijatunnus.
Osassa 4 opimme myös, että relaatiotietokannanhallintajärjestelmät (RDBMS) automaattisesti varmistavat tietokannan oikeellisuuden aina, kun dataa lisätään, muokataan tai poistetaan. Esimerkiksi RDBMS:t tarkistavat datan tyypin, varmistavat perusavaimen mukaisen rivien yksikäsitteisyyden ja tarkistavat myös, että viiteavainten viite-eheys säilyy.
Aina kuitenkaan relaatiomallin peruspalikat eivät riitä takaamaan datan loogisuutta. Yleisesti, jos taulu voidaan saattaa kohdealueen kannalta "järjettömään" tilaan, vika on usein tietokannan rakenteessa. Tässä osassa tutustumme normalisointiin eli siihen, miten tietokannan rakenteen suunnittelulla voidaan suojata datan loogisuutta ja vähentää toisteisuutta.
Normalisoinnin tarkoitus
Normalisoinnilla on kaksi päätarkoitusta:
- korjata datan oikeellisuusvirheille alttiita tietokantarakenteita ja
- vähentää datan toisteisuutta (redundancy) muutoin loogisesti oikein suunnitellussa rakenteessa.
Tämän lisäksi normalisointi helpottaa tietokannan käyttöä seuraavasti:
- Normalisoidut taulut ovat yksinkertaisempia ymmärtää, eikä niissä ole ns. "turhia" sarakkeita.
- Normalisoituja tauluja on helppo laajentaa kattamaan uusia kohdealueen tarpeita. Parhaimmillaan laajentaminen ei vaadi muutoksia tietokannan rakenteeseen.
- Normalisoidut taulut ovat paremmin suojattuja lisäys-, poisto- ja muokkausoperaatioista johtuvilta poikkeamilta (eli datan ristiriidoilta).
Datan oikeellisuusvirheille alttiilla rakenteella tarkoitetaan attribuuttien nimiä, jotka kuvaavat huonosti kohdealuetta; rakenteita, joissa esiintyy runsaasti tyhjäarvoja; sekä relaatiorakenteita, joihin on tarpeettomasti yhdistetty attribuutteja useista reaalimaailman kohteista.
Datan toisteisuudella tarkoitetaan puolestaan samojen arvojen tarpeetonta toistamista. Datan toisto luonnollisesti kasvattaa tallennustilatarpeita. Datan toisto johtaa myös helpommin datan oikeellisuuden poikkeamiin.
Esimerkki
Tarkastellaan seuraavaa opiskelijatietokantaa, joka sisältää yhden taulun. Taulu pitää kirjaa opiskelijoista, kurssisuorituksista, kursseista ja suorituksen arvosanoista:
| optun | opnimi | kurssitun | kurssinimi | arvosana |
|---|---|---|---|---|
| o7111 | Aatami Laippa | itka204 | Tietokannat | 5 |
| o7111 | Aatami Laippa | itkp113 | Oliosuunnittelu | 4 |
| o7111 | Aatami Laippa | tjta114 | Tietohallinto | HYV |
| o6800 | Bertta Hukari | itka204 | Tietokannat | 5 |
| o6604 | Cecilia Rastas | tjta114 | Tietohallinto | HYV |
Nykyisellä rakenteella tietojen lisäys, muokkaus tai poisto voi helposti aiheuttaa poikkeamia tietokantaan. Poikkeama tarkoittaa sitä, että tietokannan tiedot jäävät ristiriitaisiksi itsensä tai kohdealueen kanssa tietokantaan kohdistuvan operaation jälkeen.
Millaisia lisäys-, muokkaus- ja poistopoikkeamia tässä tietokannassa voi esiintyä?
Oletetaan, että tauluun lisätään seuraava rivi:
optun opnimi kurssitun kurssinimi arvosana o6800 Bertta Hukari tjta114 Tietohallinnon perusteet 5 Kurssin
tjta114nimestä tulee ristiriitainen, sillä lisäyksen jälkeen kurssin nimi voi olla "Tietohallinto" tai "Tietohallinnon perusteet".Kyseessä on siis lisäyspoikkeama.
Oletetaan, että taulusta poistetaan seuraava rivi:
optun opnimi kurssitun kurssinimi arvosana o7111 Aatami Laippa itkp113 Oliosuunnittelu 4 Nyt Aatami Laipalta poistui Oliosuunnittelu-kurssin suoritus, mutta nyt myös koko tietokannasta poistui tieto Oliosuunnittelu-kurssin olemassaolosta. Nyt jos tietokannan avulla haluttaisiin selvittää, mitä kursseja opetetaan, tietokannasta ei löytyisi Oliosuunnittelu-kurssia. Tämä olisi ristiriidassa kohdealueen kanssa.
Kyseessä on poistopoikkeama.
Oletetaan, että kurssin
itka204kurssikoodi muuttuu koodiksiitka2004ja nimi muuttuu "Tietokantojen perusteet". Muutoksen takia muutetaan rivioptun opnimi kurssitun kurssinimi arvosana o7111 Aatami Laippa itka204 Tietokannat 5 muotoon
optun opnimi kurssitun kurssinimi arvosana o7111 Aatami Laippa itka2004 Tietokantojen perusteet 5 Nyt kuitenkin saatettiin unohtaa, että taulussa on myös opiskelija
o6800, jonka rivi pitäisi myös päivittää! Tietokannan dataan syntyy ristiriita: yhdellä opiskelijalla on uuden kurssikoodin ja kurssin nimen suoritus, mutta toisella opiskelijalla on vanhan, olemattoman kurssikoodin mukainen suoritus.Päädyimme siis muokkauspoikkeamaan.
Nykyinen tietokanta kaipaa siis kipeasti normalisointia!
PS: Yleisestikin, jos useita kohdealueen eri kohteita mallinnetaan samaan tauluun, poikkeamat ovat erittäin mahdollisia.
Funktionaalinen riippuvuus
Kuten huomattiin, datan liiallinen toisteisuus aiheuttaa ongelmia, ja normalisoinnin ytimessä on sen vähentäminen. Toisaalta jonkinlainen toisteisuus on välttämätöntä: yllä olevassa esimerkissä perusavaimeen kuuluvat opiskelijan tunnus ja kurssikoodi on pakko toistaa taulussa, jotta perusavaimen takaama rivien yksilöllisyys voidaan taata. Toisin sanoin toisteisuus on virhealtista, mutta kaikkea datan toistoa ei voida välttää. Miten voidaan siis erottaa "sallittua" toisteisuutta sellaisesta, joka voisi olla altista poikkeamille?
Esimerkki
Palataan hetkeksi Luvussa 3.1 perusavaimen yhteydessä olevaan esimerkkiin taulusta OPISKELIJA(opnro, hetu, etunimi, sukunimi, postinro, paikkakunta).
Keksitäänpä malliksi jotain dataa, jotta ongelmia on helpompaa tarkastella:
| opnro | hetu | etunimi | sukunimi | postinro | paikkakunta |
|---|---|---|---|---|---|
| o101 | 120196-111X | Matti | Meikäläinen | 33720 | Tampere |
| o102 | 130280-112X | Lumi | Sinkki | 40520 | Jyväskylä |
| o103 | 130280-321Z | Lumi | Sinkki | 40520 | Jyväskylä |
| o104 | 121240-111A | Aku | Ankka | 60200 | Seinäjoki |
| o105 | 241252-321X | Taavi | Ankka | 33720 | Tampere |
Muistellaan, että avainehdokas on jakamaton joukko sarakkeita, jotka yksilöivät koko rivin. Toisin sanoin avainehdokkaan sarakkeiden arvojen perusteella voidaan yksilöllisesti tietää tasan tarkkaan jonkin rivin kaikkien muiden sarakkeiden arvot.
Luvun 3.1 esimerkissä totesimme, että:
{opnro}on avainehdokas, silläopnro-sarakkeen arvon perusteella tiedetään yksilöllisesti sarakkeiden{hetu, etunimi, sukunimi, postinro, paikkakunta}arvot.- Esimerkiksi jos tiedetään, että jonkun opiskelijan
opnroon o101, tiedetään, että sen opiskelijanhetuon "120196-111X",etunimiMatti,sukunimiMeikäläinen,postinro33720 japaikkakuntaTampere.
- Esimerkiksi jos tiedetään, että jonkun opiskelijan
{hetu}on myös avainehdokas samalla perustelulla: jos tiedetään jonkun opiskelijanhetu, tiedetään yksilöllisesti sitä vastaavat arvot{opnro, etunimi, sukunimi, postinro, paikkakunta}.
Taulun ja kohdealueen perusteella voidaan tehdä seuraava mielenkiintoinen huomio: vaikka {postinro} ei yksilöi kaikkia taulun sarakkeita, se yksilöi sarakkeen paikkakunta. Jos tiedetään, että jonkun oppilaan osoitteen postinumero on 33720, tiedämme yksikäsitteisesti, että paikkakunta on Tampere.
Toisin sanoin {postinro} ei ole OPISKELIJA-taulun avainehdokas, mutta se silti käyttäytyy samalla tavoin paikkakunta-sarakkeen suhteen: postinumero ei yksilöi kaikkia taulun sarakkeita, mutta se silti yksilöi jotain sarakkeita. Näemme taulussa myös, että paikkakunta-sarakkeessa on toisteisuutta.
Kokeillaan tehdä taulu PAIKKAKUNTA(postinro, paikkakunta) ja kopioidaan tietoa OPISKELIJA-taulusta:
| postinro | paikkakunta |
|---|---|
| 33720 | Tampere |
| 40520 | Jyväskylä |
| 60200 | Seinäjoki |
Tässä relaatiossa nyt {postinro} on avainehdokas, sillä se yksilöi sarakkeen paikkakunta, joka on ainoa avainehdokkaan ulkopuolella oleva sarake. Se on myös ainoa avainehdokas, eli myös perusavain. Erityisesti toisteisuutta on vähennetty: samaa postinumeron ja paikkakunnan yhdistelmää ei toistu turhaan.
Esimerkistä voimme päätellä, että normalisointiin liittyy läheisesti se, miten hyvin sarakkeiden arvot ovat yksilöitävissä toisten sarakkeiden arvojen perusteella. Asia liittyy myös osin avainehdokkaiden ja perusavainten määritelmiin, mutta vähän yleisemmällä tasolla. Tästä päästäänkin funktionaalisiin riippuvuuksiin.
Funktionaalisen riippuvuuden määritelmä
Normalisoinnin tärkein käsite on funktionaalinen riippuvuus (functional dependency). Annetaan alkuun tarkka määritelmä relaatiomallin sanoin:
Määritelmä
Jos joukko relaation R attribuutteja X yksilöi jonkin toisen joukon saman relaation attribuutteja Y, sanotaan, että relaatiossa pätee funktionaalinen riippuvuus
{X} -> {Y}
Funktionaalisessa riippuvuudessa X on määräävä attribuuttijoukko. Sanotaan, että "Attribuutit Y riippuvat funktionaalisesti attribuuteista X" tai "Y on funktionaalisesti riippuvainen X:stä". Yleisemmin voi sanoa, että "X yksilöi Y:n".
Toisin sanoen jokaisella monikolla, jossa attribuuteilla X on tietty arvo a, on attribuuteilla Y korkeintaan yksi arvo b.
Käytännössä funktionaalinen riippuvuus on tosi lähellä avainehdokkaan määritelmää. Avainehdokas on mikä tahansa (jakamaton) relaation attribuuttijoukko, joka yksilöi monikon kaikki muut attribuutit. Funktionaalinen riippuvuus taas on mikä tahansa relaation attribuuttijoukko, joka yksilöi minkä tahansa muun joukon relaation attribuutteja.
Otetaan vielä pari esimerkkiä käsitteen selkeyttämiseksi:
Esimerkki
Palataan taas Luvussa 3.1 perusavaimen yhteydessä olevaan esimerkkiin taulusta OPISKELIJA(opnro, hetu, etunimi, sukunimi, postinro, paikkakunta) sekä aiemmassa esimerkissä keksittyyn mallidataan:
| opnro | hetu | etunimi | sukunimi | postinro | paikkakunta |
|---|---|---|---|---|---|
| o101 | 120196-111X | Matti | Meikäläinen | 33720 | Tampere |
| o102 | 130280-112X | Lumi | Sinkki | 40520 | Jyväskylä |
| o103 | 130280-321Z | Lumi | Sinkki | 40520 | Jyväskylä |
| o104 | 121240-111A | Aku | Ankka | 60200 | Seinäjoki |
| o105 | 241252-321X | Taavi | Ankka | 33720 | Tampere |
Aiemmassa esimerkissä totesimme, että {opnro} on avainehdokas. Avainehdokas yksilöi taulun kaikki muut sarakkeet: jos tiedetään opiskelijan opiskelijanumero, tiedetään yksikäsitteisesti opiskelijan muiden sarakkeiden arvot. Toisin sanoin on voimassa seuraava funktionaalinen riippuvuus:
{opnro} -> {hetu, etunimi, sukunimi, postinro, paikkakunta}
Tämä ei kerro mitään uutta mitä ei avainehdokkaasta tiedetä; se on vain eri tapa merkitä asiaa.
Vastaavasti koska {hetu} on avainehdokas, pätee myös seuraava funktionaalinen riippuvuus:
{hetu} -> {opnro, etunimi, sukunimi, postinro, paikkakunta}
Aiemmassa esimerkissä huomattiin, että sarake postinro yksilöi sarakkeen paikkakunta. Jos tiedetään, että jonkun opiskelijan postinro on 33720, tiedetään yksikäsitteisesti, että opiskelijan paikkakunta on aina Tampere. Funktionaalisen riippuvuuden merkinnöillä siis:
{postinro} -> {paikkakunta}
Esimerkki
Tarkastellaan seuraavaa taulua HENKILÖ(htun, nimi):
| htun | nimi |
|---|---|
| 010160-ABCD | Aatami Laippa |
| 020270-EFGH | Bertta Hukari |
| 030380-IJKL | Aatami Laippa |
| 040490-MNOP | Cecilia Rastas |
Vaikka emme tietäisi mitään kohdealueesta, voidaan taulun tietojen perusteella päätellä, että seuraava funktionaalinen riippuvuus pätee:
{htun} -> {nimi}
Se pätee, koska jokaista htun-sarakkeen arvoa vastaa yksikäsitteinen nimi. Esimerkiksi, jos tiedetään, että rivin htun-sarakkeen arvo on 010160-ABCD, tiedetään, että sitä vastaa yksikäsitteisesti nimi Aatami Laippa.
Seuraava funktionaalinen riippuvuus ei päde:
{nimi} -> {htun}
Se ei päde, sillä esimerkiksi nimi-sarakkeen arvolla Aatami Laippa löytyy kaksi eri mahdollista htun-sarakkeen arvoa: 010160-ABCD ja 030380-IJKL. Funktionaalinen riippuvuus tarkoittaa, että vastaavuus on yksikäsitteinen: jokaiselle nimelle pitäisi löytyä korkeintaan yksi henkilötunnus, mutta näin ei tässä ole.
On syytä huomata, että yllä olevissa esimerkeissä funktionaaliset riippuvuudet on johdettu esimerkkidatasta. Tavallisesti normalisointi saatetaan tehdä jo tietokannan suunnittelun vaiheessa, kun dataa ei vielä ole. Silloin funktionaaliset riippuvuudet on pääteltävä kohdealuetta ymmärtämällä. Vaikka funktionaalisten riippuvuuksien esittämiseen käytetään matemaattista notaatiota, ei kysymyksessä ole matemaattinen ilmiö vaan nimenomaan relaation merkityksen ymmärtäminen.
| kurssikoodi | kurssinimi | vuosi | opettajatun | laajuus |
|---|---|---|---|---|
| itka201 | Algoritmit | 2015 | op447 | 4 |
| itka201 | Algoritmit | 2014 | op051 | 3 |
| itka204 | Tietokannat | 2014 | op300 | 5 |
| itka204 | Tietokannat | 2015 | op300 | 5 |
| itka204 | Tietokannat | 2013 | op300 | 4 |
| tjta118 | IT-infra | 2014 | op411 | 3 |
| tjta118 | IT-infra | 2015 | op411 | 3 |
Päättelysäännöt
Yllä olevasta määritelmästä ja esimerkeistä voi päätellä, että jotkut funktionaaliset riippuvuudet voidaan päätellä muista riippuvuuksista. Esimerkiksi jos tiedetään, että opiskelijan opiskelijatunnus yksilöi opiskelijan etunimen, sukunimen ja hetun yhdessä:
{optun} -> {hetu, etunimi, sukunimi}
niin selvästi opiskelijan tunnus yksilöi myös erikseen hetun, etunimen ja sukunimen:
{optun} -> {hetu}
{optun} -> {etunimi}
{optun} -> {sukunimi}
Eli: jos tiedetään, että optun:lla voi löytää tietyn opiskelijan hetun, etunimen sekä sukunimen, niin totta kai optun:lla voi löytää jokaisen tiedon myös yksittäin.
Armstrong [1] on esittänyt joukon sääntöjä, joilla funktionaalisia riippuvuuksia voi päätellä muiden riippuvuuksien kautta. Sellaisia funktionaalisia riippuvuuksia, joita voidaan päätellä näiden sääntöjen avulla, kutsutaan triviaaleiksi.
Refleksiivisyyssääntö (axiom of reflectivity): jos attribuuttijoukko
Yon toisen attribuuttijoukonXosajoukko, pätee triviaali funktionaalinen riippuvuus "XyksilöiY:n". Eli:Jos
Y⊂X, niin{X} -> {Y}.Säännön seurauksena attribuuttien joukko
Xriippuu aina itsestään, eli:{X} -> {X}Esimerkki: Opiskelijan etunimi riippuu opiskelijan etunimestä, eli
{etunimi} -> {etunimi}. Opiskelijan etunimi riippuu myös etunimen ja sukunimen yhdistelmästä:{etunimi, sukunimi} -> {etunimi}.Transitiivisuussääntö (axiom of transitivity): jos pätevät funktionaaliset riippuvuudet "
XyksilöiY:n" ja "YyksilöiZ:n", pätee myös funktionaalinen riippuvuus "XyksilöiZ:n". Eli:Jos
{X} -> {Y}ja{Y} -> {Z}, niin{X} -> {Z}.Esimerkki: Opiskelijan hetusta tiedetään hänen etunimensä ja sukunimensä, eli
{hetu} -> {etunimi, sukunimi}. Toisaalta jos tiedetään etunimi ja sukunimi, tiedetään hänen nimikirjaimensa, eli{etunimi, sukunimi} -> {nimikirjaimet}. Siispä opiskelijan hetusta tiedetään hänen nimikirjaimensa, eli{hetu} -> {nimikirjaimet}.Yhdistesääntö (axiom of union): jos pätevät funktionaaliset riippuvuudet "
XyksilöiY:n" ja "XyksilöiZ:n", pätee myös funktionaalinen riippuvuus "XyksilöiY:n jaZ:n yhdisteen". Eli:Jos
{X} -> {Y}ja{X} -> {Z}, niin{X} -> {Y, Z}.Esimerkki: Opiskelijan opiskelijatunnus yksilöi opiskelijan etunimen, eli
{optun} -> {etunimi}. Toisaalta myös opiskelijan opiskelijatunnus yksilöi myös opiskelijan sukunimen, eli{optun} -> {sukunimi}. Siispä opiskelijan tunnus yksilöi opiskelijan etunimen sekä sukunimen, eli{optun} -> {etunimi, sukunimi}.Jakosääntö (axiom of decomposition): jos pätee funktionaalinen riippuvuus "
XyksilöiY:n jaZ:n yhdisteen", pätevät myös funktionaaliset riippuvuudet "XyksilöiY:n" ja "XyksilöiZ:n". Eli:Jos
{X} -> {Y, Z}, niin{X} -> {Y}sekä{X} -> {Z}.Esimerkki: Opiskelijan hetu yksilöi etunimen ja sukunimen, eli
{hetu} -> {etunimi, sukunimi}. Siispä opiskelijan hetu yksilöi etunimen ({hetu} -> {etunimi}) sekä sukunimen ({hetu} -> {sukunimi}).Täydentämissääntö (axiom of augmentation): jos pätee funktionaalinen riippuvuus "
XyksilöiY:n", pätee myös funktionaalinen riippuvuus "X:n jaZ:n yhdistelmä yksilöiY:n", ts. jos "XyksilöiY:n", niin "X:n ylijoukot myös yksilöivätY:n". Eli:Jos
{X} -> {Y}, niin{X, Z} -> {Y}.Esimerkiksi: Opiskelijan hetu yksilöi opiskelijan sukunimen, eli
{hetu} -> {sukunimi}. Riippuvuus pätee, vaikka määräävään joukkoon lisättäisiin vaikkapa etunimi, eli{hetu, etunimi} -> {sukunimi}.
Huomautus
Jakosäännön ja yhdistesäännön yksi olennainen seuraus on, että funktionaalinen riippuvuus
{X} -> {Y1, Y2, ..., Yn}
on yhtäpitävä sen kanssa, että kaikki seuraavat riippuvuudet pätevät samanaikaisesti:
{X} -> {Y1}
{X} -> {Y2}
...
{X} -> {Yn}
ja myös toisinpäin. Toisin sanoin ei ole väliä, merkitäänkö funktionaaliset riippuvuudet erillään vai yhdistettynä samaan riippuvuuteen.
Huomautus
Huomaa, että yllä olevat säännöt ovat muotoa "jos (1) niin (2)". Sääntö ei välttämättä päde toisin päin, eli "jos (2) niin (1)" ei ole aina totta!
Esimerkiksi yleisin virhepäätelmä on, että määräävää attribuuttijoukkoa voi aina jakaa säilyttäen funktionaalinen riippuvuus!
Tämän luvun alussa olevassa opintosuoritusten tietokannassa on seuraava perusavaimen mukainen riippuvuus:
{optun, kurssitun} -> {opnimi, kurssinimi, arvosana}
(opiskelijatunnus ja kurssitunnus yhdessä yksilöivät opiskelijan nimen, kurssin nimen ja suorituksen arvosanan).
Tässä voi tulla esimerkiksi halu jättää kurssitun pois määräävästä attribuuttijoukosta, sillä esimerkiksi opiskelijan nimi ei riipu kurssin tunnuksesta. Kuitenkaan tämä riippuvuus:
{optun} -> {opnimi, kurssinimi, arvosana}
ei enää välttämättä päde, sillä kahdella eri opiskelijalla voi olla sama nimi, suoritettu kurssi ja jopa sama arvosana. Samasta syystä opiskelijan tunnusta ei voi jättää pois määräävästä joukosta.
Sen sijaan tässä tapauksessa huomataan, että ottamalla attribuutteja pois sekä määräävästä että riippuvasta joukosta saadaan useita valideja funktionaalisia riippuvuuksia:
{optun} -> {opnimi}
{kurssitun} -> {kurssinimi}
{optun, kurssitun} -> {arvosana}
Ole siis tarkkana, että sovellat säännöt ns. oikein päin!
Alla olevassa kuviossa on esitetty toinen tapa kuvata funktionaalisia riippuvuuksia. Nuoli osoittaa tässäkin määräävästä attribuuttijoukosta riippuvaan attribuuttiin. Funktionaaliset riippuvuudet on numeroitu monivalintatehtävän vuoksi.
R attribuutit funktionaalisine riippuvuuksineen.These are the current permissions for this document; please modify if needed. You can always modify these permissions from the manage page.