Luku 4 SQL 4.1 Yleistä SQL:stä 4.2 SQL-lauseet 4.3 DML: datan hallinta 4.4 DDL: rakenteen määrittäminen 4.5 DCL: valtuuttaminen 4.6 TxCL: tapahtumanhallinta ITKA204 kevät 2016 1
4.1 Yleistä SQL:stä Structured Query Language SQL on ANSI/ISO-standardiin perustuva korkean tason kyselykieli relaatiotietokantoihin. Juuret 1970-luvun lopulla: IBM:n System R:n SEQUEL ja RSI:n Oraclen SQL. SQL-standardista julkaistu useita versioita: 1986, 1989, 1992, 1999, 2003, 2006, 2008, 2011. Suuri osa SQL-kielestä on standardisoitu, loput kielestä perustuu vakiintuneisiin käytäntöihin. Suunniteltu alun perin yksinkertaiseksi kieleksi yksinkertaisten operaatioiden suorittamiseen. Teoreettinen perusta Coddin relaatiomallissa ja siihen kuuluvassa relaatioalgebrassa. Kieli jaetaan yleisesti neljään osaan: Datan hallinta (Data Management Language, DML) Rakenteen määrittäminen (Data Definition Language, DDL) Valtuuttaminen (Data Control Language, DCL) Tapahtumanhallinta (Transaction Control Language, TxCL) Standardoimisen johdosta eri RDBMS:t toimivat SQL:n osalta hyvin samankaltaisesti. ITKA204 kevät 2016 Toni Taipalus Jyväskylän yliopisto 2
4.1 Yleistä SQL:stä Yhtäläisyydet relaatiomalliin Vaikka SQL-standardi perustuu relaatiomalliin, niissä on joitakin eroja, mm.: SQL-standardi esittelee suuren määrän toiminnallisuutta, joka ei kuulu relaatiomalliin. SQL sallii mm. moniarvoiset attribuutit. SQL:ssä relaation monikoiden (taulun rivien) sallitaan olevan samanlaiset. Kieltä onkin kritisoitu relaatiomallista poikkeamisesta. SQL:n nimeämiskäytänteet vastaavat suurpiirteisesti seuraavia relaatiomallin käsitteitä: Relaatiomalli relaatio (relation) attribuutti (attribute) monikko (tuple) SQL taulu (table) sarake (column) rivi (row) SQL:n osalta tietomallin voikin hahmottaa joukkona tauluja ja eheysrajoitteita sekä kyselykielenä. ITKA204 kevät 2016 Toni Taipalus Jyväskylän yliopisto 3
4.1 Yleistä SQL:stä Käyttö Luvussa 2 esiteltiin kyselykielen käyttöä eri tavoin: upottamalla kyselykieltä isäntäkieleen (esim. Java, C#, Python, Haskell), kutsumalla isäntäkielessä tietokantaan tallennettua rutiinia (esim. PL/SQL tai T/SQL), kokonaan isäntäkielellä (ns. natiivikyselynä) tai ORM (object-relational mapper) työkaluja käyttäen. Tämän lisäksi kyselykieltä voidaan käyttää DBMS:n SQL-rajapinnan kautta. Tämä soveltuu erityisen hyvin SQL:n opetteluun tai yksittäisten operaatioiden suorittamiseen. Tätä lähestymistapaa käytetään myös tällä kurssilla. ITKA204 kevät 2016 Toni Taipalus Jyväskylän yliopisto 4
4.2 SQL-lauseet Yleiset säännöt SQL-operaatiota kutsutaan yleisesti SQL-lauseeksi. SQL-lause koostuu SQL-avainsanoista (kuten SELECT tai ORDER BY), tietokantaobjektien (kuten taulut, näkymät ja indeksit) nimistä, ehtolausekkeista (kuten ikä > 30) ja päättyy puolipisteeseen ; Kirjainkoolla ei ole merkitystä SQL-avainsanoissa tai tietokantaobjektien nimissä. Tietokantaobjektien nimissä voidaan käyttää kirjaimia (a-z), numeroita ja alaviivaa. Tietokantaobjektin nimi ei kuitenkaan voi alkaa numerolla, eikä varattuja sanoja voi käyttää. Rivivaihdoilla ei ole merkitystä. Tällä kurssilla käsitellään SQL:ää SQL-standardin näkökulmasta. ITKA204 kevät 2016 Toni Taipalus Jyväskylän yliopisto 5
Yleistä DML:stä DML (Data Management Language) lienee tarkimmin standardoitu SQL:n alikieli. DML:n avulla voidaan suorittaa yleisimmät SQL-lauseet, eli datan: etsiminen (SELECT), lisääminen (INSERT), muokkaaminen (UPDATE) ja poistaminen (DELETE). Tällä kurssilla käsitellään suuri osa DML-kielestä. DML lienee SQL:n osalta kurssin vaativin osa-alue. ITKA204 kevät 2016 Toni Taipalus Jyväskylän yliopisto 6
Materiaalin syntaksimerkinnät SQL-avainsanat on kirjoitettu suuraakkosilla. Tietokantaobjektien nimet on kirjoitettu pienillä kirjaimilla. Hakasulkeet [ ] ryhmittävät lauseen osia. Hakasulkeita seuraa Backus/Naur-muodon mukainen kardinaalisuusmerkintä, joka kertoo hakasulkujen sisällön pakollisuudesta: + = 1..n toistoa * = 0..n toistoa? = 0..1 toistoa Pystyviiva kertoo vaihtoehtoisuudesta, esim. [ASC DESC]? = joko ASC tai DESC tai ei kumpaakaan. Hakasulkeita, kardinaalisuusmerkintöjä tai pystyviivoja ei kirjoiteta varsinaiseen SQL-lauseeseen. ITKA204 kevät 2016 Toni Taipalus Jyväskylän yliopisto 7
Harjoitustietokannan kaava Tässä alaluvussa käytetään harjoitustietokantana seuraavaa, ellei toisin mainita: ASIAKAS ( astun, asnimi, kaup, tyyppi, mpiiri ) LASKU ( laskuno, vuosi, lask_summa, tila, astun ) LASKU_RIVI ( laskuno, tuotetun, maara ) TUOTE ( tuotetun, tuotenimi, malli, ahinta, vari ) CHAR ja VARCHAR = merkkijono INT = kokonaisluku ASIAKAS LASKU LASKU_RIVI TUOTE astun CHAR(4) laskuno CHAR(4) laskuno CHAR(4) tuotetun CHAR(4) asnimi VARCHAR(15) vuosi INT tuotetun CHAR(4) tuotenimi VARCHAR(15) kaup VARCHAR(10) lask_summa INT maara INT malli VARCHAR(10) tyyppi VARCHAR(10) tila VARCHAR(2) ahinta INT mpiiri VARCHAR(10) astun CHAR(4) vari VARCHAR(10) ITKA204 kevät 2016 Toni Taipalus Jyväskylän yliopisto 8
Hakulauseen yleinen muoto SELECT-lause on SQL-hakulause. Se koostuu yksinkertaisimmillaan kahdesta osasta: SELECT-osa: mitä sarakkeita tulostauluun halutaan. Tulostaulun otsake muodostuu näistä sarakkeista. FROM-osa: mistä tauluista data noudetaan. SELECT sarake[, sarake]* FROM taulu[, taulu]*; SELECT astun, asnimi, kaup, tyyppi, mpiiri FROM asiakas; ASIAKAS astun CHAR(4) asnimi VARCHAR(15) kaup VARCHAR(10) tyyppi VARCHAR(10) astun asnimi kaup tyyppi mpiiri mpiiri VARCHAR(10) a101 Kojo JKL y k a102 Laippa TRE h i a104 Kajo JKL h l ITKA204 kevät 2016 Toni Taipalus Jyväskylän yliopisto 9
Vertailuoperaattorit Hakuehdot asetetaan hakulauseen vapaaehtoiseen WHERE-osaan. Lukuarvojen vertailuun käytetään operaattoreita <, >, <=, >= ja = Lukuarvojen erisuuruutta voidaan tarkastaa operaattoreilla <> ja!= Ehtolausekkeita voidaan yhdistää loogisilla operaattoreilla AND ja OR. SELECT sarake[, sarake]* FROM taulu[, taulu]* [WHERE ehtolauseke[ operaattori ehtolauseke]*]?; SELECT * FROM tuote WHERE ahinta > 100 AND ahinta <= 2000; TUOTE tuotetun CHAR(4) tuotenimi VARCHAR(15) malli VARCHAR(10) ahinta INT vari VARCHAR(10) ITKA204 kevät 2016 Toni Taipalus Jyväskylän yliopisto 10
Merkkijonojen vertailu Merkkijonoja vertaillaan [NOT] LIKE predikaatilla. Vertailtava merkkijono kirjoitetaan heittomerkkien väliin. Vertailussa voidaan käyttää jokerimerkkejä: Alaviiva vastaa yhtä mitä tahansa merkkiä Prosenttimerkki vastaa 0..n mitä tahansa merkkiä. SELECT mpiiri, tyyppi FROM asiakas WHERE asnimi LIKE 'Kajo'; ASIAKAS astun CHAR(4) asnimi VARCHAR(15) kaup VARCHAR(10) tyyppi VARCHAR(10) mpiiri VARCHAR(10) SELECT asnimi, tyyppi, mpiiri FROM asiakas WHERE asnimi NOT LIKE K% AND kaup LIKE _y% ; Hae niiden asiakkaiden nimi, tyyppi ja myyntipiiri, joiden nimi ei ala K-kirjaimella ja joiden asuinkaupungin toinen kirjain on y. ITKA204 kevät 2016 Toni Taipalus Jyväskylän yliopisto 11
Suoritusjärjestys Ehtolausekkeiden suoritusjärjestystä voidaan ohjata sulkeilla. Pelkkää AND-operaattoria käytettäessä ehtojen järjestyksellä ei ole tulosten kannalta merkitystä. AND on OR:iin nähden etuoikeutettu. SELECT * FROM asiakas WHERE tyyppi LIKE y AND (mpiiri LIKE i OR mpiiri LIKE k ); SELECT * FROM asiakas WHERE tyyppi LIKE y AND mpiiri LIKE i OR mpiiri LIKE k ; Hae niiden asiakkaiden kaikki tiedot, jotka ovat yritysasiakkaita ja joiden myyntipiiri on joko itä tai keski. Hae niiden asiakkaiden kaikki tiedot, jotka ovat yritysasiakkaita itäisestä myyntipiiristä sekä kaikkien keskimyyntipiirin asiakkaiden tiedot. ASIAKAS astun CHAR(4) asnimi VARCHAR(15) kaup VARCHAR(10) tyyppi VARCHAR(10) mpiiri VARCHAR(10) ITKA204 kevät 2016 Toni Taipalus Jyväskylän yliopisto 12
Tyhjäarvon tarkastaminen Tyhjäarvon esiintymistä tarkastetaan SQL:ssä IS [NOT] NULL predikaatilla. SELECT tuotenimi FROM tuote WHERE ahinta IS NULL; SELECT tuotenimi FROM tuote WHERE malli IS NOT NULL; TUOTE tuotetun CHAR(4) tuotenimi VARCHAR(15) malli VARCHAR(10) ahinta INT vari VARCHAR(10) EI NÄIN: EIKÄ NÄIN: ET TEE NÄIN: ETKÄ NÄIN: SELECT tuotenimi FROM tuote WHERE ahinta = NULL; SELECT tuotenimi FROM tuote WHERE ahinta = 0; SELECT tuotenimi FROM tuote WHERE malli LIKE ; SELECT tuotenimi FROM tuote WHERE malli LIKE NULL ; ITKA204 kevät 2016 Toni Taipalus Jyväskylän yliopisto 13
Muita tapoja vertailuun [NOT] BETWEEN predikaatti. Tarkastetaan, onko vertailtava arvo annetulla välillä. sarake BETWEEN arvo1 AND arvo2 SELECT tuotenimi FROM tuote WHERE ahinta BETWEEN 100 AND 200; = SELECT tuotenimi FROM tuote WHERE ahinta >= 100 AND ahinta <= 200; ASIAKAS astun CHAR(4) asnimi VARCHAR(15) kaup VARCHAR(10) tyyppi VARCHAR(10) mpiiri VARCHAR(10) [NOT] IN predikaatti. Tarkastetaan, kuuluuko vertailtava arvo esitettyyn joukkoon. Jokerimerkkejä ei voida käyttää. sarake IN (pilkkulista) SELECT tuotenimi FROM tuote WHERE mpiiri IN ( i, e ); = SELECT tuotenimi FROM tuote WHERE mpiiri LIKE i OR mpiiri LIKE e ; ITKA204 kevät 2016 Toni Taipalus Jyväskylän yliopisto 14
Tulosten järjestäminen Tulostaulu voidaan järjestää vapaaehtoisella ORDER BY osalla. Lisämääre ASC (ascending) järjestää tulokset nousevaan (A Z) järjestykseen. Tämä on oletus. Lisämääre DESC (descending) järjestää tulokset laskevaan järjestykseen (Z A). ASIAKAS SELECT sarake[, sarake]* FROM taulu[, taulu]* [WHERE ehtolauseke[ operaattori ehtolauseke]*]? [ORDER BY sarake[ ASC DESC]?[, sarake[ ASC DESC]?]*]?; SELECT asnimi, kaup, tyyppi FROM asiakas WHERE mpiiri LIKE i ORDER BY asnimi; astun CHAR(4) asnimi VARCHAR(15) kaup VARCHAR(10) tyyppi VARCHAR(10) mpiiri VARCHAR(10) SELECT asnimi, kaup, tyyppi FROM asiakas ORDER BY asnimi DESC, kaup ASC, tyyppi DESC; ITKA204 kevät 2016 Toni Taipalus Jyväskylän yliopisto 15
Haku useasta taulusta Relaatiotietokannassa haluttu tieto on tavallisesti jaettu eri relaatioihin. Asiakkaan tiedot ovat yhdessä taulussa, hänen tilauksensa toisessa jne. Tästä syystä useampaa kuin yhtä taulua koskevat hakulauseet ovat yleisiä. Tarkastellaan seuraavaksi useaan tauluun kohdistuvia hakulauseita. Tärkein yksittäinen asia useampaa kuin yhtä taulua koskevassa lauseessa on liitos. Liitoksen voi lähtökohtaisesti toteuttaa neljällä eri tavalla: 1. Alikyselynä käyttäen IN-predikaattia (toimii kolmiarvoisella logiikalla). 2. Alikyselynä käyttäen EXISTS-predikaattia (toimii kaksiarvoisella logiikalla). 3. Yksitasoisena ilman alikyselyä. 4. JOIN-predikaatilla. ITKA204 kevät 2016 Toni Taipalus Jyväskylän yliopisto 16
Liitos Tarkoituksena on tavallisesti löytää sarakkeiden yhteiset arvot kahdesta taulusta. Liitos toteutetaan tavallisesti viiteavaimia (viittaavaa ja viitattua) vertailemalla. Lasku_rivi-taulu: niiden tuotteiden tuotetunnukset, joista on joskus laskutettu. lasku_rivi laskuno tuotetun määrä l200 t101 1 l200 t104 2 l201 t101 3 l202 t107 1 l202 t104 Tuote-taulu: kaikkien tuotteiden tiedot. tuote tuotetun tuotenimi t101 t104 t105 t106 t107 Jollotin Kooppari Kukerrin Tukaani Furniture Yhteiset tuotetunnukset: t101 t104 t107 Ts. tuotteet, joista on joskus laskutettu. ITKA204 kevät 2016 Toni Taipalus Jyväskylän yliopisto 17
Alikysely IN-predikaatilla 1. Liitos voidaan toteuttaa alikyselynä käyttäen IN-predikaattia Kuten aikaisemmin esitettiin, IN-predikaatilla voidaan tarkastaa, kuuluuko vertailtava arvo johonkin joukkoon. Alikyselyssä IN-predikaatille ei anneta eksplisiittisesti vertailtavaa joukkoa, vaan joukko muodostetaan alikyselyn avulla. LASKU_RIVI TUOTE Pääkysely Alikysely SELECT tuotenimi FROM tuote WHERE tuotetun IN (SELECT tuotetun FROM lasku_rivi); Hae niiden tuotteiden nimet, joista on joskus laskutettu, ts. joita on joskus tilattu, ts. joita koskee ainakin yksi laskurivi. laskuno CHAR(4) tuotetun CHAR(4) maara INT tuotetun CHAR(4) tuotenimi VARCHAR(15) malli VARCHAR(10) ahinta INT vari VARCHAR(10) Jos oletetaan, että laskutettuja tuotteita ovat t101 ja t120, kysely voisi ajon aikana sieventyä näin: SELECT tuotenimi FROM tuote WHERE tuotetun IN ( t101, t120 ); HUOM. tämä ei ole tapa tehdä liitoksia, vaan havainnollistava esimerkki. ITKA204 kevät 2016 Toni Taipalus Jyväskylän yliopisto 18
Alikysely IN-predikaatilla IN-predikaatti ei tarvitse eksplisiittistä liitosehtoa, vaan liitos muodostetaan IN-predikaatin molemmin puolin. SELECT tuotenimi FROM tuote WHERE tuotetun IN (SELECT tuotetun FROM lasku_rivi); SELECT vuosi, lask_summa FROM lasku WHERE laskuno IN (SELECT laskuno FROM lasku_rivi WHERE tuotetun IN (SELECT tuotetun FROM tuote WHERE vari LIKE punainen ) ); lasku_rivi-taulun tuotetunsarakkeen arvo vastaa tuote-taulun tuotetun-sarakkeen jotakin arvoa. Hae niiden laskujen vuodet ja summat, jotka koskevat ainakin yhtä punaista tuotetta. LASKU_RIVI laskuno CHAR(4) tuotetun CHAR(4) maara INT LASKU laskuno CHAR(4) vuosi INT lask_summa INT tila VARCHAR(2) astun CHAR(4) TUOTE tuotetun CHAR(4) tuotenimi VARCHAR(15) malli VARCHAR(10) ahinta INT vari VARCHAR(10) ITKA204 kevät 2016 Toni Taipalus Jyväskylän yliopisto 19
Alikysely IN-predikaatilla SELECT vuosi, lask_summa FROM lasku WHERE laskuno IN (SELECT laskuno FROM lasku_rivi WHERE tuotetun IN (SELECT tuotetun FROM tuote WHERE vari LIKE punainen ) ); TUOTE tuotetun CHAR(4) tuotenimi VARCHAR(15) malli VARCHAR(10) ahinta INT vari VARCHAR(10) LASKU_RIVI laskuno CHAR(4) tuotetun CHAR(4) maara INT LASKU laskuno CHAR(4) vuosi INT lask_summa INT tila VARCHAR(2) astun CHAR(4) tuote lasku_rivi lasku vari tuotetun tuotetun laskuno laskuno vuosi lask_summa punainen t210 t299 l400 l400 2002 4700 hyväksytään. punainen t222 t222 l400 l401 2003 3000 hyväksytään. sininen t299 t222 l401 l402 2002 1800 hylätään. ITKA204 kevät 2016 Toni Taipalus Jyväskylän yliopisto 20
Näkyvyysalue FROM-osissa esiteltyjen taulujen sarakkeisiin voidaan viitata sisemmissä alikyselyissä. Alikyselyiden FROM-osissa esiteltyjen taulujen sarakkeisiin ei voida viitata ulomman tason kyselyissä. Pääkysely. Tässä ei voida viitata alikyselyiden taulujen sarakkeisiin. Alikysely 1. Tässä voidaan viitata pääkyselyn taulun sarakkeisiin, mutta ei alikysely 2:n taulun. SELECT tuotenimi FROM tuote WHERE tuotetun IN (SELECT tuotetun FROM lasku_rivi WHERE laskuno IN (SELECT laskuno FROM lasku) ); Alikysely 2. Tässä voidaan viitata pääkyselyn ja alikysely 1:n taulujen sarakkeisiin. ITKA204 kevät 2016 Toni Taipalus Jyväskylän yliopisto 21
Luentotehtävä 3 Esitä seuraava kysely SQL-kielellä (voit käyttää apuna TIM:n tietokantaa): Hae niiden yritysasiakkaiden nimet ja kaupungit, jotka eivät ole Kouvolasta ja joita on laskutettu vuonna 2014. Asiakkuuden tyyppi on tallennettu muodossa y tai h (yritys- tai henkilöasiakas). [ Tuloksena: <Virtanen, Mikkeli> ja <Kassakko, Helsinki> ] ASIAKAS LASKU LASKU_RIVI TUOTE astun CHAR(4) laskuno CHAR(4) laskuno CHAR(4) tuotetun CHAR(4) asnimi VARCHAR(15) vuosi INT tuotetun CHAR(4) tuotenimi VARCHAR(15) kaup VARCHAR(10) lask_summa INT maara INT malli VARCHAR(10) tyyppi VARCHAR(10) tila VARCHAR(2) ahinta INT mpiiri VARCHAR(10) astun CHAR(4) vari VARCHAR(10) ITKA204 kevät 2016 Toni Taipalus Jyväskylän yliopisto 22
Luentotehtävä 3: ratkaisu Hae niiden yritysasiakkaiden nimet ja kaupungit, jotka eivät ole Kouvolasta ja joita on laskutettu vuonna 2014. ASIAKAS LASKU LASKU_RIVI TUOTE astun CHAR(4) laskuno CHAR(4) laskuno CHAR(4) tuotetun CHAR(4) asnimi VARCHAR(15) vuosi INT tuotetun CHAR(4) tuotenimi VARCHAR(15) kaup VARCHAR(10) lask_summa INT maara INT malli VARCHAR(10) tyyppi VARCHAR(10) tila VARCHAR(2) ahinta INT mpiiri VARCHAR(10) astun CHAR(4) vari VARCHAR(10) SELECT asnimi, kaup FROM asiakas WHERE tyyppi LIKE y AND kaup NOT LIKE Kouvola AND astun IN (SELECT astun FROM lasku WHERE vuosi = 2014); ITKA204 kevät 2016 Toni Taipalus Jyväskylän yliopisto 23
Tarkentimet Kahdessa tai useammassa taulussa voi olla saman nimisiä sarakkeita. Niihin tulee viitata yksiselitteisesti tarkentimilla. Tarkennin voi olla joko taulun nimi tai itse määritelty lauseen FROM-osassa. SELECT sarake[, sarake]* FROM taulu[ tarkennin]?[, taulu[ tarkennin]?]* [WHERE ehtolauseke[ operaattori ehtolauseke]*]? [ORDER BY sarake[ ASC DESC]?[, sarake[ ASC DESC]?]*]?; ASIAKAS astun CHAR(4) asnimi VARCHAR(15) kaup VARCHAR(10) tyyppi VARCHAR(10) mpiiri VARCHAR(10) SELECT asiakas.asnimi FROM asiakas, lasku WHERE asiakas.astun = lasku.astun AND asiakas.astun LIKE a101 ; SELECT a.asnimi FROM asiakas a, lasku l WHERE a.astun = l.astun AND a.astun LIKE a101 ; EI TOIMI: SELECT asnimi FROM asiakas, lasku WHERE astun = astun AND astun LIKE a101 ; LASKU laskuno CHAR(4) vuosi INT lask_summa INT tila VARCHAR(2) astun CHAR(4) ITKA204 kevät 2016 Toni Taipalus Jyväskylän yliopisto 24
Alikysely EXISTS-predikaatilla 2. Liitos voidaan toteuttaa alikyselynä käyttäen EXISTS-predikaattia. EXISTS-predikaatti tarvitsee eksplisiittisen liitosehdon. Liitos muodostetaan aina alikyselyssä. Alikyselyn SELECT-osan valinnalla ei ole merkitystä, tavallisesti käytetään joko numeroa yksi tai tähtimerkkiä. SELECT t.tuotenimi FROM tuote t WHERE EXISTS (SELECT * FROM lasku_rivi lr WHERE t.tuotetun = lr.tuotetun); Hae niiden tuotteiden nimet, joita koskee ainakin yksi laskurivi. LASKU_RIVI laskuno CHAR(4) tuotetun CHAR(4) maara INT TUOTE tuotetun CHAR(4) tuotenimi VARCHAR(15) malli VARCHAR(10) ahinta INT vari VARCHAR(10) ITKA204 kevät 2016 Toni Taipalus Jyväskylän yliopisto 25
Alikysely EXISTS-predikaatilla SELECT t.tuotenimi FROM tuote t WHERE EXISTS (SELECT * FROM lasku_rivi lr WHERE t.tuotetun = lr.tuotetun); SELECT l.vuosi, l.lask_summa FROM lasku l WHERE EXISTS (SELECT * FROM lasku_rivi lr WHERE l.laskuno = lr.laskuno AND EXISTS (SELECT * FROM tuote t WHERE lr.tuotetun = t.tuotetun AND t.vari LIKE punainen ) ); = = SELECT tuotenimi FROM tuote WHERE tuotetun IN (SELECT tuotetun FROM lasku_rivi); SELECT vuosi, lask_summa FROM lasku WHERE laskuno IN (SELECT laskuno FROM lasku_rivi WHERE tuotetun IN (SELECT tuotetun FROM tuote WHERE vari LIKE punainen ) ); TUOTE tuotetun CHAR(4) tuotenimi VARCHAR(15) malli VARCHAR(10) ahinta INT vari VARCHAR(10) LASKU_RIVI laskuno CHAR(4) tuotetun CHAR(4) maara INT LASKU laskuno CHAR(4) vuosi INT lask_summa INT tila VARCHAR(2) astun CHAR(4) ITKA204 kevät 2016 Toni Taipalus Jyväskylän yliopisto 26
Yksitasoinen liitos 3. Liitos voidaan toteuttaa yksitasoisena ilman alikyselyitä. Liitosehto annetaan eksplisiittisesti lauseen WHERE-osassa. Toimintalogiikka sama kuin EXISTS-predikaatilla. SELECT t.tuotenimi FROM tuote t, lasku_rivi lr WHERE t.tuotetun = lr.tuotetun; SELECT l.vuosi, l.lask_summa FROM tuote t, lasku_rivi lr, lasku l WHERE t.tuotetun = lr.tuotetun AND lr.laskuno = l.laskuno AND t.vari LIKE punainen ; Lauseet vastaavat edellisen kalvon lauseita, jos liitosehdot toteuttavissa sarakkeissa ei ole tyhjäarvoja. HUOM. kaikki lauseita ei voi toteuttaa yksitasoisena, esim. ei-ole-olemassa-tapaus ja koostefunktioihin perustuva liitos. Näitä käsitellään myöhemmin. TUOTE tuotetun CHAR(4) tuotenimi VARCHAR(15) malli VARCHAR(10) ahinta INT vari VARCHAR(10) LASKU_RIVI laskuno CHAR(4) tuotetun CHAR(4) maara INT LASKU laskuno CHAR(4) vuosi INT lask_summa INT tila VARCHAR(2) astun CHAR(4) ITKA204 kevät 2016 Toni Taipalus Jyväskylän yliopisto 27
Toisteisten rivien hylkääminen Jos jokaisesta tulostaulun rivistä halutaan uniikki, voidaan käyttää DISTINCT-lisämäärettä. Erilaiset liitokset (erityisesti yksitasoinen liitos) voivat tuottaa duplikaattirivejä tulostauluun. DISTINCT-lisämääre sijoitetaan tässä tapauksessa lauseen SELECT-avainsanan jälkeen. DISTINCT-lisämäärettä voidaan käyttää myös koostefunktioissa (esitellään myöhemmin). SELECT DISTINCT t.tuotenimi FROM tuote t, lasku_rivi lr WHERE t.tuotetun = lr.tuotetun; ITKA204 kevät 2016 Toni Taipalus Jyväskylän yliopisto 28
Liitos JOIN-predikaatilla 4. Liitos voidaan toteuttaa JOIN-predikaatilla. JOIN-predikaatti eli sisäliitos on standardin versiossa SQL-92 lisätty tapa toteuttaa liitoksia. JOIN-predikaatti on syntaksiltaan vähemmän virhealtis kuin muut esitellyt liitostavat. SELECT sarake[, sarake]* FROM taulu [tarkennin]? [[INNER]? JOIN taulu [tarkennin]? ON liitosehto]+ [WHERE ehtolauseke[ operaattori ehtolauseke]*]? [ORDER BY sarake[ ASC/DESC]?[, sarake[ ASC/DESC]]*]?; SELECT l.vuosi, l.lask_summa FROM tuote t JOIN lasku_rivi lr ON t.tuotetun = lr.tuotetun JOIN lasku l ON lr.laskuno = l.laskuno WHERE t.vari LIKE punainen ; TUOTE tuotetun CHAR(4) tuotenimi VARCHAR(15) malli VARCHAR(10) ahinta INT vari VARCHAR(10) LASKU_RIVI laskuno CHAR(4) tuotetun CHAR(4) maara INT LASKU laskuno CHAR(4) vuosi INT lask_summa INT tila VARCHAR(2) astun CHAR(4) ITKA204 kevät 2016 Toni Taipalus Jyväskylän yliopisto 29
Kyselyn suunnittelu 1. Ensin on syytä tarkastella tietokannan kaavaa ja tunnistaa ne taulut, joista tietoa halutaan tulostauluun. 2. Seuraavaksi etsitään ne taulut, joiden sarakkeisiin täytyy kohdistaa ehtolausekkeita, tällaisia ehtolausekkeita kutsutaan myös sisällöllisiksi ehdoiksi. 3. Seuraavaksi tarkastellaan, mitä muita tauluja mahdollisesti tarvitaan, jotta jo kyselyn kannalta relevanteiksi luokitellut taulut voidaan liittää liitosehdoilla. 4. Valitaan mahdolliset tarkentimet. Jos lauseessa käytetään tarkentimia, on suositeltavaa käyttää niitä koko lauseessa, vaikka jokin sarake se sellaista välttämättä tarvitsisikaan. 5. Ennen varsinaisen lauseen kirjoittamista täytyy tunnistaa, millä sarakkeilla liitosehdot voidaan tehdä. Suunnittele esim. piirtämällä. 6. Lopuksi kirjoitetaan kysely. ITKA204 kevät 2016 Toni Taipalus Jyväskylän yliopisto 30
Koostefunktiot Koostefunktioita (aggregate function) käytetään laskutoimitusten suorittamiseen. Niille annetaan tavallisesti yksi parametri, ja ne palauttavat yhden arvon. Koostefunktiot ovat minimi (MIN), maksimi (MAX), keskiarvo (AVG), summa (SUM) ja lukumäärä (COUNT). SUM käsittelee tyhjäarvoa kuten nollaa: 2+4+NULL+2 = 8. COUNT laskee vain sarakkeesta vain ne rivit (solut), joissa ei ole tyhjäarvoa: Matti + Teppo +NULL = 2. Koostefunktiot sijoitetaan lauseen SELECT- tai HAVING-osaan (esitellään myöhemmin). I Ei koostefunktioita WHERE-osaan. TYÖNTEKIJÄ ttnro palkka sukunimi t001 1000 Jantunen t002 2000 Kilpiö t003 2000 Sammal t004 3000 Räikkönen t005 Lassila Funktio Palauttaa arvon COUNT(palkka) 4 SUM(palkka) 8000 AVG(palkka) 2000 MAX(palkka) 3000 MIN(palkka) 1000 COUNT(DISTINCT palkka) 3 ITKA204 kevät 2016 Toni Taipalus Jyväskylän yliopisto 31
Koostefunktiot AS-predikaatilla voidaan tarvittaessa nimetä tulostaulun sarakkeet kuvaavammin. COUNT(*) laskee taulun rivien lukumäärän. SELECT COUNT(DISTINCT kaup) AS kaupunkien lukumaara FROM asiakas; ASIAKAS astun CHAR(4) asnimi VARCHAR(15) kaup VARCHAR(10) tyyppi VARCHAR(10) mpiiri VARCHAR(10) TUOTE tuotetun CHAR(4) SELECT MAX(t.ahinta) - MIN(t.ahinta) AS hintaero FROM tuote t, lasku_rivi lr WHERE t.tuotetun = lr.tuotetun AND lr.maara > 1; SELECT SUM(ahinta)*1.22 AS yhteensa (plus alv) FROM tuote WHERE tuotenimi IS NOT NULL; SELECT COUNT(*) AS yritysasiakkaiden lukumäärä FROM asiakas WHERE tyyppi LIKE y ; Huomaa laskenta SELECT-osassa. Laskutoimituksia voidaan tehdä ilman koostefunktioitakin. tuotenimi VARCHAR(15) malli VARCHAR(10) ahinta INT vari VARCHAR(10) LASKU_RIVI laskuno CHAR(4) tuotetun CHAR(4) maara INT ITKA204 kevät 2016 Toni Taipalus Jyväskylän yliopisto 32
Ryhmittely Koostefunktioiden tuloksia täytyy usein ryhmitellä jonkin toisen sarakkeen mukaan. Esim. tuotehintojen keskiarvot tuoteväreittäin. Ryhmittely tapahtuu GROUP BY määreellä, joka sijoittuu lauseessa heti WHERE-osan jälkeen. Ryhmittely vaatii, että ainakin yksi tulostaulun sarake on muodostettu ilman koostefunktiota (ns. ryhmittelevä sarake). Jos tulostaulussa on lisäksi ainakin yksi koostefunktion muodostama sarake, täytyy ryhmittely tehdä jokaisen ryhmittelevän sarakkeen mukaan. SELECT sarake[, sarake]* FROM taulu [tarkennin]?[, taulu [tarkennin]?]* [WHERE ehtolauseke[ operaattori ehtolauseke]*]? [GROUP BY sarake[, sarake]*]? [ORDER BY sarake[ ASC DESC]?[, sarake[ ASC DESC]]*]?; ITKA204 kevät 2016 Toni Taipalus Jyväskylän yliopisto 33
Ryhmittely SELECT SUM(ahinta) AS hinta_yht, vari FROM tuote GROUP BY vari; hinta_yht vari 97 harmaa 2001 musta SELECT tyyppi, kaup, COUNT(*) AS asiakkaita 110 punainen FROM asiakas GROUP BY tyyppi, kaup; tyyppi kaup asiakkaita h Helsinki 15 h Jyväskylä 9 y Helsinki 2 y Jyväskylä 10 SELECT COUNT(*) AS asiakkaita, kaup FROM asiakas a, lasku l WHERE a.astun = l.astun GROUP BY kaup ORDER BY asiakkaita; Hae kaupungeittain sellaisten asiakkaiden lukumäärä, joita koskee ainakin yksi lasku. Järjestä tulokset asiakkaiden lukumäärän mukaan nousevaan järjestykseen. ITKA204 kevät 2016 Toni Taipalus Jyväskylän yliopisto 34
II Ryhmittely SELECT SUM(ahinta) FROM tuote; Jos yksikin tulostaulun sarake on koostefunktion muodostama, ryhmittely on tehtävä jokaisen ryhmittelevän sarakkeen mukaan. SELECT tuotenimi FROM tuote; Oikein. SELECT SUM(ahinta), AVG(ahinta) FROM tuote; SELECT SUM(ahinta), vari FROM tuote; VÄÄRIN. Ei ryhmitelty värin mukaan. SELECT malli, SUM(ahinta), vari FROM tuote GROUP BY vari; VÄÄRIN. Ei ryhmitelty mallin mukaan. ITKA204 kevät 2016 Toni Taipalus Jyväskylän yliopisto 35
Ryhmittely Joskus ryhmiteltyjä tuloksia täytyy rajata koostefunktion tuottaman arvon perusteella. Rajaus tehdään HAVING-osalla, joka sijoittuu GROUP BY- ja ORDER BY osien väliin. SELECT sarake[, sarake]* FROM taulu [tarkennin]?[, taulu [tarkennin]?]* [WHERE ehtolauseke[ operaattori ehtolauseke]*]? [GROUP BY sarake[, sarake]*]? [HAVING ehtolauseke[ operaattori ehtolauseke]*]? [ORDER BY sarake[ ASC DESC]?[, sarake[ ASC DESC]]*]?; SELECT COUNT(*) AS asiakkaita, a.kaup FROM asiakas a, lasku l WHERE a.astun = l.astun GROUP BY kaup HAVING COUNT(*) > 3; Hae kaupungeittain sellaisten asiakkaiden lukumäärä, joita koskee ainakin yksi lasku. Ota tuloksiin mukaan vain ne kaupungit, joissa asuu enemmän kuin kolme ehdot täyttävää asiakasta. ITKA204 kevät 2016 Toni Taipalus Jyväskylän yliopisto 36
Luentotehtävä 4 Esitä seuraavat kyselyt SQL-kielellä: a) Hae asiakkaita koskevien laskujen määrä asiakkaittain (asnimi). Rajaa tulokset niihin asiakkaisiin, joita koskee yli kaksi laskua. b) Hae laskutettujen tuotteiden kappalemäärät vuosittain ja tuoteväreittäin. ASIAKAS LASKU LASKU_RIVI TUOTE astun CHAR(4) laskuno CHAR(4) laskuno CHAR(4) tuotetun CHAR(4) asnimi VARCHAR(15) vuosi INT tuotetun CHAR(4) tuotenimi VARCHAR(15) kaup VARCHAR(10) lask_summa INT maara INT malli VARCHAR(10) tyyppi VARCHAR(10) tila VARCHAR(2) ahinta INT mpiiri VARCHAR(10) astun CHAR(4) vari VARCHAR(10) ITKA204 kevät 2016 Toni Taipalus Jyväskylän yliopisto 37
Luentotehtävä 4: ratkaisu a) Hae asiakkaita koskevien laskujen määrä asiakkaittain (asnimi). Rajaa tulokset niihin asiakkaisiin, joita koskee yli kaksi laskua. b) Hae laskutettujen tuotteiden kappalemäärät vuosittain ja tuoteväreittäin. SELECT a.asnimi, COUNT(l.laskuno) AS laskuja FROM asiakas a, lasku l WHERE a.astun = l.astun GROUP BY a.asnimi HAVING COUNT(l.laskuno) > 2; SELECT l.vuosi, t.vari, SUM(lr.maara) AS kpl FROM tuote t, lasku_rivi lr, lasku l WHERE t.tuotetun = lr.tuotetun AND lr.laskuno = l.laskuno GROUP BY l.vuosi, t.vari; ASIAKAS LASKU LASKU_RIVI TUOTE astun CHAR(4) laskuno CHAR(4) laskuno CHAR(4) tuotetun CHAR(4) asnimi VARCHAR(15) vuosi INT tuotetun CHAR(4) tuotenimi VARCHAR(15) kaup VARCHAR(10) lask_summa INT maara INT malli VARCHAR(10) tyyppi VARCHAR(10) tila VARCHAR(2) ahinta INT mpiiri VARCHAR(10) astun CHAR(4) vari VARCHAR(10) ITKA204 kevät 2016 Toni Taipalus Jyväskylän yliopisto 38
Skalaarifunktiot Koostefunktioiden (jotka ovat standardoituja) lisäksi on olemassa joukko skalaarifunktioita. Skalaarifunktioiden olemassa olo, nimi ja toiminta riippuvat vahvasti tuotteesta. Yleisiä skalaarifunktioita ovat esim. Pyöristys ja katkaisu, esim. ROUND(55.9870, 2) ja TRUNCATE(3.1415926, 4). Tyyppimuunnokset, esim. TO_CHAR(100), TO_INT( 500 ), CAST(100) AS FLOAT) Merkkijonojen käsittely, esim. SUBSTRING(tilakoodi, FROM 1 FOR 5), UPPER( turska ) ja LOWER( NORJA ). Aikatiedon käsittely, esim. EXTRACT(YEAR FROM now()). SQL-rajapintaa voidaan myös käyttää laskutoimituksiin. SELECT 10 * 58 FROM dual; SELECT (SELECT ahinta FROM tuote WHERE tuotetun = 1000) + (SELECT ahinta FROM tuote WHERE tuotetun = 1008) FROM dual; ITKA204 kevät 2016 Toni Taipalus Jyväskylän yliopisto 39
Tyypillisiä ongelmia: ei-ole-olemassa-tapaus Silloin tällöin tuloksiin halutaan taulun sellaiset rivit, joihin viittaavia arvoja ei löydy jostakin muusta taulusta. Vertailuun voidaan käyttää NOT EXISTS predikaattia, jonka alikysely palauttaa arvon false, jos yksikin ehdot täyttävä rivi löytyy tai true, jos yhtään ehdot täyttävää riviä ei löydy. NOT EXISTS vaatii aina eksplisiittisen liitosehdon! Vertailuun voidaan käyttää myös NOT IN predikaattia alikyselyn yhteydessä. Katso tarvittaessa edellisiä kalvoja IN- ja EXISTS-predikaattien toiminnasta ja syntaksista. Ei mahdollista yksitasoisella ratkaisulla! ITKA204 kevät 2016 Toni Taipalus Jyväskylän yliopisto 40
Tyypillisiä ongelmia: ei-ole-olemassa-tapaus ASIAKAS LASKU LASKU_RIVI TUOTE astun CHAR(4) laskuno CHAR(4) laskuno CHAR(4) tuotetun CHAR(4) asnimi VARCHAR(15) vuosi INT tuotetun CHAR(4) tuotenimi VARCHAR(15) kaup VARCHAR(10) lask_summa INT maara INT malli VARCHAR(10) tyyppi VARCHAR(10) tila VARCHAR(2) ahinta INT mpiiri VARCHAR(10) astun CHAR(4) vari VARCHAR(10) SELECT a.asnimi FROM asiakas a WHERE NOT EXISTS (SELECT * FROM lasku l WHERE a.astun = l.astun); Hae sellaisten asiakkaiden nimet, joita ei ole koskaan (ts. ei kertaakaan) laskutettu. SELECT a.asnimi FROM asiakas a WHERE a.kaup LIKE Helsinki AND NOT EXISTS (SELECT * FROM lasku l WHERE a.astun = l.astun AND l.vuosi = 2011); Hae sellaisten helsinkiläisten asiakkaiden nimet, joita ei ole laskutettu kertaakaan vuonna 2011. SELECT t.tuotenimi FROM tuote t WHERE NOT EXISTS (SELECT * FROM lasku_rivi lr WHERE t.tuotetun = lr.tuotetun AND lr.maara > 10); Hae sellaisten tuotteiden nimet, joita ei ole koskaan tilattu yli kymmentä kappaletta kerralla. ITKA204 kevät 2016 Toni Taipalus Jyväskylän yliopisto 41
Tyypillisiä ongelmia: ei-ole-olemassa-tapaus SELECT a.asnimi FROM asiakas a WHERE NOT EXISTS (SELECT * FROM lasku l WHERE a.astun = l.astun); SELECT a.asnimi FROM asiakas a WHERE a.kaup LIKE Helsinki AND NOT EXISTS (SELECT * FROM lasku l WHERE a.astun = l.astun AND l.vuosi = 2011); SELECT t.tuotenimi FROM tuote t WHERE NOT EXISTS (SELECT * FROM lasku_rivi lr WHERE t.tuotetun = lr.tuotetun AND lr.maara > 10); SELECT asnimi FROM asiakas WHERE astun NOT IN (SELECT astun FROM lasku); SELECT asnimi FROM asiakas WHERE kaup LIKE Helsinki AND astun NOT IN (SELECT astun FROM lasku WHERE vuosi = 2011); SELECT tuotenimi FROM tuote WHERE tuotetun NOT IN (SELECT tuotetun FROM lasku_rivi WHERE maara > 10); ITKA204 kevät 2016 Toni Taipalus Jyväskylän yliopisto 42
Tyypillisiä ongelmia: ei-ole-olemassa-tapaus III Ei-ole-olemassa-tapaus vaatii aina alikyselyn. Tehtävä: hae sellaisten asiakkaiden nimet, joita ei ole laskutettu kertaakaan vuonna 2011. oikein: SELECT a.asnimi FROM asiakas a WHERE NOT EXISTS (SELECT * FROM lasku l WHERE a.astun = l.astun AND l.vuosi = 2011); oikein: SELECT asnimi FROM asiakas WHERE astun NOT IN (SELECT astun FROM lasku WHERE vuosi = 2011); väärin: SELECT a.asnimi FROM asiakas a WHERE EXISTS (SELECT * FROM lasku l WHERE a.astun = l.astun AND l.vuosi <> 2011); väärin: SELECT a.asnimi FROM asiakas a, lasku l WHERE a.astun = l.astun AND l.vuosi <> 2011; Vastaavat kysymykseen: Hae sellaisten asiakkaiden nimet, joita on laskutettu ainakin kerran jonakin muuna vuonna kuin 2011. ITKA204 kevät 2016 Toni Taipalus Jyväskylän yliopisto 43
Tyypillisiä ongelmia: alikyselyn tulosten vertailu vakioon SELECT a.asnimi FROM asiakas a WHERE 2 < (SELECT COUNT(l.laskuno) FROM lasku l WHERE a.astun = l.astun); SELECT a.asnimi FROM asiakas a WHERE 10 = (SELECT COUNT(l.laskuno) FROM lasku l WHERE a.astun = l.astun); Hae sellaisten asiakkaiden nimet, joita koskee ainakin kolme laskua. Hae sellaisten asiakkaiden nimet, joita koskee täsmälleen 10 laskua. I Ei koostefunktioita WHERE-osaan. Alikyselyn tuloksia voidaan vertailla myös sarakkeeseen: SELECT tuotenimi FROM tuote WHERE ahinta > (SELECT AVG(ahinta) FROM tuote); Hae sellaisten tuotteiden nimet, joiden hinta on tuotteiden keskimääräistä hintaa korkeampi. ITKA204 kevät 2016 Toni Taipalus Jyväskylän yliopisto 44
Tyypillisiä ongelmia: saman taulun usea läpikäynti Läpikäynnillä tarkoitetaan tässä yhteydessä taulun esittelyä hakulauseen FROM-osassa. Jos taulu halutaan tarkastaa useammin kuin kerran, on käytettävä apuna joko alikyselyiden mahdollistamia näkyvyysalueita tai useita tarkentimia yksitasoisessa ratkaisussa. SELECT asnimi, mpiiri FROM asiakas WHERE asnimi NOT LIKE 'Kajo' AND mpiiri IN (SELECT mpiiri FROM asiakas WHERE asnimi LIKE 'Kajo'); Hae niiden asiakkaiden nimet ja myyntipiirit, jotka toimivat samassa myyntipiirissä kuin Kajo. 2 asiakas asnimi Kajo Lipetti Laippa Hukari asiakas asnimi Lipetti Laippa Hukari mpiiri e k i e mpiiri k i e 4 3 asiakas asnimi Kajo Lipetti Laippa Hukari asiakas asnimi Kajo tulostaulu asnimi Hukari mpiiri e k i e mpiiri e mpiiri e 1 ITKA204 kevät 2016 Toni Taipalus Jyväskylän yliopisto 45
Tyypillisiä ongelmia: saman taulun usea läpikäynti Hae niiden asiakkaiden nimet ja myyntipiirit, jotka toimivat samassa myyntipiirissä kuin Kajo. asiakas asnimi Kajo mpiiri e asiakas asnimi Kajo mpiiri e SELECT a1.asnimi, a1.mpiiri FROM asiakas a1, asiakas a2 WHERE a1.asnimi NOT LIKE Kajo AND a1.mpiiri = a2.mpiiri AND a2.asnimi LIKE Kajo ; SELECT a1.asnimi, a1.mpiiri FROM asiakas a1 WHERE a1.asnimi NOT LIKE Kajo AND EXISTS (SELECT * FROM asiakas a2 WHERE a1.mpiiri = a2.mpiiri AND a2.asnimi LIKE Kajo ); 2 Lipetti Laippa Hukari asiakas asnimi Lipetti Laippa Hukari k i e mpiiri k i e 3 Lipetti Laippa Hukari asiakas asnimi Kajo tulostaulu asnimi k i e mpiiri e mpiiri 1 4 Hukari e ITKA204 kevät 2016 Toni Taipalus Jyväskylän yliopisto 46
Tyypillisiä ongelmia: jako-operaatio Jako-operaatiolle tyypillistä on tunnistaa, löytyykö liitosehdon muodostavan sarakkeen arvo liitoksen toisen puolen taulun jokaiselta riviltä. Hae sellaisten laskujen laskunumerot, jotka koskevat kaikkia tuotteita. SELECT DISTINCT lr1.laskuno FROM lasku_rivi lr1 WHERE NOT EXISTS (SELECT * FROM tuote t WHERE NOT EXISTS (SELECT * FROM lasku_rivi lr2 WHERE lr2.laskuno = lr1.laskuno AND lr2.tuotetun = t.tuotetun)); = SELECT laskuno FROM lasku_rivi WHERE tuotetun IN (SELECT tuotetun FROM tuote) GROUP BY laskuno HAVING COUNT(*) = (SELECT COUNT(*) FROM tuote); Ei ole olemassa sellaista tuotetunnusta, jota lasku ei koskisi. Laskua koskevia lasku_rivejä on yhtä monta kuin on tuotteita tuote-taulussa. ITKA204 kevät 2016 Toni Taipalus Jyväskylän yliopisto 47
Integroivia esimerkkejä SELECT asnimi FROM asiakas WHERE asnimi NOT LIKE 'Kassakko' AND astun IN (SELECT astun FROM lasku WHERE laskuno IN (SELECT laskuno FROM lasku_rivi WHERE tuotetun IN (SELECT tuotetun FROM lasku_rivi WHERE laskuno IN (SELECT laskuno FROM lasku WHERE astun IN (SELECT astun FROM asiakas WHERE asnimi LIKE 'Kassakko') ) ) ) ); Hae sellaisten asiakkaiden nimet, joita on joskus laskutettu samasta tuotteesta kuin Kassakko-nimistä asiakasta. asiakas ( asnimi, astun ) lasku ( laskuno, astun ) lasku_rivi ( laskuno, tuotetun ) lasku_rivi ( laskuno, tuotetun ) lasku ( laskuno, astun ) asiakas ( asnimi, astun ) tarkastetaan, että nimi ei ole Kassakko tarkastetaan, että nimi on Kassakko ITKA204 kevät 2016 Toni Taipalus Jyväskylän yliopisto 48
Integroivia esimerkkejä SELECT asnimi FROM asiakas WHERE asnimi NOT LIKE 'Kassakko' AND astun IN (SELECT astun FROM lasku WHERE laskuno IN (SELECT laskuno FROM lasku_rivi WHERE tuotetun IN (SELECT tuotetun FROM lasku_rivi WHERE laskuno IN (SELECT laskuno FROM lasku WHERE astun IN (SELECT astun FROM asiakas WHERE asnimi LIKE 'Kassakko') ) ) ) ); = Liitostapoja voidaan yhdistellä: SELECT asnimi FROM asiakas WHERE asnimi NOT LIKE 'Kassakko' AND astun IN (SELECT astun FROM lasku l1 WHERE EXISTS (SELECT * FROM lasku_rivi lr1, lasku_rivi lr2, lasku l2, asiakas a WHERE l1.laskuno = lr1.laskuno AND lr1.tuotetun = lr2.tuotetun AND lr2.laskuno = l2.laskuno AND l2.astun = a.astun AND a.asnimi LIKE Kassakko ) ); Kaikissa lauseissa on 6 taulua ja 5 liitosta. = IV Lauseessa n taulua vaatii n-1 liitosta, pl. ristitulo. SELECT DISTINCT as1.asnimi FROM asiakas as1, asiakas as2, lasku l1, lasku l2, lasku_rivi lr1, lasku_rivi lr2 WHERE as1.astun = l1.astun AND l1.laskuno = lr1.laskuno AND lr1.tuotetun = lr2.tuotetun AND lr2.laskuno = l2.laskuno AND l2.astun = as2.astun AND as1.asnimi NOT LIKE 'Kassakko' AND as2.asnimi LIKE 'Kassakko'; ITKA204 kevät 2016 Toni Taipalus Jyväskylän yliopisto 49
Integroivia esimerkkejä Hae sellaisten tuotteiden nimet, joita on ostettu ainakin kerran vuonna 2000 mutta ei kertaakaan vuonna 2001. TUOTE tuotetun CHAR(4) tuotenimi VARCHAR(15) malli VARCHAR(10) ahinta INT vari VARCHAR(10) SELECT t.tuotenimi FROM tuote t WHERE EXISTS (SELECT * FROM lasku_rivi lr, lasku l WHERE t.tuotetun = lr.tuotetun AND lr.laskuno = l.laskuno AND l.vuosi = 2000) AND NOT EXISTS (SELECT * FROM lasku_rivi lr, lasku l WHERE t.tuotetun = lr.tuotetun AND lr.laskuno = l.laskuno AND l.vuosi = 2001); Huomaa, että molemmat alikyselyt ovat ns. saman arvoisia, ts. ne eivät ole sisäkkäin. SELECT tuotenimi FROM tuote WHERE tuotetun IN (SELECT tuotetun FROM lasku_rivi WHERE laskuno IN (SELECT laskuno FROM lasku WHERE vuosi = 2000) ) AND tuotetun NOT IN (SELECT tuotetun FROM lasku_rivi WHERE laskuno IN (SELECT laskuno FROM lasku WHERE vuosi = 2001) ); LASKU_RIVI laskuno CHAR(4) tuotetun CHAR(4) maara INT LASKU laskuno CHAR(4) vuosi INT lask_summa INT tila VARCHAR(2) astun CHAR(4) ITKA204 kevät 2016 Toni Taipalus Jyväskylän yliopisto 50
Integroivia esimerkkejä Hae sellaisten laskujen tiedot, jotka koskevat ainakin neljää erilaista tuotetta. LASKU laskuno CHAR(4) vuosi INT lask_summa INT tila VARCHAR(2) astun CHAR(4) SELECT * FROM lasku WHERE laskuno IN (SELECT laskuno FROM lasku_rivi GROUP BY laskuno HAVING COUNT(*) > 3); = SELECT * FROM lasku l WHERE 3 < (SELECT COUNT(*) FROM lasku_rivi lr WHERE lr.laskuno = l.laskuno); LASKU_RIVI laskuno CHAR(4) tuotetun CHAR(4) maara INT Hae osastoittain tunnukset ja palkkakeskiarvot osastoilta, joissa maksimipalkka on pienempi kuin koko yrityksen työntekijöiden keskipalkka: SELECT otun, AVG(ttpalkka) FROM tyontekija GROUP BY otun HAVING MAX(ttpalkka) < (SELECT AVG(ttpalkka) FROM tyontekija); HUOM. vertailu vaatii alikyselyn! TYONTEKIJÄ tttun CHAR(4) ttnimi VARCHAR(20) ttpalkka INT otun CHAR(4) ITKA204 kevät 2016 Toni Taipalus Jyväskylän yliopisto 51
Vastaavuudet relaatioalgebran operaatioihin 1. Projektio π saavutetaan lauseen SELECT-osalla. SELECT tuotenimi, malli ; 2. Valinta σ saavutetaan lauseen WHERE-osan ehtolausekkeilla. WHERE ahinta > 100 AND ; 3. Yhdiste saavutetaan UNION-predikaatilla. SELECT laskuno FROM lasku UNION SELECT laskuno FROM lasku_rivi; Hae laskunumerot laskuja lasku_rivi-tauluista. 4. Leikkaus ja 5. liitos saavutetaan esim. EXISTS-predikaatilla. ITKA204 kevät 2016 Toni Taipalus Jyväskylän yliopisto 52
Vastaavuudet relaatioalgebran operaatioihin 6. Erotus saavutetaan esim. NOT EXISTS predikaatilla. 7. Jako-operaatio saavutetaan esim. NOT EXISTS predikaatilla tai koostefunktiolla. 8. Ristitulo saavutetaan joko SELECT-osan sarakelistalla ja hakemalla kahdesta taulusta ilman liitosehtoa tai CROSS JOIN predikaatilla. SELECT tuotenimi, maara FROM tuote, lasku_rivi; SELECT tuotenimi, maara FROM tuote CROSS JOIN lasku_rivi; ITKA204 kevät 2016 Toni Taipalus Jyväskylän yliopisto 53
Muita vastaavuuksia 9. Ulkoliitos [LEFT RIGHT] OUTER JOIN SELECT DISTINCT t.tuotenimi, lr.laskuno, lr.maara FROM tuote t LEFT OUTER JOIN lasku_rivi lr ON t.tuotetun = lr.tuotetun ORDER BY t.tuotenimi DESC, lr.laskuno; Hae tuotteiden nimet ja lukumäärät, sekä kuinka paljon tuotteita on laskukohtaisesti laskutettu. Järjestä tulokset tuotenimen mukaan laskevaan järjestykseen ja edelleen laskunumeron mukaan nousevaan järjestykseen. Listaa myös ne tuotteet, joita ei ole laskutettu. 10. Luonnollinen liitos NATURAL JOIN luottaa tietokannan hyvään loogiseen suunnitteluun. Sarakkeiden nimien täytyy olla täsmälleen samat. SELECT sarakelista FROM taulu1 NATURAL JOIN taulu2; 11. Attribuuttilistan mukainen liitos samoin nimettyjen attribuuttien mukaan. SELECT sarakelista FROM taulu1 JOIN taulu2 USING (sarakelista); ITKA204 kevät 2016 Toni Taipalus Jyväskylän yliopisto 54
TOP 5 -virheet 1. Liitosehtoja puuttuu. 2. Koostefunktio on sijoitettu lauseen WHERE-osaan. 3. Ei-ole-olemassa -tapaus on ratkaistu väärin. 4. Ryhmittely puuttuu tai on tehty väärin. 5. Kysymys on luettu huolimattomasti: sisällöllisiä ehtoja puuttuu. ITKA204 kevät 2016 Toni Taipalus Jyväskylän yliopisto 55
Stressikäyrä Sisältörunko 1. Johdanto 2. Käsitteellinen mallintaminen 3. Relaatiomalli 4. Transformointi 5. Relaatioalgebra 6. SQL 7. Tapahtumanhallinta 8. Normalisointi 9. Tietovarastointi 10. Hajautus 11. Tietokantaparadigmat 10 9 8 7 6 5 4 3 2 1 0 Tässä 1 2 3 4 5 6 7 8 9 10 11 ITKA204 kevät 2016 Toni Taipalus Jyväskylän yliopisto 56
Taulurivien lisääminen Taulurivi lisätään INSERT-lauseella. Sarakkeet, joihin arvoja halutaan lisätä, voidaan antaa pilkkulistalla INTO-osassa. VALUES-osan pilkkulistassa annetut arvot tallennetaan INTO-osassa esiteltyyn vastaavaan sarakkeeseen. INSERT INTO taulu [(sarake[, sarake]*)]? VALUES (arvo[, arvo]*); INSERT INTO asiakas (astun, asnimi, kaup, tyyppi, mpiiri) VALUES ('a999', 'Jokinen Ry', 'Tampere', NULL, 'i'); ASIAKAS astun CHAR(4) asnimi VARCHAR(15) kaup VARCHAR(10) tyyppi VARCHAR(10) mpiiri VARCHAR(10) 1. sarake 2. sarake 3. sarake 4. sarake 5. sarake ITKA204 kevät 2016 Toni Taipalus Jyväskylän yliopisto 57
Taulurivien lisääminen Jos INTO-osassa ei ole annettu pilkkulistaa, VALUES-osan arvot tallennetaan niitä vastaaviin sarakkeisiin: ensimmäinen arvo ensimmäiseen sarakkeeseen jne. Jos arvoa ei anneta VALUES-osassa, annetaan oletusarvo (tavallisesti NULL). INSERT INTO asiakas VALUES ( a809, Toivakka, Helsinki, y, e ); INSERT INTO asiakas (astun, tyyppi, mpiiri) VALUES ( a810, y, e ); ASIAKAS astun CHAR(4) asnimi VARCHAR(15) kaup VARCHAR(10) tyyppi VARCHAR(10) mpiiri VARCHAR(10) 1. sarake 2. sarake 3. sarake 4. sarake 5. sarake INSERT INTO asiakas VALUES ( a811, y, e ); VÄÄRIN. VALUES-osassa on liian vähän sarakkeita. TYONTEKIJA INSERT INTO asiakas (asnimi, kaup) SELECT (ttnimi, ttkaup) FROM tyontekija WHERE ttkkpalkka > 20000; INSERT INTO asiakas (astun, kaup) VALUES ( a101, (SELECT kaup FROM asiakas WHERE astun LIKE a100 )); tttun CHAR(4) ttnimi VARCHAR(15) ttkaup VARCHAR(10) ttkkpalkka INT ITKA204 kevät 2016 Toni Taipalus Jyväskylän yliopisto 58
Taulurivien muokkaaminen Yhden taulun rivejä muokataan UPDATE-lauseella. UPDATE taulu SET sarake = lauseke[, sarake = lauseke]* [WHERE ehtolauseke[ operaattori ehtolauseke]*]?; WHERE-osan ehtolausekkeet päättävät, mitä rivejä muokataan. Jos WHERE-osa jätetään pois, muokataan taulun kaikkia rivejä. WHERE-osa voi sisältää miten monimutkaisia ehtoja tahansa. Kaikki tuotteet eivät salli itse määrättyjen tarkentimien käyttöä UPDATE-lauseessa. Turvallisin tapa on siis käyttää taulujen nimiä tarkentimina. ITKA204 kevät 2016 Toni Taipalus Jyväskylän yliopisto 59
Taulurivien muokkaaminen UPDATE tuote SET ahinta = ahinta * 1.15 WHERE vari LIKE harmaa ; UPDATE tuote SET ahinta = (SELECT MIN(ahinta) FROM tuote) WHERE vari IS NULL; UPDATE lasku SET tila = m WHERE EXISTS (SELECT * FROM lasku_rivi, tuote WHERE lasku.laskuno = lasku_rivi.laskuno AND lasku_rivi.tuotetun = tuote.tuotetun AND lasku.vuosi = 2015 AND tuote.tuotenimi LIKE Kellotin ); Korota harmaiden tuotteiden hintaa 15%. Muuta niiden tuotteiden, joiden väriä ei ole määritetty, hinta samaksi kuin halvimman tuotteen hinta. Vuonna 2015 myydyt Kellottimet olivat viallisia. Hyvitä asia kirjaamalla kaikki Kellottimia sisältäneet vuoden 2015 laskut maksetuiksi. TUOTE tuotetun CHAR(4) tuotenimi VARCHAR(15) malli VARCHAR(10) ahinta INT vari VARCHAR(10) LASKU_RIVI laskuno CHAR(4) tuotetun CHAR(4) maara INT LASKU laskuno CHAR(4) vuosi INT lask_summa INT tila VARCHAR(2) astun CHAR(4) ITKA204 kevät 2016 Toni Taipalus Jyväskylän yliopisto 60
Luentotehtävä 5 MM-kisojen urheilijat palkitaan mitalien tyypin ja määrän mukaisesti seuraavan kaavan mukaan: palkkio = Σ mtyyppi * mmaara * 10 000 missä mtyyppi: 3 = kulta, 2 = hopea, 1 = pronssi. Päivitä palkkiot olettaen, että a) urheilijalla voi olla vain yhden tyyppisiä mitaleja. b) urheilijalla voi olla usean tyyppisiä mitaleja. URHEILIJA urhtun CHAR(4) urhnimi VARCHAR(20) palkkiomaara INT MITALIT urhtun CHAR(4) mtyyppi INT mmaara INT ITKA204 kevät 2016 Toni Taipalus Jyväskylän yliopisto 61
Luentotehtävä 5: ratkaisu Päivitä palkkiot olettaen, että a) urheilijalla voi olla vain yhden tyyppisiä mitaleja. b) urheilijalla voi olla usean tyyppisiä mitaleja. UPDATE urheilija SET palkkiomaara = (SELECT mtyyppi * mmaara * 10000 FROM mitalit WHERE urheilija.urhtun = mitalit.urhtun); UPDATE urheilija SET palkkiomaara = (SELECT SUM(mtyyppi * mmaara * 10000) FROM mitalit WHERE urheilija.urhtun = mitalit.urhtun); URHEILIJA urhtun CHAR(4) urhnimi VARCHAR(20) palkkiomaara INT MITALIT urhtun CHAR(4) mtyyppi INT mmaara INT ITKA204 kevät 2016 Toni Taipalus Jyväskylän yliopisto 62
Taulurivien poistaminen Yhden taulun rivejä poistetaan DELETE-lauseella. DELETE FROM taulu [WHERE ehtolauseke[ operaattori ehtolauseke]*]?; Ilman WHERE-osaa poistetaan kaikki taulun rivit. WHERE-osa voi sisältää miten monimutkaisia ehtoja tahansa. Kaikki tuotteet eivät salli tarkentimien käyttöä DELETE-lauseessa. Turvallisin tapa on käyttää alikyselyitä. TUOTE tuotetun CHAR(4) tuotenimi VARCHAR(15) malli VARCHAR(10) ahinta INT vari VARCHAR(10) LASKU_RIVI laskuno CHAR(4) tuotetun CHAR(4) maara INT DELETE FROM tuote WHERE ahinta < 50 AND tuotetun NOT IN (SELECT tuotetun FROM lasku_rivi); Poista ne tuotteet, joiden hinta on alle 50 euroa ja joita ei koske yksikään lasku. ITKA204 kevät 2016 Toni Taipalus Jyväskylän yliopisto 63
4.4 DDL: rakenteen määrittäminen Yleistä DDL:stä DDL (Data Definition Language) on SQL:n alikieli tietokannan rakenteen määrittämiseen ja tietokantaobjektien (taulut, näkymät, indeksit, triggerit jne.) hallintaan. DDL:n avulla tietokantaobjekteja voidaan luoda (CREATE), muokata (ALTER) ja poistaa (DROP). ITKA204 kevät 2016 Toni Taipalus Jyväskylän yliopisto 64
4.4 DDL: rakenteen määrittäminen Tietokantaympäristö Tietokantaympäristön yleinen, standardiin perustuva rakenne koostuu neljästä kerroksesta: Tietokantapalvelin eli tietokantainstanssi on prosessi tai prosessiperhe, joka vastaa tietokantojen ylläpitämisestä. Tietokantapalvelin sijaitsee laitteen keskusmuistissa. Tietokantapalvelin pitää yllä katalogeja, jotka sijaitsevat tavallisesti massamuistissa. Katalogit koostuvat skeemoista eli nimiavaruuksista. Jokainen katalogi sisältää lisäksi DBMS:n luoman skeeman, jossa on tietokannan järjestelmätaulut eli metadata. Skeemat sisältävät katalogin tietokantaobjektit kuten taulut ja näkymät. Tietokantapalvelin (instanssi) Katalogi Katalogi Skeema Järjestelmätaulut Skeema Skeema Järjestelmätaulut Skeema Taulu Taulu Taulu Triggeri Taulu Taulu Triggeri Taulu Skeema Taulu Taulu Taulu Taulu Taulu Taulu Skeema Taulu Taulu ITKA204 kevät 2016 Toni Taipalus Jyväskylän yliopisto 65
4.4 DDL: rakenteen määrittäminen Tietotyypit SQL-kieli on vahvasti tyypittävä, mikä tarkoittaa, että sarakkeelle annetaan tietotyyppi silloin, kun taulu luodaan. Yleisiä, standardin mukaisia tietotyyppejä ovat mm. Tietotyyppi Selite Käyttöesimerkki Esimerkkiarvo Esimerkkisarake CHAR, CHARACTER VARCHAR, CHARACTER VARYING Määrätyn mittainen merkkijono Vaihtelevan mittainen merkkijono CHAR(11) 010230-ABCD henkilötunnus VARCHAR(20) Aatami etunimi INTEGER, INT kokonaisluku INT 2000 syntymävuosi NUMERIC (desimaali)luku NUMERIC(7,2) 12072.54 kuukausipalkka BOOLEAN totuusarvo BOOLEAN true tilaus_vahvistettu DATE päivämäärä DATE 01 Jan 2016 tilauspvm TIMESTAMP aikaleima TIMESTAMP 01/01/2016 08:01:00+02 maksuaika ARRAY lista INTEGER[7] 1, 2, 9, 12, 19, 24, 25 lottonumerot ITKA204 kevät 2016 Toni Taipalus Jyväskylän yliopisto 66
4.4 DDL: rakenteen määrittäminen Taulujen luominen Taulu luodaan komennolla CREATE TABLE. Komennolla annetaan vähintään taulun nimi ja otsake tietotyyppeineen. Komennon yleinen syntaksi on seuraava: CREATE TABLE taulu ( sarake tietotyyppi[, sarake tietotyyppi]* ); Tavallisesti voidaan käyttää joko auki kirjoitettuja tietotyyppien nimiä tai lyhenteitä, esim. CHAR. CREATE TABLE tuote ( tuotetun CHARACTER(4), tuotenimi CHARACTER VARYING(15), malli CHARACTER VARYING(10), ahinta INTEGER, vari CHARACTER VARYING(10) ); TUOTE tuotetun CHAR(4) tuotenimi VARCHAR(15) malli VARCHAR(10) ahinta INT vari VARCHAR(10) ITKA204 kevät 2016 Toni Taipalus Jyväskylän yliopisto 67
4.4 DDL: rakenteen määrittäminen Perusavain Taululle voidaan määrittää perusavain joko taulun luontikomennolla tai myöhemmin muokkaamalla taulua. Taulun luontikomennolla perusavaimen lisääminen muuttaa syntaksia seuraavalla tavalla: CREATE TABLE taulu ( sarake tietotyyppi [PRIMARY KEY]?[, sarake tietotyyppi]* ); CREATE TABLE tuote ( tuotetun CHAR(4) PRIMARY KEY, tuotenimi VARCHAR(15) [...] ); Tai, jos perusavain koostuu useammasta kuin yhdestä sarakkeesta: CREATE TABLE taulu ( sarake tietotyyppi[, sarake tietotyyppi]*[, PRIMARY KEY (sarake[, sarake]*)]? ); CREATE TABLE lasku_rivi ( laskuno CHAR(4), tuotetun CHAR(4), maara INT, PRIMARY KEY (laskuno, tuotetun) ); ITKA204 kevät 2016 Toni Taipalus Jyväskylän yliopisto 68
4.4 DDL: rakenteen määrittäminen Viiteavain Viiteavain määritetään renkitaulun (viittaavan taulun) luonnin yhteydessä tai sitä myöhemmin muokkaamalla. Sarakkeiden täytyy olla samaa tietotyyppiä, merkkijonojen täytyy olla myös (maksimi)pituudeltaan samat. CREATE TABLE taulu ( sarake tietotyyppi[, sarake tietotyyppi]*[, PRIMARY KEY (sarake[, sarake]*)]?[, FOREIGN KEY (sarake[, sarake]*) REFERENCES isäntätaulu (sarake[, sarake]*)]* ); olettaen, että tuote- ja lasku-taulut on luotu aiemmin: CREATE TABLE lasku_rivi ( laskuno CHAR(4), tuotetun CHAR(4), maara INT, PRIMARY KEY (laskuno, tuotetun), FOREIGN KEY (laskuno) REFERENCES lasku (laskuno), FOREIGN KEY (tuotetun) REFERENCES tuote (tuotetun) ); ITKA204 kevät 2016 Toni Taipalus Jyväskylän yliopisto 69
4.4 DDL: rakenteen määrittäminen Viiteavain Lisämääreillä voidaan vaikuttaa siihen, miten toimitaan, kun isäntätaulun (viitatun taulun) viitatun sarakkeessa arvoissa tapahtuu muutoksia (UPDATE) tai rivejä poistetaan (DELETE). RESTRICT estetään muutokset ja poistot viitatussa taulussa. SET NULL asetetaan tyhjäarvo viittaavan taulun vastaaville riveille. SET DEFAULT asetetaan oletusarvo viittaavan taulun vastaaville riveille. CASCADE vyörytetään viitatun taulun muutokset ja poistot viittaavaan tauluun. CREATE TABLE lasku_rivi ( laskuno CHAR(4), tuotetun CHAR(4), maara INT, PRIMARY KEY (laskuno, tuotetun), FOREIGN KEY (laskuno) REFERENCES lasku (laskuno) ON UPDATE RESTRICT ON DELETE SET NULL, FOREIGN KEY (tuotetun) REFERENCES tuote (tuotetun) ON UPDATE RESTRICT ON DELETE RESTRICT ); ITKA204 kevät 2016 Toni Taipalus Jyväskylän yliopisto 70