Luku 5

Luku 5
SQL
T¨
am¨
an luvun tarkoituksena on perehdytt¨
a¨
a lukija SQL-kieleen1 . Asiat on
pyritty esitt¨
am¨
a¨
an siten, ett¨
a SQL:n omaksuminen ei vaadi relaatioalgebran
hallintaa, mutta ennen SQL:¨
a¨
an perehtymist¨
a on kuitenkin syyt¨
a tutustua
ainakin karteesisen tulon k¨
asitteeseen. Esityksess¨
a on my¨
os pyritty yksinkertaisuuteen, joten syntaksin l¨
apik¨
aymisen sijasta SQL esitell¨
a¨
an l¨
ahinn¨
a idiomeja eli kielelle tyypillisi¨
a ilmauksia k¨
aytt¨
aen. SQL:n syntaksi on muutenkin
hyvin laaja ja useimmat tietokannanhallintaj¨
arjestelm¨
at poikkeavat standardista, joten tietty¨
a tietokannahallintaj¨
arjestelm¨
a¨
a k¨
aytett¨
aess¨
a on syyt¨
a tutustua
tietokannanhallintaj¨
arjestelm¨
an manuaaleihin.
5.1
5.1.1
SQL:n perusteita
SQL:n tietotyypit
M¨
a¨
aritelt¨
aess¨
a attribuutteja SQL:n avulla on jokaiselle attribuutille
m¨
a¨
aritelt¨
av¨
a jokin tietotyyppi, joka kertoo, millaisia arvoja kyseinen
attribuutti voi saada. Tietotyypeilt¨
a¨
an SQL vastaa joitain poikkeuksia lukuunottamatta tavallisimpia ohjelmointikielti¨
a, kuten esimerkiksi Javaa. SQL:n
t¨
arkeimm¨
at numeeriset tyypit ovat seuraavat:
1. SMALLINT eli kahden tavun avulla tallennettava etumerkillinen kokonaisluku.
2. INTEGER eli INT on nelj¨
an tavun avulla tallennettava etumerkillinen
kokonaisluku.
3. REAL vastaa nelj¨
an tavun avulla tallennettavaa etumerkillist¨
a reaalilukua.
4. DOUBLE PRECISION eli DOUBLE on kahdeksan tavun avulla tallennettava
etumerkillinen reaaliluku.
1 Tarkasti ottaen luvussa perehdyt¨
a¨
an PostgreSQL-tietokannanhallintaj¨
arjestelm¨
an tukemaan SQL-murteeseen [5], jonka perustana on vuonna 1992 esitelty SQL2-standardi. Ensimm¨
aisen kerran SQL standardoitiin 1986 ja vuonna 1999 esiteltiin uusi standardi SQL3,
joka sis¨
alt¨
a¨
a mm. joitakin olio-ominaisuuksia. K¨
ayt¨
ann¨
oss¨
a SQL3:n merkitys on kuitenkin
j¨
a¨
anyt v¨
ah¨
aiseksi.
31
5. DECIMAL(P ituus, Desimaalit), miss¨
a P ituus on luvun numeropositioiden kokonaism¨
a¨
ar¨
a ja Desimaalit desimaalien m¨
a¨
ar¨
a, on k¨
aytt¨
aj¨
an
m¨
a¨
arittelem¨
an tarkkuuden etumerkillinen reaaliluku.
Tietotyyppi DECIMAL on k¨
ayt¨
ann¨
ollinen esimerkiksi silloin, kun on
tallennettava raham¨
a¨
ari¨
a. Pankkitilin saldo voitaisiin m¨
a¨
aritell¨
a tyypin
DECIMAL(9,2) avulla; tilin maksimisaldo olisi siten 9999999.99. T¨
arkeimm¨
at
merkkijonotyypit ovat seuraavat:
Merkkijonoja varten SQL:ss¨
a on m¨
aa
¨ritelty koko joukko erilaisia
tietotyyppej¨
a, joiden tallennuskoko voi olla joko kiinnitetty kuten numeerisilla
tyypeill¨
a tai vaihteleva.
1. CHARACTER(P ituus) eli CHAR(P ituus) vastaa merkkijonoa, jonka
maksimipituus on P ituus ja jonka tallentamiseen k¨
aytet¨
a¨
an aina P ituus
tavua.
2. CHARACTER VARYING(P ituus) eli VARCHAR(P ituus) vastaa vaihtelevan
pituista merkkijonoa, jonka maksimipituus on P ituus ja jonka
tallentamiseen k¨
aytet¨a¨
an enint¨
a¨
an P ituus tavua2 .
3. TEXT vastaa rajoittamattoman pituista merkkijonoa, jonka tallentamiseen
k¨
aytet¨
a¨
an vaihteleva m¨
a¨
ar¨
a tavuja3 .
Kaikissa merkkijonotyypeiss¨
a SQL erottelee toisistaan isot ja pienet
kirjaimet, joten esimerkiksi ”Jaskan vimpain” ja ”jaskan vimpain” eiv¨
at ole
ekvivalentit. Valittaessa tietotyyppi¨
a merkkijonolle on syyt¨
a mietti¨
a, millaisia
tallennettavat merkkijonot yleens¨
a ovat. Jos merkkijonojen pituus vaihtelee
suuresti, on levytilan s¨
a¨
ast¨
amiseksi syyt¨
a valita VARCHAR tai TEXT. Jos taas
tallennettavat merkkijonot ovat useimmiten yht¨
a pitki¨
a, kuten esimerkiksi
autojen rekisterinumerot, on kannattavampaa valita CHAR. Tallennettujen
merkkijonojen pituustietojen tarkistamiseen ja yll¨
apitoon nimitt¨
ain kuluu
jonkin verran ylim¨
a¨
ar¨
aist¨
a aikaa, mik¨
a hidastaa kyselyit¨
a ja p¨
aivityksi¨
a.
P¨
aiv¨
am¨
a¨
arien ja kellonaikojen tallentamiseen on SQL:ss¨
a on m¨
a¨
aritelty
seuraavat tyypit:
1. DATE vastaa p¨
aiv¨am¨
a¨
ar¨
a¨
a; p¨
aiv¨
am¨
a¨
ar¨
at esitet¨
a¨
an useimmiten
amerikkalaisittain eli muodossa YYYY-MM-DD. 1980-07-04 vastaa
siten p¨
aiv¨
am¨
a¨
ar¨
a¨
a 4.7. 1980.
2. Tyyppi TIME vastaa kellonaikaa sekuntin tarkkuudella. Useimmissa tietokannanhallintaj¨
arjestelmiss¨
a kellonajat ilmoitetaan muodossa
HH:MM:SS, eli esimerkiksi muodossa 04:05:06. K¨
ayt¨
ann¨
oss¨
a kaikki tietokannanhallintaj¨
arjestelm¨
at sallivat kellonaikojen tallentamisen tarvittaessa my¨
os millisekuntin tarkkuudella. T¨
all¨
oin kellonaika ilmaistaan useimmiten esimerkiksi muodossa 04:05:06.789.
3. TIMESTAMP eli aikaleima on tyyppien DATE ja TIME yhdistelm¨
a.
2 K¨
ayt¨
ann¨
oss¨
a tallentamiseen tarvitaan muutamia tavuja enemm¨
an, sill¨
a my¨
os tallennetun
merkkijonon pituus on tallennettava
3 Tyyppi TEXT ei oikeastaan kuulu SQL2-standardiin.Valtaosa nykyisist¨
a tietokannahallintaj¨
arjestelmist¨
a kuitenkin kuitenkin tukee t¨
at¨
a tietotyyppi¨
a.
32
Edell¨
a esiteltyjen tietotyyppien lis¨
aksi monissa tietokannanhallintaj¨
arjestelmiss¨
a on m¨
a¨
aritelty useita muita, toisinaan varsin erikoisiakin
tietotyyppej¨
a. Monissa j¨
arjestelmiss¨
a bin¨
a¨
aridatan, kuten esimerkiksi kuvien,
videoiden ja ¨
a¨
anitiedostojen, tallentamiseen tarjotaan omaa tietotyyppi¨
a;
esimerkiksi PostgreSQL:ss¨
a voidaan k¨
aytt¨
a¨
a tyyppi¨
a BYTEA eli rajoittamattoman pituista tavutaulukkoa, jonka tallennetamiseen k¨
aytet¨
aa
¨n vaihteleva
m¨
a¨
ar¨
a tavuja. BYTEA vastaa siis tyyppi¨
a TEXT sill¨
a erotuksella, ett¨
a mill¨
a¨
an
merkeill¨
a, kuten esimerkiksi sisennyksill¨
a ja rivinvaihdoilla, ei ole erityismerkityst¨
a. PostgreSQL:ss¨
a on mahdollista k¨
aytt¨
a¨
a my¨
os useita geometrisi¨
a tietotyyppej¨
a, kuten pisteit¨
a, suoria, ympyr¨
oit¨
a ja polygoneja. T¨
am¨
a saattaa olla
hyvinkin hy¨
odyllist¨
a, jos tietokannalla on tarkoitus k¨
asitell¨
a geometrist¨
a dataa.
Java-yhteensopivana mainostettu McKoi-tietokanta taas tarjoaa tietotyypin
JAVA OBJECT, joka vastaa sarjallistettua Java-oliota.
5.1.2
Kolmiarvologiikka ja NULL
Paitsi attribuuttien arvona, m¨
a¨
arittelem¨
at¨
on arvo NULL esiintyy relaatiotietokantojen yhteydess¨
a my¨os totuusarvona. Logiikkaa, jossa perinteisten totuusarvojen TRUE ja FALSE lis¨
aksi esiintyy my¨
os m¨
a¨
arittelem¨
at¨
on arvo NULL, kutsutaan kolmiarvologiikaksi. Useimmissa tapauksissa arvon NULL voi huoletta
samaistaa totuusarvoon ”ehk¨
a”, mutta joissakin tilanteissa - kuten esimerkiksi alikyselyiden yhteydess¨
a - kolmiarvologiikka saattaa tuottaa yll¨
atyksi¨
a. Siksi
asiaan on syyt¨
a perehty¨
a hieman tarkemmin.
Negaatio NOT toimii oikeastaan hyvin johdonmukaisesti. NOT FALSE = TRUE
ja NOT TRUE = FALSE. NOT NULL puolestaan on NULL, mit¨
a voi perustella
esimerkiksi siten, ett¨
a ”ehk¨
a ei” tarkoittaa oikestaan samaa kuin ”ehk¨
a”.
Konjunktion AND ja disjuktion OR kolmiarvologiikan mukaiset totuustaulut 5.1
ja 5.2 puolestaan saattavat n¨
aytt¨
a¨
a monimutkaisilta, mutta operaatiot toimivat
oikeastaan t¨
aysin kuten bin¨
a¨
arisen logiikan vastineensa. Perinteiseen tapaan
FALSE AND Arvo = FALSE, TRUE AND Arvo = Arvo, FALSE OR Arvo = Arvo
ja TRUE OR Arvo = TRUE mille tahansa totuusarvolle Arvo, siis my¨
os arvolle
NULL.
Taulukko 5.1: Konjunktion AND kolmiarvologiikan mukainen totuustaulu.
FALSE NULL
TRUE
FALSE FALSE FALSE FALSE
FALSE NULL
NULL
NULL
TRUE
FALSE NULL
TRUE
Taulukko 5.2: Disjunktion OR kolmiarvologiikan mukainen totuustaulu.
FALSE NULL TRUE
FALSE FALSE NULL TRUE
NULL
NULL TRUE
NULL
TRUE
TRUE TRUE
TRUE
Kun NULL esiintyy totuusarvon sijasta attribuutin arvona, se voidaan
33
tulkita tuntemattomaksi arvoksi. T¨
ast¨
a seuraa muutamia huomionarvoisia
seikkoja erityisesti silloin, kun NULL-arvoa vertaillaan muihin arvoihin. Jos
toinen vertailtavista arvoista nimitt¨
ain on NULL, on vertailun tulos aina
NULL. Esimerkiksi vertailut 7 < NULL, ’Orimatti’ = NULL ja jopa NULL
= NULL evaluoituvat siten totuusarvoksi NULL, mik¨
a saattaa aluksi tuntua
kummalliselta. Jos siis haluamme tarkastaa, onko jokin arvo tosiaan NULL, on
meid¨
an k¨
aytett¨
av¨
a IS NULL -vertailua; NULL IS NULL evaluoituu totuusarvoksi
TRUE.
5.2
Tietokannan m¨
a¨
arittely SQL:n avulla
5.2.1
Tietokannan luominen
Tietokannan luominen SQL:n avulla on hyvin yksinkertaista. Seuraavalla SQLkomennolla voimme luoda tietokannan, jonka nimi on Firma:
CREATE DATABASE Firma;
Yleisess¨
a tapauksessa komento on muotoa
CREATE DATABASE T ietokanta;
Edellisess¨
a komennossa T ietokanta on tietenkin luotavan tietokannan nimi.
Monissa tietokannanhallintaj¨
arjestelmiss¨
a luontioperaatiolle voidaan antaa
lis¨
am¨
a¨
areit¨
a, kuten esimerkiksi k¨
aytett¨
avien levyblokkien koko. Tietokanta
voidaan tuhota komennolla
DROP DATABASE T ietokanta;
Kannattaa huomata, ett¨
a tietokannanhallintaj¨
arjestelm¨
at eiv¨
at pyyd¨
a
k¨
aytt¨
aj¨
a¨
a vahvistamaan tietokannan tuhoamista, joten h¨
at¨
ainen k¨
aytt¨
aj¨
a voi
helposti saada aikaan paljon vahinkoa. T¨
allaisten tilanteiden v¨
altt¨
amiseksi
tietokannanhallintaj¨
arjestelm¨
at tarjoavat mahdollisuuden luoda k¨
aytt¨
aji¨
a ja
rajoittaa k¨
aytt¨
ajien oikeuksia.
5.2.2
Taulun luominen
Tauluja voidaan luoda komennolla CREATE TABLE. Seuraava komento luo
kuvassa 2.3 esitetyn tietokannan Asiakas-relaatiota vastaavan taulun:
CREATE TABLE Asiakas (
Id INT NOT NULL,
Etunimi VARCHAR32 NOT NULL,
Sukunimi VARCHAR32 NOT NULL,
PRIMARY KEY (Id)
);
Taulu luodaan siis yksinkertaisesti luettelemalla attribuutit ja niiden
tyypit. Lis¨
am¨
a¨
are NOT NULL kertoo, ett¨
a kyseisen attribuutin arvoksi ei
voida asettaa m¨
a¨
arittelem¨at¨
ont¨
a arvoa NULL. PRIMARY KEY taas m¨
a¨
arittelee
tietysti taulun p¨
a¨
aavaimen. Entiteettieheyden takaamiseksi avainattribuutit 34
eli p¨
aa
aa
a NOT
¨avaimeen kuuluvat attribuutit - on tietysti varustettava m¨
¨reell¨
NULL. Jos n¨
ain ei tehd¨
a, ilmoittavat useimmat tietokannanhallintaj¨
arjestelm¨
at
virheest¨
a. Monet tietokannanhallintaj¨
arjestelm¨
at sallivat muitakin lis¨
am¨
a¨
areit¨
a,
kuten esimerkiksi oletusarvon m¨
a¨
arittelyn. Yleisesti komento CREATE TABLE on
muotoa
CREATE TABLE T aulu (
Attribuutti1 T yyppi1 [NOT NULL] [DEFAULT Arvo1 ],
...
Attribuuttin T yyppin [NOT NULL] [DEFAULT Arvon ],
[PRIMARY KEY (Avain1 , ..., Avainm )],
[FOREIGN KEY (V iite1 , ..., V iitel ) REFERENCES Kohde
ON DELETE (CASCADE | RESTRICT | SET DEFAULT)
ON UPDATE (CASCADE | RESTRICT | SET DEFAULT)],
...
);
Edellisess¨
a komennossa valinnaiset osat on kirjoitettu merkkien [ ja ]
sis¨
aa
a¨
aritell¨
a¨
an FOREIGN KEY -osassa. Kos¨n4 . Taulua luotaessa viitevavaimet m¨
ka relaatiotietokannan on oltava ehe¨
a, taulun viitevaimia m¨
a¨
aritelt¨
aess¨
a on otettava kantaa my¨
os siihen, miten viite-eheyden rikkoviin p¨
aivitysoperaatioihin
suhtaudutaan. Viite-eheys saattaa rikkoutua kahdella tavalla: viittauksen kohteena olevan monikon avainattribuuttien arvoja voidaan muuttaa tai viittauksen kohteena oleva monikko voidaan tuhota. Viiteavainm¨
a¨
arittelyn ON UPDATE
-osa liittyy luonnollisesti edelliseen tapaukseen ja ON DELETE j¨
alkimm¨
aiseen.
Molemmissa tapauksissa voidaan k¨
aytt¨
a¨
a seuraavia m¨
a¨
arittelyj¨
a:
1. CASCADE eli vy¨
orytys tarkoittaa sit¨
a, ett¨
a muutokset propagoidaan
viittaaviin monikoihin. ON UPDATE -tapauksessa eli silloin, jos jonkin
monikon p¨
a¨
aavaimen arvoa muutetaan, muutetaan vastaavasti my¨
os
viittaavien monikoiden viiteavainten arvoja. ON DELETE -tapauksessa
eli silloin, kun monikko tuhotaan, johtaa CASCADE-m¨
a¨
arittely kaikkien
tuhottavaan monikkoon viittaavien monikkojen tuhoamiseen.
2. RESTRICT-m¨
a¨
arittely est¨
a¨
a viite-eheyden rikkovat operaatiot. Tietokannanhallintaj¨
arjestelm¨
a ei t¨
all¨
oin suorita viite-eheytt¨
a rikkovia operaatioita vaan ilmoittaa virheest¨
a.
3. SET DEFAULT asettaa viite-eheyden rikkoutuessa viiteavaimen arvoksi attribuuttim¨
a¨
arittelyn DEFAULT-osassa m¨
a¨
aritellyn oletusarvon. Esimerkiksi yrityksen tietokannassa, jossa jokainen ty¨
ontekij¨
amonikko viittaa ty¨
ontekij¨
an esimiest¨
a vastaavaan monikkoon, voisimme m¨
aa
a
¨ritell¨
”oletusesimiehen”. Jos ty¨
ontekij¨
an esimiest¨
a vastaava rivi siis poistettaisiin, asettaisi tietokannanhallintaj¨
arjestelm¨
a ty¨
ontekij¨
an esimieheksi t¨
am¨
an
oletusesimiehen.
Edell¨
a listatuista vaihtoehdoista on jokaiselle viiteavaimelle valittava sopivin
vaihtoehto. ON UPDATE -tapauksessa on luonnollisinta valita joko CASCADE tai
4 P¨
a¨
aavaimen m¨
a¨
aritteleminen siis on SQL-standardin mukaan vapaaehtoista, mutta
yleens¨
a avain kannattaa m¨
a¨
aritell¨
a. Ilman p¨
a¨
aavaimiahan ei voi olla viiteavaimia ja ilman
viiteavaimia ei moneen tauluun hajautettua tietoa voida k¨
atev¨
asti hallita.
35
RESTRICT. CASCADE sopii tietysti paremmin tapauksiin, joissa avaimia joudutaan
toisinaan p¨
aivitt¨
am¨
a¨
an5 . Jos viiteavain taas viittaa pysyv¨
a¨
an avaimeen, kuten
esimerkiksi sosiaaliturvatunnukseen, voidaan valita RESTRICT.
ON DELETE -tapaus tuottaa usein huomattavasti enemm¨
an p¨
a¨
anvaivaa. Miten
esimerkkitapauksessamme pit¨
aisi menetell¨
a esimerkiksi silloin, kun Toimittajataulusta tuhotaan rivi, johon jotkin Tuote-taulun rivit viittaavat? Jos haluamme
tuhota tuhottavaan Toimittaja-taulun monikkoon viittaavat Tuote-taulun
monikot, m¨
a¨
arittelemme Tuote-taulun seuraavasti:
CREATE TABLE Tuote (
Id INT NOT NULL,
Nimi VARCHAR32 NOT NULL,
Kuvaus VARCHAR64 NOT NULL,
Hinta DECIMAL(7,2) NOT NULL,
ToimittajaId INT NOT NULL,
PRIMARY KEY (Id),
FOREIGN KEY (ToimittajaId) REFERENCES Toimittaja
ON UPDATE CASCADE ON DELETE CASCADE
);
On t¨
arke¨
a¨
a huomata, ett¨
a viite-eheysrikkomuksiin otetaan kantaa nimenomaan sen taulun m¨
a¨
arittelyss¨
a, jossa viiteavaimet sijaitsevat. T¨
am¨
a johtuu siit¨
a, ett¨
a yhteen ja samaan p¨a¨
aavaimeen voi viitata useampi vierasavain. Tilaustaulu voitaisiin esimerkiksi m¨
a¨
aritell¨
a seuraavasti:
CREATE TABLE Tilaus (
AsiakasId INT NOT NULL,
TuoteId INT NOT NULL,
PRIMARY KEY (AsiakasId, TuoteId),
FOREIGN KEY (AsiakasId) REFERENCES Asiakas
ON UPDATE CASCADE ON DELETE CASCADE,
FOREIGN KEY (TuoteId) REFERENCES Tuote
ON UPDATE CASCADE ON DELETE RESTRICT
);
Jos Asiakas-taulusta siis tuhotaan rivi, tuhotaan my¨
os siihen viittaavat Tilaus-taulun rivit ja jos asiakkaan attribuuttia Id p¨
aivitet¨
a¨
an,
p¨
aivitet¨
a¨
an my¨
os viittaavien Tilaus-taulun rivien vierasavainattribuuttia
AsiakasId. Samalla tavoin p¨
aivitet¨
aa
¨n Tilaus-taulun toisen vierasavainattribuutin TuoteId arvoa, jos viittauksen kohteena olevan Tuote-taulun rivin
p¨
a¨
aavainattribuutin Id arvoa p¨
aivitet¨
a¨
an. ON DELETE RESTRICT -m¨
a¨
arittely
taas est¨
a¨
a sellaisten Tuote-taulun rivien tuhoamisen, joihin jokin Tilaus-taulun
rivi viittaa.
Taulua voidaan my¨
ohemmin muuttaa ALTER TABLE -komennolla, jonka
avulla voidaan esimerkiksi muuttaa relaation kaavaa eli lis¨
at¨
a tai poistaa
attribuutteja. Taulu tuhotaan seuraavasti:
DROP TABLE T aulu;
5 Jos avaimia joudutaan p¨
aivitt¨
am¨
a¨
an usein, on syyt¨
a mietti¨
a, onko relaation p¨
a¨
aavain
valittu oikein.
36
5.2.3
K¨
aytt¨
aj¨
an luominen
Kun
tietokannanhallintaj¨
arjestelm¨
a
asennetaan,
luodaan
samalla
k¨
aytt¨
aj¨
atunnus tietokannan hallinnasta vastaavalle k¨
aytt¨
aj¨
alle eli DBA:lle.
PostgreSQL:ss¨
a DBA:n tunnus on oletusarvoisesti postgres. Koska DBA:n
oikeudet ovat k¨
ayt¨
ann¨
oss¨
a rajoittamattomat, on tietokannahallintaj¨
arjestelm¨
an
tarjottava mahdollisuus v¨
ah¨
aisemmill¨
a oikeuksilla varustettujen k¨
aytt¨
ajien
luomiseen. Voimme esimerkiksi luoda asiakask¨
aytt¨
aj¨
an seuraavasti:
CREATE USER Asiakas WITH PASSWORD 123abc;
Yleisess¨
a tapauksessa komento on muotoa:
CREATE USER T unnus WITH PASSWORD Salasana;
Edellisess¨
a komennossa T unnus on tietenkin k¨
aytt¨
aj¨
atunnus ja Salasana
k¨
aytt¨
aj¨
an salasana. K¨
aytt¨
ajille voidaan antaa erilaisia oikeuksia tietokantojen
ja taulujen k¨
asittelyyn, luomiseen ja tuhoamiseen joko k¨
aytt¨
aji¨
a luotaessa tai
my¨
ohemmin k¨
askyll¨
a GRANT. Esimerkiksi kaikille verkkopankin tai verkkokaupan
k¨
aytt¨
ajille ei luoda omaa k¨
aytt¨
aj¨
atunnusta, vaan kaikki verkkopalvelun
k¨
aytt¨
aj¨
at toimivat samalla k¨
aytt¨
aj¨
atunnuksella ja samoilla, varsin rajatuilla
oikeuksilla. K¨
aytt¨
aj¨
a tuhotaan komennolla:
DROP USER T unnus;
5.2.4
Hakemiston luominen
Hakemisto eli indeksi on rakenne, jonka tarkoituksena on nopeuttaa
tiedon hakemista tietokannasta. Toisaalta hakemistot taas hidastavat
p¨
aivitysoperaatioita, joten jokaisen hakemiston luomista on punnittava tarkasti.
Seuraava komento luo hakemiston, jonka avulla voidaan tehokkasti hakea
asiakkaita nimen perusteella:
CREATE INDEX Nimi ON Asiakas(Sukunimi,Etunimi);
Edellinen hakemisto mahdollistaa tehokkaat haut tilanteessa, jossa
asiakkaita haetaan joko sukunimen tai sek¨
a sukunimen ett¨
a etunimen
perusteella. Hakemisto ei kuitenkaan nopeuta pelk¨
an etunimen perusteella
hakemista. Yleisess¨
a tapauksessa komento on seuraavaa muotoa:
CREATE INDEX N imi ON T aulu(Attribuutti1 ,...,Attribuuttin );
Hakemisto voidaan my¨
ohemmin m¨
aa
a ryv¨
ast¨
av¨
aksi eli klus¨ritell¨
teroiduksi indeksiksi eli indeksiksi, jonka mukaan taulun rivit on fyysisesti
j¨
arjestetty kiintolevylle. Oletuksena ryv¨
ast¨
av¨
a indeksi luodaan taulun avainattribuuteille, mutta joissakin tapauksissa on edullista valita jotkin muut attribuutit. Tyypillisimmill¨
a¨
an ryv¨
ast¨
av¨
a hakemisto luodaan silloin, kun taulusta haetaan usein monikkoja k¨
aytt¨
aen hakuehtona arvov¨
ali¨
a, kuten esimerkiksi hintav¨
ali¨
a tai hintaluokkaa. Esimerkkitietokannassamme saattaisi siten olla hy¨
odyllist¨
a rakentaa ryv¨
ast¨
av¨
a indeksi Tuote-taulun attribuutille Hinta.
Esimerkiksi PostgreSQL ei kuitenkaan tarjoa mahdollisuutta m¨
a¨
aritell¨
a indeksi¨
a ryv¨
ast¨
av¨
aksi; vain p¨
a¨
aavaimelle automaattisesti luotava indeksi siis on
ryv¨
ast¨
av¨
a. Kiintolevylle talletettujen monikkojen j¨
arjest¨
aminen uudelleen on
kuitenkin PostgreSQL:ss¨
akin mahdollista. Kun taululle T aulu on luotu indeksi
N imi, voidaan monikot j¨
arjest¨
a¨
a hakemistoattribuuttien mukaan seuraavasti:
37
Id
Asiakas
Etunimi Sukunimi
1
2
3
Orimatti
Laupias
Hanhikki
Teuras
Psaltari
Hillosilm¨
a
Etunimi
Sukunimi
Orimatti
Laupias
Hanhikki
Teuras
Psaltari
Hillosilm¨
a
Kuva 5.1: Esimerkki SQL kyselyn toiminnasta.
CLUSTER N imi ON T aulu;
Hakemisto tuhotaan komennolla:
DROP INDEX N imi;
5.3
Kyselyt SQL:n avulla
5.3.1
Yhteen tauluun kohdistuvat kyselyt
Kuten relaatioalgebran operaatioidenkin kohdalla, my¨
os SQL-kyselyn tulos on
aina relaatio eli taulu. Peruskysely koostuu SELECT-, FROM- ja WHERE-osista,
joiden merkitys on seuraava:
1. SELECT-osassa luetellaan tulostauluun valittavat sarakkeet. Jos halutaan
valita kaikki sarakkeet, voidaan k¨
aytt¨
a¨
a jokerimerkki¨
a *.
2. FROM-osassa m¨
a¨
aritell¨a¨
an, mist¨
a taulusta sarakkeita valitaan.
3. WHERE-osassa m¨
a¨
aritell¨
a¨
an mitk¨
a rivit FROM-osassa m¨
a¨
aritellyst¨
a
relaatiosta valitaan. Jos halutaa valita kaikki rivit, voidaan WHERE-osa
j¨
att¨
a¨
a kokonaan pois.
Yhteen relaatioon kohdistuva SQL-kysely siis vastaa sarakkeita valitsevan
projektion ja rivej¨
a valitsevan valintaoperaation yhdistelm¨
a¨
a siten, ett¨
a
valittavat sarakkeet eli attribuutit luetellaan SELECT-osassa ja rivien
valintaehto kirjoitetaan WHERE-osaan. Esimerkiksi seuraava SQL-kysely, jota on
havainnollistettu kuvassa 5.1, valitsee kaikkien asiakkaiden etu- ja sukunimet:
SELECT Etunimi, Sukunimi
FROM Asiakas;
Kuten relaatioalgebrassakin, merkkijonovakiot erotetaan attribuuttien
nimist¨
a k¨
aytt¨
am¨
all¨
a heittomerkki¨
a. Seuraava kysely, jota on havainnollistettu
kuvassa 5.2, valitsee siten ne asiakkaat, joiden etunimi on ”Orimatti”:
SELECT *
FROM Asiakas
WHERE Etunimi=’Orimatti’;
Tietokannanhallintaj¨
arjestelm¨
an voi ajatella evaluoivan yhteen tauluun
kohdistuvia kyselyit¨
a seuraavasti:
38
Id
Asiakas
Etunimi Sukunimi
1
2
3
Orimatti
Laupias
Hanhikki
Teuras
Psaltari
Hillosilm¨
a
Id
Etunimi
Sukunimi
1
Orimatti
Teuras
Kuva 5.2: Esimerkki SQL-kyselyn toiminnasta.
Tuote
Id
Nimi
Kuvaus
Hinta
ToimittajaId
1
2
3
4
Hilavitkutin
Vimpain
Masiina
Jiirisaha
Laatuisa vitkutin
Vimmattu vimpaaja
Tuotantov¨
aline lis¨
aarvon tuottamiseen
Komea s¨
ahk¨
oty¨
okalu
10.99
99.99
12.50
0.99
2
1
1
2
Id
Nimi
Kuvaus
Hinta
ToimittajaId
1
2
3
Hilavitkutin
Vimpain
Masiina
Laatuisa vitkutin
Vimmattu vimpaaja
Tuotantov¨
aline lis¨
aarvon tuottamiseen
10.99
99.99
12.50
2
1
1
Nimi
Hilavitkutin
Vimpain
Masiina
Kuva 5.3: Esimerkki SQL-kyselyn toiminnasta.
1. Tietokannanhallintaj¨arjestelm¨
a hakee FROM-osassa m¨
a¨
aritellyst¨
a relaatiosta WHERE-osassa esitetyn ehdon t¨
aytt¨
avat rivit.
2. Tietokannanhallintaj¨arjestelm¨
a valitsee edellisen vaiheen tulostaulusta
SELECT-osassa luetellut attribuutit eli sarakkeet.
Seuraavassa kyselyss¨
a, joka valitsee yli kymmenen euroa maksavien
tuotteiden nimet, voidaan siten erottaa kuvassa 5.3 esitetyt vaiheet:
SELECT Nimi
FROM Tuote
WHERE Hinta>10;
Ensimm¨
aisess¨
a vaiheessa valitaan WHERE-osassa m¨
a¨
aritellyt rivit eli ne rivit,
joilla Hinta-attribuutin arvo on suurempi kuin 10. N¨
am¨
a rivit on tummennettu
kuvassa 5.3. T¨
am¨
an j¨
alkeen valitaan SELECT-osassa m¨
a¨
aritellyt sarakkeet eli
t¨
ass¨
a tapauksessa vain attribuuttia Nimi vastaava sarake; sarake on tummenettu
kuvassa 5.3. Kyselyn tuloksena saadaan siten kuvassa 5.3 alinna esitetty taulu.
Relaatioalgebrasta poiketen SQL-kyselyn tulostaulu saattaa sis¨
alt¨
a¨
a rivej¨
a,
joiden sis¨
alt¨
o on t¨
asm¨
alleen sama. Esimerkiksi seuraava kysely tuottaa
tuloksenaan joukon {<2>, <1>, <1>, <2>}:
SELECT ToimittajaId
FROM Tuote;
39
Suku
Etu
Hillosilm¨
a
Psaltari
Teuras
Hanhikki
Laupias
Orimatti
Kuva 5.4: Yksinkertaisen SQL-kyselyn tulos.
Duplikaatit voidaan poistaa lis¨
a¨
am¨
all¨
a SELECT-osaan m¨
a¨
arittely DISTINCT.
Siten seuraava kysely tuottaa joukon {<2>, <1>}:
SELECT DISTINCT ToimittajaId
FROM Tuote;
Tulossarakkeita voidaan j¨
arjest¨
a¨
a lis¨
a¨
am¨
all¨
a kyselyn loppuun ORDER
BY -osa, jossa voidaan luetella attribuutit, joiden perusteella tulostaulun
rivit j¨
arjestet¨
aa
¨n joko nousevaan (ASCENDING) tai laskevaan (DESCENDING)
j¨
arjestykseen. Jotta t¨
am¨
a voitaisiin tehd¨
a, on j¨
arjest¨
amiseen k¨
aytett¨
avien
attribuuttien tietenkin sis¨
allytt¨
av¨
a SELECT-osaan. Seuraava kysely valitsee yli
kymmenen euroa maksavien tuotteiden nimet ja j¨
arjest¨
a¨
a tulostaulun rivit
nousevaan aakkosj¨
arjestykseen attribuutin Nimi perusteella:
SELECT Nimi
FROM Tuote
WHERE Hinta>10
ORDER BY Nimi ASCENDING;
Ellei ORDER BY-osassa m¨
aa
a nousevaa tai laskevaa j¨
arjestyst¨
a, k¨
aytet¨
aa
¨ritell¨
¨n
nousevaa j¨
arjestyst¨
a. Tulostaulun sarakkeita taas voidaan nimet¨
a uudelleen
k¨
aytt¨
aen AS-operaatiota. Niinp¨
a voisimme kirjoittaa esimerkiksi seuraavan
kyselyn, joka tuottaa tuloksenaan kuvassa 5.4 esitetyn relaation:
SELECT Sukunimi AS Suku, Etunimi AS Etu
FROM Asiakas
ORDER BY Suku, Etu;
5.3.2
Useaan tauluun kohdistuvat kyselyt
Relaatioalgebraa k¨
asitelt¨
aess¨
a mainittiin, ett¨
a jokaisen relaatioalgebran
operaation tulos on relaatio. Koska t¨
am¨
a p¨
atee my¨
os karteesiseen tuloon,
puoliliitoksiin ja liitoksiin, voimme m¨
aa
a FROM-osassa mink¨
a tahansa
¨ritell¨
relaation k¨
aytt¨
aen erilaisia liitoksia taulujen v¨
alill¨
a6 . Periaatteessa voimme
ajatella tietokannanhallintaj¨
arjestelm¨
an evaluoivan useaan tauluun kohdistuvia
kyselyit¨
a seuraavasti:
1. Tietokannanhallintaj¨arjestelm¨
a muodostaa FROM-osassa m¨
a¨
aritellyn
taulun. Jos liitosta ei eksplisiittisesti mainita, voimme ajatella, ett¨
a
FROM-osassa luetellut taulut yhdistet¨
a¨
an toisiinsa karteesisen tulon avulla.
6 Itse asiassa voimme kirjoittaa FROM-osaan vaikkapa kokonaisia SQL-kyselyit¨
a, mutta t¨
am¨
a
ei yleens¨
a ole tarpeen.
40
Tuote.Id
Tuote.Nimi
...
ToimittajaId
Toimittaja.Id
Toimittaja.Nimi
1
1
2
2
3
3
4
4
Hilavitkutin
Hilavitkutin
Vimpain
Vimpain
Masiina
Masiina
Jiirisaha
Jiirisaha
...
...
...
...
...
...
...
...
2
2
1
1
1
1
2
2
1
2
1
2
1
2
1
2
Jaskan vimpain
Orimattilan kone
Jaskan vimpain
Orimattilan kone
Jaskan vimpain
Orimattilan kone
Jaskan vimpain
Orimattilan kone
Tuote.Id
Tuote.Nimi
...
ToimittajaId
Toimittaja.Id
Toimittaja.Nimi
2
3
Vimpain
Masiina
...
...
1
1
1
1
Jaskan vimpain
Jaskan vimpain
Tuote.Nimi
Vimpain
Masiina
Kuva 5.5: Esimerkki SQL-kyselyn toiminnasta.
2. Tietokannahallintaj¨
arjestelm¨
a valitsee edellisen vaiheen tulostaulusta
WHERE-osassa m¨
a¨
aritellyt rivit.
3. T¨
am¨
an j¨
alkeen valitaan edellisen vaiheen tulostaulusta SELECT-osassa
m¨
a¨
aritellyt sarakkeet.
Edell¨
a esitetyn perusteella seuraava kysely tuottaa tuloksenaan relaatioiden
Tuote ja Toimittaja karteesisen tulon:
SELECT *
FROM Tuote, Toimittaja;
Jos FROM-osassa luetelluissa tauluissa on useita samannimisi¨
a attribuutteja,
voidaan attribuuttien nimi¨
a tarkentaa taulujen nimill¨
a. Seuraava kysely valitsee
Jaskan vimpaimen toimittamien tuotteiden nimet; kyselyn toimintaa on
havainnollistettu kuvassa 5.5:
SELECT DISTINCT Tuote.Nimi
FROM Tuote, Toimittaja
WHERE Toimittaja.Nimi=’Jaskan vimpain’
AND Toimittaja.Id=ToimittajaId;
Kuvassa 5.5 on ylinp¨
an¨a esitetty FROM-osassa m¨
a¨
aritelty relaatio eli t¨
ass¨
a
tapauksessa taulujen Tuote ja Toimittaja karteesinen tulo, josta sitten on valittu WHERE-osassa m¨
aa
¨ritellyt rivit eli ne rivit, joissa Toimittaja.Nimi=’Jaskan
vimpain’ ja Toimittaja.Id=ToimittajaId. Lopuksi on valittu tietenkin
SELECT-osassa m¨
a¨
aritellyt sarakkeet ja p¨
a¨
adytty kuvassa alinna esitetyyn tulostauluun.
Toisinaan attribuuttien nimien tarkentaminen edell¨
a esitetyll¨
a tavalla ei
kuitenkaan riit¨
a. T¨
all¨
oin FROM-osassa esitellyt taulut voidaan nimet¨
a uudelleen.
N¨
ain on tehty esimerkiksi seuraavassa kyselyss¨
a, joka valitsee kaikkien masiinaa
halvempien tuotteiden nimet ja hinnat:
41
t1.Id
t1.Nimi
...
t1.Hinta
...
t2.Id
t2.Nimi
...
t2.Hinta
...
1
1
1
1
2
2
2
2
3
3
3
3
4
4
4
4
Hilavitkutin
Hilavitkutin
Hilavitkutin
Hilavitkutin
Vimpain
Vimpain
Vimpain
Vimpain
Masiina
Masiina
Masiina
Masiina
Jiirisaha
Jiirisaha
Jiirisaha
Jiirisaha
...
...
...
...
...
...
...
...
...
...
...
...
...
...
...
...
10.99
10.99
10.99
10.99
99.99
99.99
99.99
99.99
12.50
12.50
12.50
12.50
0.99
0.99
0.99
0.99
...
...
...
...
...
...
...
...
...
...
...
...
...
...
...
...
1
2
3
4
1
2
3
4
1
2
3
4
1
2
3
4
Hilavitkutin
Vimpain
Masiina
Jiirisaha
Hilavitkutin
Vimpain
Masiina
Jiirisaha
Hilavitkutin
Vimpain
Masiina
Jiirisaha
Hilavitkutin
Vimpain
Masiina
Jiirisaha
...
...
...
...
...
...
...
...
...
...
...
...
...
...
...
...
10.99
99.99
12.50
0.99
10.99
99.99
12.50
0.99
10.99
99.99
12.50
0.99
10.99
99.99
12.50
0.99
...
...
...
...
...
...
...
...
...
...
...
...
...
...
...
...
t1.Id
t1.Nimi
...
t1.Hinta
...
t2.Id
t2.Nimi
...
t2.Hinta
...
3
3
Masiina
Masiina
...
...
12.50
12.50
...
...
1
4
Hilavitkutin
Jiirisaha
...
...
10.99
0.99
...
...
t2.Nimi
t2.Hinta
Hilavitkutin
Jiirisaha
10.99
0.99
Kuva 5.6: Esimerkki SQL-kyselyn toiminnasta.
SELECT DISTINCT t2.Nimi, t2.Hinta
FROM Tuote AS t1, Tuote AS t2
WHERE t1.Nimi=’Masiina’
AND t2.Hinta<t1.Hinta;
Kyselyn toimintaa on havainnollisettu kuvassa 5.6, jossa ylimp¨
an¨
a on
esitetty FROM-osassa m¨
a¨
aritelty taulu uudelleennime¨
amisineen, keskell¨
a taulu,
johon on valittu WHERE-osassa m¨
a¨
aritellyt rivit, ja alimpana kyselyn tulostaulu.
Samat vaiheet voidaan erottaa my¨
os seuraavassa kyselyss¨
a, joka valitsee jotakin
tilanneiden asiakkaiden etu- ja sukunimen sek¨
a asiakkaiden tilaamien tuotteiden
nimet:
SELECT DISTINCT Asiakas.Etunimi, Asiakas.Sukunimi,
Tuote.Nimi
FROM Asiakas, Tilaus, Tuote
WHERE Tilaus.AsiakasId=Asiakas.Id
AND Tuote.Id=Tilaus.TuoteId;
Kuten edell¨
a mainittiin, voidaan FROM-osassa k¨
aytt¨
a¨
a pelk¨
an taulujen
luettelemisen lis¨
aksi my¨
os esimerkiksi puoliliitoksia. Vasenta puoliliitosta vastaa
LEFT OUTER JOIN, oikeaa RIGHT OUTER JOIN ja t¨
aytt¨
a FULL OUTER JOIN. N¨
ain
voimme esimerkiksi seuraavan, kuvassa 5.7 havainnollistetun kyselyn avulla
valita niiden asiakkaiden nimet, jotka eiv¨
at ole tilanneet mit¨
aa
¨n:
SELECT DISTINCT Etunimi, Sukunimi
FROM Asiakas LEFT OUTER JOIN Tilaus ON Id=AsiakasId
WHERE AsiakasId IS NULL;
42
Id
Etunimi
Sukunimi
AsiakasId
TuoteId
1
2
2
3
Orimatti
Laupias
Laupias
Hanhikki
Teuras
Psaltari
Psaltari
Hillosilm¨
a
1
2
2
NULL
1
2
3
NULL
Id
Etunimi
Sukunimi
AsiakasId
TuoteId
3
Hanhikki
Hillosilm¨
a
NULL
NULL
Etunimi
Sukunimi
Hanhikki
Hillosilm¨
a
Kuva 5.7: Esimerkki SQL-kyselyn toiminnasta.
On syyt¨
a huomata, ett¨
a edellisess¨
a kyselyss¨
a AsiakasId:t¨
a vertaillaan
IS NULL -vertailulla. Jos vertailussa olisi k¨
aytetty yht¨
asuuruutta = olisi
kysely tuottanut tyhj¨
an tuloksen, mik¨
a seuraa suoraan aiemmin esitellyst¨
a
kolmiarvologiikasta.
SQL:¨
a¨
a kirjoittaessaan kannattaa aina pit¨
a¨
a mieless¨
a relaatioalgebran
operaatiot eli rivien valinta, sarakkeiden valinta, uudelleennime¨
aminen ja
erilaiset liitokset. Algebran operaatioiden ja SQL:n yhteyden ymm¨
art¨
aminen
nimitt¨
ain helpottaa kyselyiden kirjoittamista huomattavasti. Ajattelemalla
SQL-kyselyit¨
a rivien ja sarakkeiden valinnan, uudelleennime¨
amisen sek¨
a
karteesisen tulon yhdistelm¨an¨
a p¨
a¨
asee jo sangen pitk¨
alle.
5.3.3
Aggregaattifunktiot
Aggregaatti- eli koostefunktioiden avulla tietokannasta voidaan hakea
esimerkiksi keskiarvoja, maksimeita, minimej¨
a sek¨
a rivien lukum¨
a¨
ari¨
a.
Aggregaattifunktiot kirjoitetaan tuostauluun valittavien attribuuttien tapaan
kyselyn SELECT-osaan jotakin seuraavista k¨
aytt¨
aen:
1. SUM(Attribuutti), joka laskee sarakkeen Attribuutti arvojen summan.
Vaatimuksena on tietysti se, ett¨
a attribuutin arvojoukko koostuu luvuista.
2. AVERAGE(Attribuutti), joka palauttaa sarakkeen keskiarvon. Attribuuttisarakkeen on j¨
alleen sis¨
allett¨
av¨
a vain lukuja.
3. MAX(Attribuutti), joka palauttaa sarakkeen suurimman arvon. Attribuutin Attribuutti arvojen on oltava vertailtavissa.
4. MIN(Attribuutti), joka palauttaa sarakkeen pienimm¨
an arvon. Attribuutin
Attribuutti arvojen on j¨
alleen oltava vertailtavissa.
5. COUNT(Attribuutti), joka laskee attribuutin Attribuutti arvojen m¨
a¨
ar¨
an.
On syyt¨
a korostaa, ett¨
a arvojen ei tarvitse olla erilaisia. Muista
aggregaattifunktioista poiketen attribuutin ei tarvitse saada arvojaan
lukujoukosta.
Yksinkertaisimmassa muodossaan aggregaattifunktiot kohdistuvat kokonaiseen relaatioon; t¨
all¨
oin tulosrelaatiossa on vain yksi rivi. Koska funktio COUNT
43
k¨
ayt¨
ann¨
oss¨
a laskee relaation rivit, voidaan funktion sy¨
otteen¨
a muista aggregaattifunktioista poiketen k¨
aytt¨a¨
a my¨
os jokerimerkki¨
a *. Voimme esimerkiksi hakea
tilausten m¨
a¨
ar¨
an seuraavasti:
SELECT COUNT(*)
FROM Tilaus;
Kannattaa huomata, ett¨
a edellisen kyselyn tulos on yhden rivin ja
yhden sarakkeen relaatio eik¨
a yksitt¨
ainen luku. Jos haluamme laskea niiden
asiakkaiden lukum¨
a¨
ar¨
an, jotka ovat tilanneet jotakin, meid¨
an on muistettava,
ett¨
a COUNT ei laske erilaisten arvojen m¨
aa
aa
a voimme k¨
aytt¨
aa
¨r¨
¨. Niinp¨
¨ seuraavaa
kysely¨
a selvitt¨
a¨
aksemme moniko asiakas on tilannut jotakin:
SELECT COUNT(DISTINCT AsiakasId)
FROM Tilaus;
Seuraava kysely taas valitsee tuotteiden minimi- ja maksimihinnat:
SELECT MIN(Hinta), MAX(Hinta)
FROM Tuote;
Toisinaan on kuitenkin tarpeen kohdistaa aggregaattifunktio kokonaista
taulua pienempiin kokonaisuuksiin. T¨
all¨
oin k¨
aytet¨
a¨
an ns. ryhmittelyattribuutteja, jotka luetellaan kyselyn loppuun kirjoitettavassa GROUP BY osassa; ryhmittelyattibuuttien on esiinnytt¨
av¨
a my¨
os SELECT-osassa. Tietokannanhallintaj¨
arjestelm¨
an voi ajatella evaluoivan aggregaattifunktioita sis¨
alt¨
avi¨
a
kyselyit¨
a seuraavasti:
1. Tietokannanhallintaj¨
arjestelm¨
a muodostaa FROM-osassa m¨
a¨
aritellyn relaation.
2. Edellisen vaiheen tulostaulusta valitaan WHERE-osassa m¨
a¨
aritellyt rivit.
3. T¨
am¨
an j¨
alkeen tietokannanhallintaj¨
arjestelm¨
a etsii edellisen vaiheen
tulostaulusta ryhmittelyattribuuttien erilaiset arvoyhdelm¨
at ja laskee
jokaiselle yhdelm¨
alle aggregaattifunktioiden arvot.
4. Lopuksi valitaan SELECT-osassa m¨
a¨
aritellyt sarakkeet. SELECT-osassa
voidaan valita ainoastaan aggregaattifunktioita vastaavia sarakkeita tai
ryhmittelyattribuutteja.
Esimerkkin¨
a t¨
ast¨
a voimme tarkastella seuraavaa kysely¨
a, joka valitsee
kaikkien toimittajan nimen sek¨
a toimittajan toimittamien tuotteiden minimija maksimihinnat:
SELECT DISTINCT Toimittaja.Nimi, MIN(Hinta), MAX(Hinta)
FROM Tuote, Toimittaja
WHERE Tuote.ToimittajaId=Toimittaja.Id
GROUP BY Toimittaja.Nimi;
Edellisen kyselyn toimintaa on havainnollistettu kuvassa 5.8. Ensimm¨
aisess¨
a
vaiheessa siis valitaan ylinn¨
a esitetyst¨
a taulujen Tuote ja Toimittaja
karteesisesta tulosta WHERE-osassa m¨
a¨
aritellyt rivit, eli ne rivit, joissa
44
Tuote.Id
Tuote.Nimi
...
Hinta
ToimittajaId
Toimittaja.Id
Toimittaja.Nimi
1
1
2
2
3
3
4
4
Hilavitkutin
Hilavitkutin
Vimpain
Vimpain
Masiina
Masiina
Jiirisaha
Jiirisaha
...
...
...
...
...
...
...
...
10.99
10.99
99.99
99.99
12.50
12.50
9.99
9.99
2
2
1
1
1
1
2
2
1
2
1
2
1
2
1
2
Jaskan vimpain
Orimattilan kone
Jaskan vimpain
Orimattilan kone
Jaskan vimpain
Orimattilan kone
Jaskan vimpain
Orimattilan kone
Tuote.Id
Tuote.Nimi
...
Hinta
ToimittajaId
Toimittaja.Id
Toimittaja.Nimi
1
2
3
4
Hilavitkutin
Vimpain
Masiina
Jiirisaha
...
...
...
...
10.99
99.99
12.50
9.99
2
1
1
2
2
1
1
2
Orimattilan kone
Jaskan vimpain
Jaskan vimpain
Orimattilan kone
Tuote.Nimi
MIN(Hinta)
MAX(Hinta)
Orimattilan kone
Jaskan vimpain
0.99
12.50
10.99
99.99
Kuva 5.8: Esimerkki aggregaattifunktioiden k¨
ayt¨
ost¨
a.
attribuuttien ToimittajaId ja Toimittaja.Id arvot ovat samat. T¨
am¨
an
vaiheen tuloksena on keskell¨
a esitetty taulu, josta haetaan ryhmittelyattribuutin
Toimittaja.Nimi erilaiset arvot eli ”Orimattilan kone” ja ”Jaskan vimpain”.
Koska ryhmittelyattribuutilla on kaksi erilaista arvoa, tied¨
amme nyt,
ett¨
a tulostaulussa on kaksi rivi¨
a, yksi Orimattilan koneelle ja toinen
Jaskan vimpaimelle. T¨
am¨an j¨
alkeen lasketaan aggregaattifunktioiden arvot
kummallekin ryhmittelyattribuutin arvolle. Esimerkiksi Orimattilan konetta
vastaavalle tulostaulun riville attribuutin MIN(Hinta) valitaan tietysti
attribuutin Hinta pienin arvo sellaiselta keskell¨
a esitetyn taulun rivilt¨
a, jolla
Toimittaja.Nimi=’Orimattilan kone’. N¨
ain p¨
a¨
adyt¨
a¨
an alimpana esitettyyn
tulostauluun. Yht¨
a hyvin olisimme voineet vaikkapa maksimihinnan sijasta etsi¨
a
vaikkapa keskihintaa. T¨
all¨oin Orimattilan koneelle olisi laskettu toimitettujen
tuotteiden keskihinta laskemalla attribuutin Hinta keskiarvo sellaisila riveilt¨
a,
joilla Toimittaja.Nimi=’Orimattilan kone’.
On syyt¨
a viel¨
a uudelleen korostaa, ett¨
a aggregaattifunktioita sis¨
alt¨
av¨
an
kyselyn SELECT-osan attribuuttilista voi sis¨
alt¨
a¨
a vain attribuutteja, joihin
kohdistetaan jokin aggregaattifunktio tai ryhmittelyattribuutteja. Tulostaulussa
ei standardin mukaan voi esiinty¨
a muita sarakkeita. Kannattaa my¨
os huomata,
ett¨
a aggregaattifunktioita ei voi k¨
aytt¨
a¨
a kyselyn WHERE-osassa, joten jos
aggregaattifunktioiden tuloksille halutaan asettaa ehtoja, on kyselyyn viel¨
a
kirjoitettava HAVING-osa, johon ehdot sitten lis¨
at¨
a¨
an. Seuraava kysely valitsee
toimittajien nimet sek¨
a toimittajien toimittamien tuotteiden maksimihinnan
edellytt¨
aen, ett¨
a kallein toimittajan toimittama tuote maksaa v¨
ahemm¨
an
kuin 50 euroa, ja j¨
arjest¨
a¨
a tuloksen toimittajan nimen mukaiseen k¨
a¨
anteiseen
aakkosj¨
arjestykseen:
SELECT DISTINCT Toimittaja.Nimi, MAX(Hinta)
FROM Tuote, Toimittaja
WHERE ToimittajaId=Toimittaja.Id
GROUP BY Toimittaja.Nimi
45
HAVING MAX(Hinta)<50
ORDER BY Toimittaja.Nimi DESCENDING;
Aggregaattifunktioita k¨
aytt¨
avien kyselyiden tulostaulujen rivej¨
a voi siis
j¨
arjest¨
a¨
a kuten miss¨
a tahansa SQL-kyselyn tuloksen rivej¨
a. Lis¨
aksi attribuutteja
ja tauluja voidaan nimet¨
a uudelleen.
5.3.4
Alikyselyt
SQL sallii my¨
os ns. alikyselyt eli sis¨
akyselyt eli kyselyt, jotka on
kirjoitettu toisten kyselyiden sis¨
a¨
an. Kysely¨
a, joka sis¨
alt¨
a¨
a alikyselyn, kutsutaan
ulkokyselyksi. Jos sis¨
akysely palauttaa tuloksenaan yhden rivin ja yhden
sarakkeen relaation, voidaan jotakin ulkokyselyn attribuuttia arvoa verrata
sis¨
akyselyn tulokseen operaatioita =, 6=, <, ≤, > ja ≥ k¨
aytt¨
aen. N¨
ain voimme
kirjoittaa esimerkiksi seuraavan kyselyn, joka hakee kaikkien Jaskan vimpaimen
toimittamien, viitt¨
akymment¨
a euroa kalliimpien tuotteiden nimet:
SELECT DISTINCT Nimi
FROM Tuote
WHERE Hinta>50
AND ToimittajaId=(
SELECT Id
FROM Toimittaja
WHERE Nimi=’Jaskan vimpain’);
Edellisess¨
a kyselyss¨
a sis¨
akysely siis palauttaa yhden rivin relaation, jonka
ainoan attribuutin arvo on 1. Ulkokyselyn attribuutin ToimittajaId arvoa siis
verrataan t¨
ah¨
an arvoon. Alikyselyt ovat yleens¨
a pahasta, sill¨
a ne vaikeuttavat
tietokannanhallintaj¨
arjestelm¨
an kyselyjen optimoijan toimintaa7 ja saattavat
n¨
ain johtaa tehottomiin kyselyihin. Seuraava, t¨
aysin edellist¨
a kysely¨
a vastaava
kysely on lis¨
aksi huomattavasti selke¨
ampi:
SELECT DISTINCT Tuote.Nimi
FROM Tuote, Toimittaja
WHERE Hinta>50
AND ToimittajaId=Toimittaja.Id
AND Toimittaja.Nimi=’Jaskan vimpain’;
SQL sallii my¨
os sellaisten alikyselyiden k¨
ayt¨
on, jotka palauttavat useampia
rivej¨
a mutta vain yhden sarakkeen. T¨
all¨
oin ulkokyselyn jonkin attribuutin
arvoa voidaan vertailla sis¨akyselyn tulosjoukkoon. Tarkemmin sanoen kahden
attribuutin arvon tai attribuutin arvon ja vakion vertailemisen sijasta voidaan
kirjoittaa Attribuutti Operaatio (Alikysely), miss¨
a Attribuutti on attribuutti,
jonka arvoa vertaillaan, Alikysely yhden sarakkeen relaation palauttava SQLkysely ja Operaatio jokin seuraavista:
1. IN, joka vastaa normaalia joukkoon sis¨
altymist¨
a. Vastaavasti voidaan
kirjoittaa NOT IN, joka tietysti vaatii, ett¨
a attribuutin arvo ei sis¨
ally
alikyselyn tulosjoukkoon. IN-operaation sis¨
alt¨
av¨
at kyselyt voidaan yleens¨
a
kirjoittaa uudelleen yksinkertaisempia operaatioita k¨
aytt¨
aen, mutta NOT
IN on syyt¨
a painaa mieleen, sill¨
a sit¨
a saattaa joskus tarvita.
7 Kyselyjen
optimointia k¨
asitell¨
a¨
an tarkemmin Tietokantojen jatkokurssilla.
46
2. Vertailuoperaatio =, 6=, <, ≤, > ja ≥ yhdistettyn¨
a sanaan ALL. Esimerkiksi
tapauksessa < ALL valitaan vain ne ulkokyselyn rivit, joissa attribuutin
Attribuutti arvo on jokaista sis¨
akyselyn tulosjoukon arvoa pienempi.
Vertailu = ALL ei tietysti ole kovinkaan mielek¨
as, jos sis¨
akyselyn tuloksessa
on useita rivej¨
a, mutta standardi sen kuitenkin sallii.
3. Vertailuoperaatio =, 6=, <, ≤, > ja ≥ yhdistettyn¨
a sanaan ANY. Esimerkiksi
tapauksessa < ALL valitaan vain ne ulkokyselyn rivit, joissa attribuutin
Attribuutti arvo on jotakin sis¨
akyselyn tulosjoukon arvoa pienempi.
Joissakin tietokannanhallintaj¨
arjestelmiss¨
a k¨
aytet¨
a¨
an my¨
os sanaa SOME.
K¨
ayt¨
ann¨
oss¨
a t¨
at¨
ak¨
a¨
an operaatiota ei juuri tarvita.
N¨
ait¨
a operaatioita k¨
aytt¨
aen voimme esimerkiksi kirjoittaa seuraavan
kyselyn, joka hakee kaikkia Orimattilan koneen toimittamia koneita kalliimpien
tuotteiden nimet:
SELECT DISTINCT Nimi
FROM Tuote
WHERE Hinta > ALL (
SELECT Hinta
FROM Tuote, Toimittaja
WHERE Toimittaja.Nimi=’Orimattilan kone’
AND ToimittajaId=Toimittaja.Id);
Edellisten lis¨
aksi voidaan k¨
aytt¨
a¨
a olemassaolotesti¨
a EXISTS tai olemassaolemattomuustesti¨
a NOT EXISTS, joita ei edellisist¨
a operaatiosta poiketen verrata ulkokyselyn attribuutin arvoon ja joita seuraava sis¨
akysely voi sis¨
alt¨
a¨
a useita sarakkeita ja useita rivej¨
a. Voimme esimerkiksi etsi¨
a jotakin tilanneiden asiakkaiden nimet seuraavasti:
SELECT DISTINCT Etunimi, Sukunimi
FROM Asiakas
WHERE EXISTS (SELECT * FROM Tilaus WHERE Id=AsiakasId);
EXISTS-operaatio ei ole kovin keskeinen, sill¨
a se voidaan k¨
ayt¨
ann¨
oss¨
a aina
korvata yksinkertaisemmilla operaatioilla. Esimerkiksi edellinen kysely voidaan
helposti kirjoittaa uudelleen huomattavasti sievemm¨
ass¨
a muodossa:
SELECT DISTINCT Etunimi, Sukunimi
FROM Asiakas, Tilaus
WHERE Id=AsiakasId;
Kuten edell¨
a mainittiin, on alikyselyiden k¨
aytt¨
o usein pahasta. NOT IN
kannattaa kuitenkin pit¨
a¨
a mieless¨
a, koska sill¨
a on toisinaan k¨
aytt¨
o¨
a. T¨
at¨
a
havainnollistaaksemme voimme tarkastella seuraavaa kysely¨
a, jolla yritet¨
a¨
an
etsi¨
a asiakkaat, jotka eiv¨
at ole tilanneet mit¨
a¨
an:
SELECT DISTINCT Etunimi, Sukunimi
FROM Asiakas, Tilaus
WHERE NOT Id=AsiakasId;
47
Id
Etunimi
Sukunimi
AsiakasId
TuoteId
1
1
1
2
2
2
3
3
3
Orimatti
Orimatti
Orimatti
Laupias
Laupias
Laupias
Hanhikki
Hanhikki
Hanhikki
Teuras
Teuras
Teuras
Psaltari
Psaltari
Psaltari
Hillosilm¨
a
Hillosilm¨
a
Hillosilm¨
a
1
2
2
1
2
2
1
2
2
1
2
3
1
2
3
1
2
3
Id
Etunimi
Sukunimi
AsiakasId
TuoteId
1
1
2
3
3
3
Orimatti
Orimatti
Laupias
Hanhikki
Hanhikki
Hanhikki
Teuras
Teuras
Psaltari
Hillosilm¨
a
Hillosilm¨
a
Hillosilm¨
a
2
2
1
1
2
2
2
3
1
1
2
3
Etunimi
Sukunimi
Orimatti
Laupias
Hanhikki
Teuras
Psaltari
Hillosilm¨
a
Kuva 5.9: Esimerkki v¨
a¨
arin kirjoitetun SQL-kyselyn toiminnasta. Ei n¨
ain.
Kyselyn tuottama tulos on kuitenkin v¨
a¨
ar¨
a, sill¨
a tulokseen valitaan taulujen
Asiakas ja Tilaus karteesisesta tulosta ne rivit, joissa attribuuteilla Id ja
AsiakasId on eri arvot. T¨
am¨
an j¨
alkeen valitaan SELECT-osassa m¨
aa
¨ritellyt
attribuutit duplikaatit poistaen. Kyselyn toimintaa on havainnollistettu kuvassa
5.9. Ellei kysely¨
a halua kirjoittaa esimerkiksi puoliliitosta k¨
aytt¨
aen, on k¨
atev¨
a¨
a
k¨
aytt¨
a¨
a alikysely¨
a ja NOT IN -operaatiota seuraavasti:
SELECT DISTINCT Etunimi, Sukunimi
FROM Asiakas
WHERE Id NOT IN (SELECT AsiakasId FROM Tilaus);
Kannattaa kuitenkin huomata, ett¨
a NOT IN -vertailu joukkoon, joka sis¨
alt¨
a¨
a
arvon NULL, palauttaa aina tuloksenaan tyhj¨
an joukon8 . Niinp¨
a NOT IN vertailua k¨
aytett¨
aess¨
a on syyt¨
a varmistua siit¨
a, ett¨
a alikyselyn tulos ei sis¨
all¨
a
NULL-arvoa.
Voimme ajatella tietokannanhallintaj¨
arjestelm¨
an evaluoivan alikyselyit¨
a
sis¨
alt¨
av¨
at kyselyt siten, ett¨
a se tuttuun tapaan ensin muodostaa ulkokyselyn
FROM-osassa m¨
a¨
aritellyn relaation, josta sitten valitaan ne ulkokyselyn WHEREosassa m¨
a¨
aritellyt rivit, jotka valittaisiin ilman alikyselyit¨
a. T¨
am¨
an j¨
alkeen
jokaista edellisess¨
a vaiheessa valittua rivi¨
a kohden evaluoidaan alikyselyt
ja vertaillaan attribuuttien arvoja sis¨
akyselyn tuloksiin; EXISTS-tapauksessa
mukaan otetaan tietysti vain ne ulkokyselyn rivit, joille alikysely ei tuota tyhj¨
a¨
a
tulosta. Lopuksi valitaan tulokseen ulkokyselyn SELECT-osassa m¨
a¨
aritellyt
8 Lopputulos on t¨
aysin kolmiarvologiikan mukainen, vaikka se ei kovin intuitiiviselta
tuntuisikaan.
48
sarakkeet. T¨
am¨
a evaluointij¨
arjestys on voimassa riippumatta siit¨
a, k¨
aytet¨
aa
o
¨nk¨
alikyselyss¨
a ulkokyselyn FROM-osassa esiteltyjen taulujen attribuutteja vai ei.
5.3.5
Joukko-opilliset operaatiot
Edell¨
a esiteltyjen ominaisuuksien lis¨
aksi SQL sis¨
alt¨
a¨
a my¨
os joukko-opillisia
unionia, leikkausta ja erotusta vastaavat operaatiot UNION, INTERSECT
ja EXCEPT. Kyselyiden Kysely1 ja Kysely2 tulosrelaatiot voidaan n¨
aiden
operaatioiden avulla liitt¨
a¨
a toisiinsa kirjoittamalla Kysely1 Operaatio Kysely2
edellytt¨
aen, ett¨
a Kysely1 :n ja Kysely2 :n tulosrelaatiot ovat yhteensopivat, eli
molemmilla relaatioilla on sama m¨
a¨
ar¨
a attribuutteja ja attribuuttien arvojoukot
ovat samat. Edell¨
a Operaatio on tietenkin joko UNION, INTERSECT tai EXCEPT.
K¨
aytt¨
aen erotusta voimme seuraavan kyselyn avulla hakea ne asiakkaat, jotka
eiv¨
at ole tilanneet mit¨
a¨
an:
SELECT Etunimi, Sukunimi
FROM Asiakas
EXCEPT
SELECT Etunimi, Sukunimi
FROM Asiakas, Tilaus
WHERE Id=AsiakasId;
Edellisess¨
a kyselyss¨
a ensimm¨
ainen osa tuottaa joukon {<Orimatti,
Teuras>,<Laupias,Psaltari>,<Hanhikki,Hillosilm¨
a>}, josta sitten poistetaan toisen kyselyn tulosjoukko {<Orimatti,Teuras>, <Laupias,Psaltari>}
ja saadaan tulokseksi {<Hanhikki,Hillosilm¨
a>}. Operaatiot vastaavat joukkoopillisia operaatioita my¨
os siin¨
a, ett¨
a niiden tulokset eiv¨
at muista SQL:n operaatioista poiketen sis¨
all¨
a duplikaatteja. Jos duplikaatit jostain syyst¨
a halutaan
mukaan tulokseen, voidaan k¨
aytt¨
a¨
a operaatioita UNION ALL, INTERSECT ALL,
EXCEPT ALL.
Kannattaa huomata, ett¨
a operaatio INTERSECT evaluoidaan ennen
operaatioita UNION ja EXCEPT. Operaatioilla UNION ja EXCEPT ei kuitenkaan ole
keskin¨
aist¨
a j¨
arjestyst¨
a, vaan ne evaluoidaan normaalisti vasemmalta oikealle.
Mill¨
a tahansa kyselyill¨
a Kysely1 , Kysely2 ja Kysely3 lauseke Kysely1 UNION
Kysely2 EXCEPT Kysely3 siis evaluoidaan kuten (Kysely1 UNION Kysely2 )
EXCEPT Kysely3 . Kysely1 UNION Kysely2 INTERSECT Kysely3 taas evaluoidaan
kuten Kysely1 UNION (Kysely2 INTERSECT Kysely3 ).
5.4
P¨
aivitykset SQL:n avulla
5.4.1
Monikon lis¨
a¨
aminen
Tehokkaiden hakuoperaatioiden lis¨
aksi tietokannanhallintaj¨
arjestelm¨
an on
tarjottava my¨
os mahdollisuus kannassa olevan tiedon muokkaamiseen.
Yksinkertaisin p¨
aivitysoperaatio on monikon lis¨
a¨
aminen relaatioon. Voimme
esimerkiksi lis¨
at¨
a Asiakas-tauluun rivin <4,Assi,Asiakas> seuraavasti:
INSERT INTO Asiakas VALUES (4, ’Assi’, ’Asiakas’);
Kokonaisen monikon lis¨
a¨
amisen sijasta voimme lis¨
at¨
a monikon siten, ett¨
a
m¨
a¨
arittelemme vain osan monikon attribuuttien arvoista; muut sarakkeet
49
alustetaan t¨
all¨
oin oletusarvolla. Olettaen, ett¨
a Asiakas-taulua m¨
aa
aess¨
a
¨ritelt¨
Sukunimi-attribuutille ei ole m¨
a¨
aritelty NULL:ista poikkeavaa oletusarvoa,
seuraava komento lis¨
a¨
a Asiakas-tauluun rivin <5,Antti,NULL>:
INSERT INTO Asiakas (Id, Etunimi) VALUES (5, ’Antti’);
Kannattaa pit¨
a¨
a mieless¨
a, ett¨
a lis¨
aysoperaatio onnistuu vain, jos se ei riko
eheyss¨
aa
oj¨
a - siis entiteettieheytt¨
a ja viite-eheytt¨
a - tai avainrajoitetta eli sit¨
a,
¨nt¨
ett¨
a mill¨
a¨
an taulussa jo esiintyv¨
all¨
a monikolla ei ole samoja avainttribuuttien
arvoja kuin lis¨
att¨
av¨
all¨
a monikolla. Lis¨
aksi arvojen on tietenkin sovittava
attribuuttien arvojoukkoihin. Emme siis voi lis¨
at¨
a esimerkiksi monikkoa, jossa
jonkin kokonaislukuattribuutin arvo on ’xyz’.
My¨
os kyselytulosten k¨
aytt¨
aminen p¨
aivitysoperaatioissa on mahdollista.
Niinp¨
a voimme lis¨
at¨
a tauluun kyselyn tulosjoukon seuraavasti:
INSERT INTO T aulu (Attribuutti1 , ..., Attribuuttin ) Kysely;
Edellisess¨
a operaatiossa kyselyn Kysely tulosrelaation attribuuttien on tietenkin sovittava taulun T aulu attribuutteihin Attribuutti1 , ..., Attribuuttin .
T¨
allaisten INSERT-komentojen k¨
aytt¨
aminen mahdollistaa v¨
alitulosten tallentamisen. V¨
alitulosten k¨
aytt¨
aminen on suotavaa silloin, kun kysely on
kovin monimutkainen. Lopuksi kannattaa huomata, ett¨
a monissa tietokannanhallintaj¨
arjestelmiss¨
a dataa voidaan lis¨
at¨
a tauluihin suoraan tekstitiedostoista.
Esimerkiksi PostgreSQL tarjoaa t¨
ah¨
an tarkoitukseen COPY-komennon.
5.4.2
Monikon poistaminen
Monikon poistaminen on monikon lis¨
a¨
amisen tapaan hyvin yksinkertaista.
T¨
ah¨
an tarkoitukseen SQL tarjoaa DELETE-komennon. Voimme esimerkiksi
tuhota ne Asiakas-taulun rivit, joissa attribuutin Id arvo on suurempi kuin
3 seuraavasti:
DELETE FROM Asiakas WHERE Id>3;
Kuten SQL-kielen kyselyiss¨
akin, WHERE-osa m¨
a¨
arittelee ne rivit, joihin
operaatio kohdistuu. WHERE-osan ehto voi olla hyvinkin monimutkainen ja
sis¨
alt¨
aa
a rivin tuhoaminen saattaa
¨ vaikkapa alikyselyit¨a. Kannattaa muistaa, ett¨
viiteavainten m¨
a¨
arittelyist¨
a riippuen aiheuttaa muidenkin rivien tuhoamisen.
Jos kohdistamme edell¨
a esitetyn operaation kantaan, jossa olemme m¨
a¨
aritelleet
Tilaus-taulun viiteavaimen AsiakasId seuraavasti:
FOREIGN KEY AsiakasId REFERENCES Asiakas
ON UPDATE CASCADE ON DELETE CASCADE
tuhoaa tietokannanhallintaj¨
arjestelm¨
a automaattisesti my¨
os ne Tilaus-taulun
rivit, joissa AsiakasId>3. Jos taas olisimme m¨
a¨
aritelleet
FOREIGN KEY AsiakasId REFERENCES Asiakas
ON UPDATE CASCADE ON DELETE RESTRICT
ja taulussa Tilaus olisi ollut rivej¨
a, joilla AsiakasId>3, olisi tietokannanhallintaj¨
arjestelm¨
a j¨
att¨
anyt operaation suorittamatta ja palauttanut virheilmoituksen.
50
5.4.3
Monikon muuttaminen
Monikon sis¨
alt¨
o¨
a voidaan muuttaa UPDATE-komennolla. UPDATE-komennossa
voidaan DELETE-komennon tapaan k¨
aytt¨
a¨
a WHERE-osaa, joka m¨
a¨
arittelee, mihin
riveihin p¨
aivitys kohdistuu. Niinp¨
a voimme kirjoittaa esimerkiksi seuraavan
kyselyn:
UPDATE Asiakas
SET Sukunimi=’Teuras’
WHERE Sukunimi=’Hillosilm¨
a’;
Jos WHERE-osaa ei kirjoiteta, kohdistuu p¨
aivitys tietysti kaikkiin taulun
monikkoihin. Seuraava kysely korottaa kaikkien tuotteitten hintoja kymmenell¨
a
prosentilla:
UPDATE Tuote
SET Hinta=Hinta*1.1;
Toisinaan p¨
aivitysoperaatioon tarvitaan tietoja my¨
os muista kuin
p¨
aivitett¨
av¨
ast¨
a taulusta. T¨
all¨
oin muiden taulujen tietoja haetaan alikyselyiden
avulla. N¨
ain on toimittu esimerkiksi seuraavassa kyselyss¨
a:
UPDATE Tuote
SET Hinta=Hinta*1.1
WHERE ToimittajaId IN (
SELECT Id
FROM Toimittaja
WHERE Toimittaja.Nimi=’Jaskan vimpain’);
Standardi ei sit¨
a salli, mutta huomattavasti elegantimpaa olisi ilmaista sama
asia kirjoittamalla
UPDATE Tuote, Toimittaja
SET Hinta=Hinta*1.1
WHERE ToimittajaId=Toimittaja.Id
AND Toimittaja.Nimi=’Jaskan vimpain’;
Lopuksi kannattaa huomata, ett¨
a my¨
os monikon sis¨
all¨
on muuttaminen
saattaa aiheuttaa vy¨
orytyksen. Jos p¨
aivit¨
amme jonkin viittauksen kohteena
olevan p¨
a¨
aavainattribuutin arvoa, p¨
aivitet¨
a¨
an my¨
os viittaavien rivien
viiteavainattribuuttien arvoja. T¨
am¨
a tietenkin edellytt¨
a¨
a, ett¨
a viiteavaimet on
varustettu ON UPDATE CASCADE -m¨
a¨
areell¨
a.
5.5
5.5.1
Transaktiot
ACID
Tietokantaoperaatioiden
yhteydess¨
a
puhutaan
usein
ns.
ACIDominaisuuksista eli siit¨
a, ett¨
a jokainen operaatio on atomaarinen (Atomic),
s¨
ailytt¨
a¨
a tietokannan oikeellisuuden ja eheyden (Consistent), toimii itsen¨
aisesti (Isolated) ja muuttaa tietokannan tilaa pysyv¨
asti (Durable).
51
Tarkemmin sanottuna atomaarisuudella tarkoitetaan sit¨
a, ett¨
a jokainen operaatio suoritetaan joko kokonaan tai j¨
atet¨
a¨
an kokonaan suorittamatta. Jos
operaatio jostain syyst¨
a estyy tai keskeytyy, on tietokannanhallintaj¨
arjestelm¨
an
peruutettava tietokantaan tekem¨
ans¨
a muutokset. Oikeellisuuden ja eheyden
s¨
ailymisell¨
a tarkoitetaan sit¨
a, ett¨
a oikeelliseen ja ehe¨
a¨
an tietokantaan kohdistetun operaation j¨
alkeen tietokannan on edelleen oltava oikeellinen ja ehe¨
a.
Tietokannanhallintaj¨
arjestelm¨
an on huolehdittava t¨
ast¨
a joko est¨
am¨
all¨
a eheyden
rikkovat operaatiot tai eheytt¨
am¨
all¨
a tietokanta p¨
aivitysoperaation j¨
alkeen.
Tietokantaoperaatioiden itsen¨
ainen toiminta taas tarkoittaa sit¨
a, ett¨
a
mik¨
a¨
an operaatio ei saa vaikuttaa toisen, keskener¨
aisen operaation toimintaan.
Tietokantaoperaatio saattaisi esimerkiksi siirt¨
a¨
a 100 euroa tililt¨
a A, jonka
alkusaldo on 500 euroa, tilille B, jonka alkusaldo on 500 euroa. Operaatio
v¨
ahent¨
a¨
a tililt¨
a A ensin 100 euroa ja lis¨
a¨
a sitten tilille B 100 euroa, mink¨
a j¨
alkeen
tilien saldot ovat tietysti 400 ja 600 euroa. Toinen tietokantaoperaatio saattaisi
samaan aikaan tutkia tilien A ja B saldoa. Koska operaatioiden on toimittava
itsen¨
aisesti, j¨
alkimm¨
ainen operaatio havaitsisi tilien A ja B saldoiksi joko 500
ja 500 euroa tai 400 ja 600 euroa. Operaatio ei kuitenkaan voi tutkia tilien
saldoja, kun rahoja siirt¨
av¨
a operaatio on kesken eli tilien saldot ovat 400 ja 500
euroa. Tietokannanhallintaj¨
arjestelm¨
at tarjoavat tavallisesti mahdollisuuden
yhdist¨
a¨
a useita erillisi¨
a p¨
aivityksi¨
a ja kyselyit¨
a ns. transaktioksi, eli yhdeksi
tietokantaoperaatioksi, jolle taataan edell¨
a mainitut ACID-ominaisuudet.
Muutosten pysyvyys taas on varsin ilmeinen ehto. Jos tietokantapalvelimemme esimerkiksi kaatuu p¨
aivitysoperaation j¨
alkeen, on operaation tekemien
muutosten s¨
ailytt¨
av¨
a. Jos palvelin taas kaatuu kesken operaation, on tietokannanhallintaj¨
arjestelm¨
an uudelleen k¨
aynnistett¨
aess¨
a kyett¨
av¨
a peruuttamaan jo
mahdollisesti tekem¨
ans¨
a muutokset. T¨
am¨
a seuraa operaatioiden atomaarisuuden ehdosta.
5.5.2
Transaktioiden tarpeesta
Jokainen yksitt¨
ainen SQL-kysely tai -p¨
aivitys t¨
aytt¨
a¨
a edell¨
a esitetyt ACIDominaisuudet. Toisinaan tietyn p¨
aivityksen tekemiseen tarvitaan kuitenkin
useampia yksitt¨
aisi¨
a kyselyit¨
a. T¨
am¨
a on helppoa ymm¨
art¨
a¨
a tarkastelemalla
vaikkapa seuraavia kyselyit¨a, jotka siirt¨
av¨
at 100 euroa tililt¨
a 123 tilille 321:
UPDATE Tili SET Saldo=Saldo-100 WHERE Id=123;
UPDATE Tili SET Saldo=Saldo+100 WHERE Id=321;
Vaikka kyseess¨
a onkin kaksi erillist¨
a kysely¨
a, halutaan n¨
ait¨
a kyselyit¨
a
kohdella yhten¨
a transaktiona, joka joko suoritetaan kokonaisuudessaan
tai j¨
atet¨
a¨
an kokonaisuudessaan suorittamatta. Transaktioiden yhteydess¨
a
puhutaan t¨
all¨
oin transaktion vahvistamisesta eli transaktion aiheuttamien
muutosten tekemisest¨
a pysyviksi tai peruuttamisesta eli tietokannan
palauttamisesta transaktiota edelt¨
aneeseen tilaan. Esimerkiksi edellisess¨
a
tapauksessa emme siis voi sallia tilannetta, jossa tililt¨
a 123 v¨
ahennet¨
a¨
an 100
euroa, mutta tilille 321 ei lis¨
at¨
ak¨
a¨
an 100 euroa.
5.5.3
Transaktiotasot
Transaktioiden k¨
ayt¨
oll¨
a pyrit¨
a¨
an est¨
am¨
a¨
an jollain tavalla virheellisen datan
k¨
aytt¨
o tietokantaa k¨
aytt¨
aviss¨
a sovelluksissa. K¨
ayt¨
ann¨
oss¨
a siis halutaan est¨
a¨
a
52
kyselyit¨
a palauttamasta virheellist¨
a dataa, mik¨
a voi tapahtua seuraavilla
tavoilla:
1. Luetaan vahvistamatonta dataa eli sellaista dataa, jonka on
kirjoittanut vahvistamaton transaktio.
2. Vahvistamattoman datan lukeminen on toistamattomissa olevan
lukuoperaation erikoistapaus. Kohta n¨
ahd¨
a¨
an, ett¨
a kyselyoperaatiota
ei v¨
altt¨
am¨
att¨
a voida toistaa, vaikka sallittaisiinkin vain vahvistetun datan
lukeminen.
3. Toistettu lukuoperaatio palauttaa enemm¨
an rivej¨
a; ylim¨
a¨
ar¨
aisi¨
a
rivej¨
a kutsutaan haamuriveiksi. T¨
am¨
an virheen ja toistamattomien
lukuoperaatioiden eroa valotetaan pian.
Vahvistamattoman datan lukemisen ja toistamattomissa olevan lukuoperaation eroa voidaan havainnollistaa seuraavien vaiheiden avulla:
1. Transaktio 1 alkaa.
2. Transaktio 1 lukee rivin.
3. Transaktio 2 alkaa.
4. Transaktio 2 muuttaa transaktion 1 lukeman rivin sis¨
alt¨
o¨
a.
5. Transaktio 2 vahvistetaan.
6. Transaktio 1 lukee aiemmin lukemansa rivin uudelleen.
N¨
aiden vaiheiden j¨
alkeen transaktio 1 on lukenut virheellist¨
a dataa. Koska
transaktio 2 vahvistettiin ennen kuin transaktio 1 luki rivin uudelleen, ei
kyseess¨
a kuitenkaan ollut vahvistamattoman datan luku. Samalla tavoin
transaktio 2 olisi saattanut tuhota transaktion 1 lukeman rivin, mink¨
a j¨
alkeen
transaktio 1 ei tietenk¨
a¨
an olisi voinut lukea rivi¨
a. Molemmille tapauksille on
kuitenkin yhteist¨
a se, ett¨a transaktion 1 ensimm¨
ainen lukuoperaatio ei ole
toistettavissa.
Seuraava esimerkki selvitt¨
a¨
a haamurivej¨
a aiheuttavien transaktioiden
toimintaa:
1. Transaktio 1 alkaa.
2. Transaktio 1 lukee rivit, joissa vaikkapa attribuutin X arvo on 3.
3. Transaktio 2 alkaa.
4. Transaktio 2 lis¨
a¨
a rivin, jossa attribuutin X arvo on 3.
5. Transaktio 2 vahvistetaan.
6. Transaktio 1 lukee rivit, joissa attribuutin X arvo on 3.
53
Nyt transaktion 1 toisen lukuoperaation tulokseen sis¨
altyv¨
at sek¨
a
ensimm¨
aisess¨
a lukuoperaatiossa haetut rivit ett¨
a transaktion 2 lis¨
a¨
am¨
a rivi.
Periaatteessa transaktion 1 lukuoperaatio siis on toistettavissa, kunhan
tuloksesta jotenkin poistetaan transaktion 2 lis¨
a¨
am¨
a rivi. T¨
am¨
a erottaa
toitamattomissa olevan lukuoperaation ja haamurivej¨
a lukevan lukuoperaation
toisistaan.
N¨
aiden virhetilanteiden ymm¨
art¨
aminen on tarpeen, kun m¨
a¨
aritell¨
a¨
an
transaktiolle ns. transaktiotasoa. Yleisess¨
a tapauksessa operaatioista
Operaatio1 , ..., Operaation koostuva transaktio kirjoitetaan muodossa:
BEGIN;
SET TRANSACTION ISOLATION LEVEL T aso;
Operaatio1 ;
...
Operaatio1 ;
COMMIT;
Edellisess¨
a transaktiossa T aso on jokin seuraavista:
1. READ UNCOMMITTED: vahvistamattoman datan lukeminen, toistamattomat
lukuoperaatiot sek¨
a haamurivien lukeminen ovat mahdollisia.
2. READ COMMITTED: est¨a¨
a vahvistamattoman datan lukemisen, mutta ei
muita virheit¨
a. T¨
am¨
a on useimmissa tietokannanhallintaj¨
arjestelmiss¨
a
transaktioiden oletustaso.
3. READ REPEATABLE: est¨
a¨
a vahvistamattoman datan lukemisen ja toistamattomat lukuoperaatiot, mutta ei haamurivien lukemista.
4. SERIALIZABLE: est¨
a¨
a kaikki edell¨
a mainitut virheet eli vahvistamattoman
datan lukemisen, toistamattomat lukuoperaatiot sek¨
a haamurivien
lukemisen.
Jotkin tietokannahallintaj¨
arjestelm¨
at tarjoavat muitakin transaktiotasoja; n¨
aihin kannattaa tutustua lukemalla k¨
aytt¨
am¨
ans¨
a tietokannanhallintaj¨
arjestelm¨
an manuaaleja. Edell¨
a esitettyj¨
a transaktiotasoja k¨
aytt¨
aen
voimme kirjoittaa esimerkiksi seuraavan transaktion:
BEGIN;
SET TRANSACTION LEVEL SERIALIZABLE;
UPDATE Tili SET Saldo=Saldo-100 WHERE Id=123;
UPDATE Tili SET Saldo=Saldo+100 WHERE Id=321;
COMMIT;
Periaatteessa vain SERIALIZABLE takaa transaktiolle t¨
aydet ACIDominaisuudet. Korkeimman transaktiotason k¨
aytt¨
aminen kuitenkin hidastaa
tietokantaoperaatioita, joten kaikille transaktioille ei v¨
altt¨
am¨
att¨
a kannata
m¨
a¨
aritell¨
a tasoa SERIALIZABLE. Transaktioiden toteuttamiseksi tietokannanhallintaj¨
arjestelm¨
at k¨
aytt¨
av¨
at erilaisia lukituksia, jotka est¨
av¨
at muita transaktioita k¨
asittelem¨
ast¨
a sellaista dataa, jota jokin muu transaktio parhaillaan
k¨
asittelee.
54
Lis¨
aksi transaktioiden peruuttaminen vaatii sit¨
a, ett¨
a tietokannanhallintaj¨
arjestelm¨
a pit¨
a¨
a yll¨
a lokia tietokantaan tehdyist¨
a muutoksista.
K¨
ayt¨
ann¨
oss¨
a t¨
am¨
a toteutetaan siten, ett¨
a jokaiseen tietokannan riviin on liitetty
mm. tieto siit¨
a, mik¨
a transaktio sit¨
a parhaillaan k¨
asittelee ja mik¨
a on transaktion tila. Lukituksiin ja transaktioiden peruuttamiseen tutustutaan tarkemmin
Tietokantojen jatkokurssilla.
55
Kirjallisuutta
[1] E. F. Codd (1970). A Relational Model for Large Shared Data Banks.
Communications of the ACM 13(6): 377-387.
[2] Aku Ankan vuosikerrat 1951-2006.
[3] R. Elmasri & S. B. Navathe (2000). Fundamentals of Database Systems, 3rd
Edition. Addison-Wesley.
[4] P. P. Chen (1976). The Entity-Relationship Model - Toward a Unified View
of Data. ACM Transactions on Database Systems 1(1): 9-36.
[5] PostgreSQL:n dokumentaatio. http://www.postgresql.org/docs/.
56