SQL:n alkeet
Peruskomennot (Basic commands)
Tässä luvussa tutustumme tavallisimpiin SQL-komentoihin, joiden avulla voimme lisätä, hakea, muuttaa ja poistaa tietokannan sisältöä. Nämä komennot muodostavat perustan tietokannan käyttämiselle. Yleisesti alalla näitä toimintoja kutsutaan englaninninkielisillä nimillä. Create, Read, Update and Delete, eli lyhenteenä CRUD, muodostavat tietokannan perustoiminnot, erityisesti dokumentaatiossa.
Taulun luonti
Komento CREATE TABLE
luo taulun, jossa on halutut sarakkeet. Esimerkiksi seuraava komento luo taulun Products
, jossa on kolme saraketta:
CREATE TABLE Products (id INTEGER PRIMARY KEY, name TEXT, price INTEGER);
Voimme nimetä taulun ja sarakkeet haluamallamme tavalla. Tällä kurssilla käytäntönä on, että kirjoitamme taulun nimen suurella alkukirjaimella ja monikkomuotoisena. Sarakkeiden nimet puolestaan kirjoitamme pienellä alkukirjaimella.
Jokaisesta sarakkeesta ilmoitetaan nimen lisäksi tyyppi. Tässä taulussa sarakkeet id
ja price
ovat kokonaislukuja (INTEGER) ja sarake name
on merkkijono (TEXT). Sarake id
on lisäksi taulun pääavain (PRIMARY KEY), mikä tarkoittaa, että se yksilöi jokaisen taulun rivin ja voimme viitata sen avulla kätevästi mihin tahansa riviin.
Pääavain (Primary key)
Tietokannan taulun pääavain on jokin sarake (tai sarakkeiden yhdistelmä), joka yksilöi taulun jokaisen rivin eli millään kahdella rivillä ei ole samaa pääavainta. Käytännössä hyvin tavallinen valinta pääavaimeksi on kokonaislukumuotoinen id-numero.
Usein haluamme lisäksi, että id-numerolla on juokseva numerointi. Tämä tarkoittaa, että kun tauluun lisätään rivejä, ensimmäinen rivi saa automaattisesti id-numeron 1, toinen rivi saa id-numeron 2, jne.
Juoksevan numeroinnin toteuttaminen riippuu tietokantajärjestelmästä. Esimerkiksi SQLite-tietokannassa INTEGER PRIMARY KEY
-tyyppinen sarake saa automaattisesti juoksevan numeroinnin.
Tiedon lisääminen
Komento INSERT
lisää uuden rivin tauluun. Esimerkiksi seuraava komento lisää rivin äsken luomaamme tauluun Products
INSERT INTO Products (name,price) VALUES ('radish',7);
Tässä annamme arvot lisättävän rivin sarakkeille name
ja price
. Kun sarakkeessa id
on juokseva numerointi, se saa automaattisesti arvon 1, kun kyseessä on taulun ensimmäinen rivi. Niinpä tauluun ilmestyy seuraava rivi:
id name price---------- ---------- ----------1 radish 7
Jos emme anna arvoa jollekin sarakkeelle, se saa oletusarvon. Tavallisessa sarakkeessa oletusarvo on NULL
, mikä tarkoittaa tiedon puuttumista. Esimerkiksi seuraavassa komennossa emme anna arvoa sarakkeelle price
:
INSERT INTO Products (name) VALUES ('radish');
Tällöin tauluun ilmestyy rivi, jossa hinta on NULL
(eli tyhjä):
id name price---------- ---------- ----------1 radish
Esimerkkitaulu
Oletamme tämän osion tulevissa esimerkeissä, että olemme lisänneet tauluun Products
seuraavat viisi riviä:
INSERT INTO Products (name,price) VALUES ('radish',7);INSERT INTO Products (name,price) VALUES ('carrot',5);INSERT INTO Products (name,price) VALUES ('turnip',4);INSERT INTO Products (name,price) VALUES ('cucumber',8);INSERT INTO Products (name,price) VALUES ('celery',4);
Taulun sisältö on siis seuraavanlainen:
id name price---------- ---------- ----------1 radish 72 carrot 53 turnip 44 cucumber 85 celery 4
Tiedon hakeminen
Komento SELECT
suorittaa kyselyn (query) eli hakee tietoa taulusta. Yksinkertaisin tapa tehdä kysely on hakea kaikki tiedot taulusta:
SELECT * FROM Products;
Tässä tapauksessa kyselyn tulos on seuraava:
id name price---------- ---------- ----------1 radish 72 carrot 53 turnip 44 cucumber 85 celery 4
Kyselyssä tähti *
ilmaisee, että haluamme hakea kaikki sarakkeet. Kuitenkin voimme myös hakea vain osan sarakkeista. Esimerkiksi seuraava kysely hakee vain tuotteiden nimet:
SELECT name FROM Products;
Kyselyn tulos on seuraava:
name----------radishcarrotturnipcucumbercelery
Tämä kysely puolestaan hakee nimet ja hinnat:
SELECT name, price FROM Products;
Kyselyn tulos on nyt seuraavanlainen:
name price---------- ----------radish 7carrot 5turnip 4cucumber 8celery 4
Kyselyn tuloksena olevat rivit muodostavat taulun, jota kutsutaan nimellä tulostaulu (result set
). Sen sarakkeet ja rivit riippuvat kyselyn sisällöstä. Esimerkiksi äskeinen kysely loi tulostaulun, jossa on kaksi saraketta ja viisi riviä.
Tietokannan käsittelyssä esiintyy siis kahdenlaisia tauluja: tietokannassa kiinteästi olevia tauluja, joihin on tallennettu tietokannan sisältö, sekä kyselyjen muodostamia väliaikaisia tulostauluja, joiden tiedot on koostettu kiinteistä tauluista.
Hakuehdot (Search clauses)
Liittämällä SELECT
-kyselyyn WHERE
-osan voimme valita vain osan riveistä halutun ehdon perusteella. Esimerkiksi seuraava kysely hakee tiedot kurkusta:
SELECT * FROM Products WHERE name='cucumber';
Kyselyn tulos on seuraava:
id name price---------- ---------- ----------4 cucumber 8
Ehdoissa voi käyttää vertailuja ja sanoja AND
ja OR
samaan tapaan kuin ohjelmoinnissa. Esimerkiksi seuraava kysely etsii tuotteet, joiden hinta on välillä 4...6:
SELECT * FROM Products WHERE price>=4 AND price<=6;
Kyselyn tulos on seuraava:
id name price---------- ---------- ----------2 carrot 53 turnip 45 celery 4
Järjestäminen
Oletuksena kyselyn tuloksena olevien rivien järjestys voi olla mikä tahansa. Voimme kuitenkin määrittää halutun järjestyksen ORDER BY
-osan avulla. Esimerkiksi seuraava kysely hakee tuotteet järjestyksessä nimen mukaan:
SELECT * FROM Products ORDER BY name;
Kyselyn tulos on seuraava:
id name price---------- ---------- ----------2 carrot 55 celery 44 cucumber 81 radish 73 turnip 4
Järjestys on oletuksena pienimmästä suurimpaan (ASCENDING). Kuitenkin jos haluamme järjestyksen suurimmasta pienimpään, voimme lisätä sanan DESC
(DESCENDING) sarakkeen nimen jälkeen:
SELECT * FROM Products ORDER BY name DESC;
Tämän seurauksena kyselyn tulos on seuraava:
id name price---------- ---------- ----------3 turnip 41 radish 74 cucumber 85 celery 42 carrot 5
SQL-kielessä on myös avainsana ASC
, joka tarkoittaa nousevaa järjestystä. Seuraavat kyselyt toimivat siis samalla tavalla:
SELECT * FROM Products ORDER BY name;SELECT * FROM Products ORDER BY name ASC;
Käytännössä sanaa ASC
käytetään kuitenkin äärimmäisen harvoin.
Voimme myös järjestää rivejä usealla eri perusteella. Esimerkiksi seuraava kysely järjestää rivit ensisijaisesti kalleimmasta halvimpaan hinnan mukaan ja toissijaisesti aakkosjärjestykseen nimen mukaan:
SELECT * FROM Products ORDER BY price DESC, name;
Kyselyn tulos on seuraava:
id name price---------- ---------- ----------4 cucumber 81 radish 72 carrot 55 celery 43 turnip 4
Tässä tapauksessa turnipsi ja selleri järjestetään nimen mukaan, koska ne ovat yhtä kalliita.
Erilliset tulosrivit (Distinct result rows)
Joskus tulostaulussa voi olla useita samanlaisia rivejä. Näin käy esimerkiksi seuraavassa kyselyssä:
SELECT price FROM Products;
Koska kahden tuotteen hinta on 4, kahden tulosrivin sisältönä on 4:
price----------75484
Jos kuitenkin haluamme vain erilaiset tulosrivit, voimme lisätä kyselyyn sanan DISTINCT
:
SELECT DISTINCT price FROM Products;
Tämän seurauksena kyselyn tulos muuttuu näin:
price----------7548
Tiedon muuttaminen (Changing information)
Komento UPDATE
muuttaa taulun rivejä, jotka täsmäävät haluttuun ehtoon. Esimerkiksi seuraava komento muuttaa tuotteen turnip
hinnaksi 6:
UPDATE Products SET price=6 WHERE name='turnip';
Useita sarakkeita voi muuttaa yhdistämällä muutokset pilkuilla. Esimerkiksi seuraava komento muuttaa tuotteen turnip
nimeksi pineapple
ja hinnaksi 9:
UPDATE Products SET name='pineapple', price=9 WHERE name='turnip';
Muutos voidaan myös laskea aiemman arvon perusteella. Esimerkiksi seuraava komento kasvattaa turnipsin hintaa yhdellä:
UPDATE Products SET price=price+1 WHERE name='turnip';
Jos komennossa ei ole ehtoa, se vaikuttaa kaikkiin riveihin. Esimerkiksi seuraava komento kasvattaa jokaisen tuotteen hintaa yhdellä:
UPDATE Products SET price=3;
Tiedon poistaminen (Removing information)
Komento DELETE
poistaa taulusta rivit, jotka täsmäävät annettuun ehtoon. Esimerkiksi seuraava komento poistaa taulusta tuotteen nimeltä carrot
:
DELETE FROM Products WHERE name='carrot';
Kuten muuttamisessa, jos ehtoa ei ole, niin komento vaikuttaa kaikkiin riveihin. Seuraava komento siis poistaa kaikki tuotteet taulusta:
DELETE FROM Products;
Komento DROP TABLE
poistaa tietokannan taulun (ja kaiken sen sisällön). Esimerkiksi seuraava komento poistaa taulun Products
:
DROP TABLE Products;
Yhteenveto ja ryhmittely (Aggregate queries)
Yhteenvetokysely laskee jonkin yksittäisen arvon taulun riveistä, kuten taulun rivien määrän tai sarakkeen kaikkien arvojen summan. Tällaisen kyselyn tulostaulussa on vain yksi rivi. VOimme myös ryhmitellä rivejä sarakkeiden mukaan ja tehdä yhteenvetoja jokaisesta ryhmästä.
Koostefunktiot (Aggregate functions)
Yhteenvetokyselyn perustana on koostefunktio, joka laskee yhteenvetoarvon taulun riveistä. Tavallisimmat koostefunktiot ovat seuraavat:
name function--------- ---------------------------COUNT() laskee rivien määränSUM() laskee summan rivien arvoistaMIN() noutaa pienimmän arvonMAX() noutaa suurimman arvonAVG() laskee keskiarvon
Esimerkkejä
Tarkastellaan taas taulua Products
:
id name price---------- ---------- ----------1 radish 72 carrot 53 turnip 44 cucumber 85 celery 4
Seuraava kysely hakee taulun rivien määrän:
SELECT COUNT(*) FROM Products;
COUNT(*)----------5
Seuraava kysely hakee niiden rivien määrän, joissa hinta on 4:
SELECT COUNT(*) FROM Products WHERE price=4;
COUNT(*)----------2
Seuraava kysely puolestaan laskee summan tuotteiden hinnoista:
SELECT SUM(price) FROM Products;
SUM(price)----------28
Rivien valinta (Selecting rows)
Jos koostefunktion sisällä on tähti *
, kysely laskee kaikki rivit. Jos taas funktion sisällä on sarakkeen nimi, kysely laskee rivit, joissa sarakkeessa on arvo (eli sarake ei ole NULL
).
Tarkastellaan esimerkkinä seuraavaa taulua, jossa rivillä 3 ei ole hintaa:
id name price---------- ---------- ----------1 radish 72 turnip 43 cucumber4 celery 4
Seuraava kysely hakee rivien yhteismäärän:
SELECT COUNT(*) FROM Products;
COUNT(*)----------4
Seuraava kysely taas hakee niiden rivien määrän, joilla on hinta:
SELECT COUNT(price) FROM Products;
COUNT(price)------------3
Voimme myös käyttää sanaa DISTINCT
, jotta saamme laskettua, montako eri arvoa jossakin sarakkeessa on:
SELECT COUNT(DISTINCT price) FROM Products;
COUNT(DISTINCT price)---------------------2
Ryhmittely (Grouping)
Ryhmittelyn avulla voimme yhdistää rivikohtaista ja koostefunktion antamaa tietoa. Ideana on, että rivit jaetaan ryhmiin GROUP BY
-osassa annettujen sarakkeiden mukaan ja tämän jälkeen koostefunktion arvo lasketaan jokaiselle ryhmälle erikseen.
Tarkastellaan esimerkkinä seuraavaa taulua Sales
, jossa on eri vuosien myyntitietoja:
id product year amount---------- ---------- ---------- ----------1 radish 2017 1202 radish 2018 853 radish 2019 1504 turnip 2017 305 turnip 2018 356 turnip 2019 107 cucumber 2017 758 cucumber 2018 1009 cucumber 2019 80
Seuraava kysely palauttaa kokonaismyynnin jokaiselta vuodelta ryhmiteltynä:
SELECT year, SUM(amount) FROM Sales GROUP BY year;
The query returns as follows:
year SUM(amount)---------- ----------2017 2252018 2202019 240
Esimerkiksi vuoden 2017 kokonaismyynti on 120 + 30 + 75 = 225.
Toisaalta, voimme saada tuotteiden kokonaismyynnin näin:
SELECT product, SUM(amount) FROM Sales GROUP BY product;
Kyselyn tulos on seuraava:
product SUM(amount)---------- ----------cucumber 255turnip 75radish 355
Esimerkiksi kurkun kokonaismyynti on 75 + 100 + 80 = 255.
Tulossarakkeen nimentä
Oletuksena tulostaulun sarake saa nimen suoraan kyselyn perusteella, mutta voimme halutessamme antaa myös oman nimen AS
-sanan avulla. Tämän ansiosta voimme esimerkiksi selventää, mistä yhteenvetokyselyssä on kyse.
Esimerkiksi seuraavassa kyselyssä toisen sarakkeen nimeksi tulee total
:
SELECT product, SUM(amount) AS total FROM Sales GROUP BY product;
Kysely palauttaa seuraavaa:
product total---------- --------cucumber 255turnip 75radish 355
Itseasiassa sana AS
ei ole pakollinen, eli voisimme kirjoittaa kyselyn myös näin:
SELECT product, SUM(amount) total FROM Sales GROUP BY product;
Rajaus ryhmittelyn jälkeen (Limitation after grouping)
Voimme lisätä avainsanan HAVING
kyselyymme, joka rajoittaa tuloksia ryhmittelyn jälkeen. Esimerkiksi seuraava kysely palauttaa tuotteet, joiden myynti on vähintään 200:
SELECT product, SUM(amount) AS totalFROM SalesGROUP BY productHAVING total >= 200;
Kysely palauttaa seuraavasti:
product total---------- --------cucumber 255radish 355
Kyselyn yleiskuva (Query overview)
Kyselyissämme voimme käyttää useita avainsanoja joita olemme tähän mennessä oppineet, kunhan ne ovat seuraavassa järjestyksessä:
SELECT – FROM – WHERE – GROUP BY – HAVING – ORDER BY
Seuraavassa on esimerkki kyselystä, joka sisältää yhtä aikaa kaikki nämä osat.
SELECT product, SUM(amount) AS totalFROM SalesWHERE year < 2019GROUP BY productHAVING total >= 100ORDER BY product;
Kysely palauttaa tuotteiden myyntitiedot ennen vuotta 2019, näyttää ainoastaan tuotteet joiden myynti näinä vuosina on yli 100, ja järjestää ne nimen mukaan. Kysely palauttaa seuraavaa:
product total---------- --------cucumber 175radish 205
Huomaa ero WHERE
ja HAVING
välillä: WHERE
rajoittaa rivejä ennen ryhmittelyä, kun HAVING
rajoittaa tuloksia ryhmittelyn jälkeen.
SQLite-tietokanta
SQLite on yksinkertainen avoimesti saatavilla oleva tietokantajärjestelmä, joka soveltuu hyvin SQL-kielen opetteluun. Voit kokeilla helposti SQL-kieleen liittyviä asioita SQLiten avulla, ja käytämme sitä tämän kurssin esimerkeissä.
Tietokantajärjestelmät (Database systems)
SQLite on mainio valinta SQL-kielen harjoitteluun, mutta siinä on tiettyjä rajoituksia, jotka voivat aiheuttaa ongelmia todellisissa sovelluksissa.
Muita suosittuja avoimia tietokantajärjestelmiä ovat MySQL ja PostgreSQL. Niissä on suuri määrä ominaisuuksia, jotka puuttuvat SQLitestä, mutta toisaalta niiden asentaminen ja käyttäminen on vaikeampaa.
Eri tietokantajärjestelmien välillä siirtyminen on onneksi helppoa, koska kaikissa on samantapainen SQL-kieli.
SQLite-tulkki (SQLite interpreter)
SQLite-tulkki
on ohjelma, jonka kautta voidaan käyttää SQLite-tietokantaa. Tulkki käynnistyy antamalla komentorivillä komento sqlite3
. Tämän jälkeen tulkkiin voi kirjoittaa joko suoritettavia SQL-komentoja tai pisteellä alkavia SQLite-tulkin omia komentoja.
Jos käyttämälläsi koneella ei ole vielä SQLite-tulkkia, voit asentaa sen tästä: https://www.sqlite.org/download.html
Valitse oman käyttöjärjestelmäsi mukainen paketti, jonka vieressä on otsikko command-line tools (eli komentorivityökalut). Tarvittava tiedosto on se, jonka nimi alkaa sqlite3.
Esimerkki
SQLite-tulkissa tietokanta on oletuksena muistissa (in-memory database). Tämä tarkoittaa, että se on aluksi tyhjä ja katoaa, kun tulkki suljetaan. Tämä on hyvä tapa testailla SQL-kielen ominaisuuksia. Keskustelu tulkin kanssa voi näyttää vaikkapa tältä (tähän on lisätty ylimääräisiä rivinvaihtoja luettavuuden takia):
$ sqlite3SQLite version 3.11.0 2016-02-15 17:29:24Enter ".help" for usage hints.Connected to a transient in-memory database.Use ".open FILENAME" to reopen on a persistent database.sqlite> CREATE TABLE Products (id INTEGER PRIMARY KEY, name TEXT, price INTEGER);sqlite> .tablesProductssqlite> INSERT INTO Products (name,price) VALUES ('radish',7);sqlite> INSERT INTO Products (name,price) VALUES ('carrot',5);sqlite> INSERT INTO Products (name,price) VALUES ('turnip',4);sqlite> INSERT INTO Products (name,price) VALUES ('cucumber',8);sqlite> INSERT INTO Products (name,price) VALUES ('celery',4);sqlite> SELECT * FROM Products;1|radish|72|carrot|53|turnip|44|cucumber|85|celery|4sqlite> .mode columnsqlite> .headers onsqlite> SELECT * FROM Products;id name price---------- ---------- ----------1 radish 72 carrot 53 turnip 44 cucumber 85 celery 4sqlite> .quit
Esimerkissä luomme aluksi taulun Products
ja tarkastamme sitten komennolla .tables
, mitä tauluja tietokannassa on. Ainoa taulu on Products
, mikä kuuluu asiaan.
Tämän jälkeen lisäämme tauluun rivejä ja haemme sitten kaikki rivit taulusta. SQLite-tulkin oletustapa näyttää tulosrivit pystyviivoin erotettuina ei ole kovin tyylikäs, minkä vuoksi parannamme tulostusta komennoilla .mode column
(jokaisella sarakkeella on kiinteä leveys) ja .headers on
(sarakkeiden nimet näytetään).
Lopuksi suoritamme komennon .quit
, joka sulkee SQLite-tulkin.
Tietokanta tiedostossa
Käynnistyksen yhteydessä SQLite-tulkille voi antaa parametrina tiedoston, johon tietokanta tallennetaan. Tällöin tietokannan sisältö säilyy tallessa tulkin sulkemisen jälkeen.
Seuraavassa esimerkissä tietokanta tallennetaan tiedostoon test.db
. Tämän ansiosta tietokannan sisältö on edelleen tallessa, kun tulkki käynnistetään uudestaan.
$ sqlite3 test.dbSQLite version 3.11.0 2016-02-15 17:29:24Enter ".help" for usage hints.sqlite> CREATE TABLE Products (id INTEGER PRIMARY KEY, name TEXT, price INTEGER);sqlite> .tablesProductssqlite> .quit$ sqlite3 test.dbSQLite version 3.11.0 2016-02-15 17:29:24Enter ".help" for usage hints.sqlite> .tablesProductssqlite> .quit
Komennot tiedostosta
Voimme myös ohjata SQLite-tulkille tiedoston, jossa olevat komennot suoritetaan peräkkäin. Tämän avulla voimme automatisoida komentojen suorittamista. Esimerkiksi voimme laatia seuraavan tiedoston commands.sql
:
CREATE TABLE Products (id INTEGER PRIMARY KEY, name TEXT, price INTEGER);INSERT INTO Products (name,price) VALUES ('radish',7);INSERT INTO Products (name,price) VALUES ('carrot',5);INSERT INTO Products (name,price) VALUES ('turnip',4);INSERT INTO Products (name,price) VALUES ('cucumber',8);INSERT INTO Products (name,price) VALUES ('celery',4);.mode column.headers onSELECT * FROM Products;
Tämän jälkeen voimme ohjata komennot tiedostosta tulkille näin:
$ sqlite3 < commands.sqlid name price---------- ---------- ----------1 radish 72 carrot 53 turnip 44 cucumber 85 celery 4