README.md 8.37 KB
Newer Older
M1888's avatar
M1888 committed
1 2
# ttzc0800-harkka

M1888's avatar
M1888 committed
3 4
Tietokannat (TTZC0800) kurssin harkkatyö

M1888's avatar
M1888 committed
5
## Suunnitelmavaihe
M1888's avatar
M1888 committed
6 7 8 9 10

**[Vaatimusmäärittelyyn](vaatimusmaarittely.md)** tästä.

Vaatimusmäärittelyn pohjalta nousi ainakin seuraavanlaisia käsitteitä:

M1888's avatar
M1888 committed
11 12 13 14 15 16
- Kaupunki (sisältää areenoja, joukkueilla kotikaupunki)
- Areena (sisältää otteluita)
- Joukkue (sisältää pelaajia)
- Pelaaja (kuuluu joukkueeseen)
- Ottelu (sisältää tapahtumia)
- Tapahtumat (maali & jäähy, eri tauluista)
M1888's avatar
M1888 committed
17

M1888's avatar
M1888 committed
18
## Toteutus
M1888's avatar
M1888 committed
19

M1888's avatar
M1888 committed
20 21
### 1. [create-kendokanta.sql](create-kendokanta.sql)

M1888's avatar
M1888 committed
22 23 24 25 26 27 28 29 30 31
Kun kannan skeema oli vihdoin kunnossa, oli aika alkaa työstämään rajoittimia ja triggereitä. Yhden hienon CHECK constraintin ehdin jo kirjoittaa, kunnes muistin, että ne eivät MySQL:ssä tehneetkään mitään.

```SQL
  CONSTRAINT `CK_tapahtumaid` CHECK (
    CASE WHEN aikalisa_id IS NULL THEN 0 ELSE 1 END +
    CASE WHEN jaahy_id    IS NULL THEN 0 ELSE 1 END +
    CASE WHEN rankkari_id IS NULL THEN 0 ELSE 1 END +
    CASE WHEN maali_id    IS NULL THEN 0 ELSE 1 END = 1)
```

M1888's avatar
M1888 committed
32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103
CHECKin kaltaiset rajoitteet pitikin siis toteuttaa triggereillä. Ensimmäinen vedos ylläolevasta oli tällainen:

```SQL
-- Sama trigger, kun ylempi constraint. 
DELIMITER //
CREATE TRIGGER `vain_yksi_tapahtuma_ins` BEFORE INSERT ON `ottelutapahtuma`
    FOR EACH ROW
    BEGIN
        IF ((CASE WHEN NEW.aikalisa_id IS NULL THEN 0 ELSE 1 END) +
            (CASE WHEN NEW.jaahy_id    IS NULL THEN 0 ELSE 1 END) +
            (CASE WHEN NEW.rankkari_id IS NULL THEN 0 ELSE 1 END) +
            (CASE WHEN NEW.maali_id    IS NULL THEN 0 ELSE 1 END) != 1) THEN
          SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = 'Liikaa ottelutapahtumia';
        END IF;
    END//

DELIMITER ;
```

Vaan Student-palvelimen MySQL ei tuntenut [SIGNAL SQLSTATE](https://dev.mysql.com/doc/refman/5.5/en/signal.html) -määrettä. Tällä voitaisiin kertoa virheistä, mutta tuki on tullut vasta versiossa 5.5, kun student-palvelimella on MySQL 5.1.

Lopulliseen versioon päästiin korvaamalla SIGNAL SQLSTATE -rivi kutsumalla funktiota, jota ei ole olemassa ja näin saadaan virheilmoitus käyttäjälle, vaikkakin vähän kankeasti.

```SQL
-- Sama trigger, kun ylempi constraint.
DELIMITER //
CREATE TRIGGER `vain_yksi_tapahtuma_ins` BEFORE INSERT ON `ottelutapahtuma`
    FOR EACH ROW
    BEGIN
        IF ((CASE WHEN NEW.aikalisa_id IS NULL THEN 0 ELSE 1 END) +
            (CASE WHEN NEW.jaahy_id    IS NULL THEN 0 ELSE 1 END) +
            (CASE WHEN NEW.rankkari_id IS NULL THEN 0 ELSE 1 END) +
            (CASE WHEN NEW.maali_id    IS NULL THEN 0 ELSE 1 END) != 1) THEN
          CALL `ottelutapahtuma_liikaa_tapahtumia`;
        END IF;
    END//

DELIMITER ;
```

Tämä näyttikin toimivan hyvin:

```SQL
mysql> INSERT INTO ottelutapahtuma (ottelu_id, jaahy_id, maali_id, aika) VALUES (1, 1, 1, 5);
ERROR 1305 (42000): PROCEDURE M1888.ottelutapahtuma_liikaa_tapahtumia does not exist
mysql> INSERT INTO ottelutapahtuma (ottelu_id, aika) VALUES (1, 1);
ERROR 1305 (42000): PROCEDURE M1888.ottelutapahtuma_liikaa_tapahtumia does not exist
```

Seuraavaksi lähdin toteuttamaan lisää triggereitä ottelutapahtuma-taululle. Ensimmäisenä oli tarkoitus varmistaa, että aikalisän ottaa ottelussa joukkue, joka myöskin ottaa osaa kyseiseen otteluun.

```SQL
-- Aikalisän voi pitää vain joukkue, joka osallistuu kyseiseen otteluun
CREATE TRIGGER `aikalisa_oikeat_tiimit_ins` BEFORE INSERT ON `ottelutapahtuma`
    FOR EACH ROW
    BEGIN
      IF (NEW.aikalisa_id IS NOT NULL) THEN
        IF (SELECT joukkue_id FROM aikalisa WHERE aikalisa_id = NEW.aikalisa_id)
          NOT IN (SELECT kotijoukkue, vierasjoukkue FROM ottelu WHERE ottelu_id = NEW.ottelu_id) THEN
          CALL `aikalisa_vaara_joukkue`;
        END IF;
      END IF;
    END//
```

Mutta taas palvelimen vanha MySQL-versio taisteli vastaan:

```SQL
ERROR 1235 (42000): This version of MySQL doesn't yet support 'multiple triggers with the same action time and event for one table'
```

Kaikki validaatio piti siis tehdä yhden triggerin sisään. Lopulta siitä tuli tällainen hirvitys:
M1888's avatar
M1888 committed
104

M1888's avatar
M1888 committed
105 106
### 2. [insert-teams.sql](insert-teams.sql)

M1888's avatar
M1888 committed
107 108 109 110 111 112 113 114 115 116 117 118 119 120
Alkuun lisättiin neljä kaupunkia, niiden areenat ja joukkueet.

| Kaupunki | Joukkue | Areena |
|---|---|---|
| Jyväskylä | JYP | LähiTapiola Areena |
| Kuopio | KalPa | Niiralan monttu |
| Tampere | Tappara | Hakametsän jäähalli |
| Pori | Ässät | Isomäki Areena |

Jokaiselle joukkueelle pyrin lisäämään seitsemän pelaajaa - kolme hyökkääjää, kolme puolustajaa sekä yhden maalivahdin.

Näiden välille lähdettiin sitten lisäämään otteluita. Ensimmäisen keksin päästä, ja kahteen jälkimmäiseen totesin helpommaksi käyttää lähteenä oikeita ottelutapahtumia näiden joukkueiden välisistä peleistä.

| Ottelu | Kotijoukkue | Vierasjoukkue | Tulos |
M1888's avatar
M1888 committed
121
|:-:|---|---|:-:|
M1888's avatar
M1888 committed
122 123 124 125 126 127
| 1 | JYP | KalPa | 3-2 |
| 2 | Tappara | Ässät | 4-3 |
| 3 | Ässät | KalPa | 3-2 |

Kun ottelut oli saatu lisättyä, oli aika lähteä työstämään erilaisia kyselyitä kannasta.

M1888's avatar
M1888 committed
128
### [views.sql](views.sql)
M1888's avatar
M1888 committed
129

M1888's avatar
M1888 committed
130
Näin saatiinkin aikaiseksi kätevät pistepörssit joukkueille ja pelaajille, sekä statistiikkaa jäähyminuuteista.
M1888's avatar
M1888 committed
131

M1888's avatar
M1888 committed
132
```SQL
M1888's avatar
M1888 committed
133
mysql> SELECT * FROM Pisteporssi ORDER BY pts DESC;
M1888's avatar
M1888 committed
134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179
+------------+--------+-------------+-----+
| pelaaja_id | enimi  | snimi       | pts |
+------------+--------+-------------+-----+
|         24 | Niklas | Appelgren   |   3 |
|         10 | Eetu   | Luostarinen |   3 |
|          1 | David  | Tomasek     |   2 |
|          3 | Anton  | Stråka     |   2 |
|          8 | Balazs | Sebok       |   2 |
|         18 | Teemu  | Suhonen     |   2 |
|         11 | Mikael | Seppälä   |   2 |
|         22 | Aleksi | Rekonen     |   2 |
|         23 | Lenni  | Killinen    |   2 |
|         15 | Matti  | Järvinen   |   2 |
|         16 | Niko   | Ojamäki    |   1 |
|         25 | Saku   | Salmela     |   1 |
|         17 | Patrik | Puistola    |   1 |
|         26 | Aleksi | Varttinen   |   1 |
|         27 | Juuso  | Ketola      |   1 |
|         20 | Hugo   | Haš        |   1 |
|         12 | Joona  | Riekkinen   |   1 |
+------------+--------+-------------+-----+
17 rows in set (0.01 sec)

mysql> SELECT * FROM Joukkue_maalit;
+-----------+------------+--------+
| ottelu_id | joukkue_id | maalit |
+-----------+------------+--------+
|         1 |          1 |      3 |
|         1 |          2 |      2 |
|         3 |          2 |      2 |
|         2 |          3 |      4 |
|         2 |          4 |      3 |
|         3 |          4 |      3 |
+-----------+------------+--------+
6 rows in set (0.01 sec)

mysql> SELECT * FROM Voittajat;
+-----------+------------+---------+--------+
| ottelu_id | joukkue_id | nimi    | maalit |
+-----------+------------+---------+--------+
|         1 |          1 | JYP     |      3 |
|         2 |          3 | Tappara |      4 |
|         3 |          2 | KalPa   |      3 |
+-----------+------------+---------+--------+
3 rows in set (0.00 sec)

M1888's avatar
M1888 committed
180
mysql> SELECT * FROM Pisteporssi_joukkue ORDER BY pts DESC;
M1888's avatar
M1888 committed
181 182 183 184 185 186 187 188 189
+---------+-----+
| nimi    | pts |
+---------+-----+
| JYP     |   1 |
| KalPa   |   1 |
| Tappara |   1 |
+---------+-----+
3 rows in set (0.01 sec)

M1888's avatar
M1888 committed
190
mysql> SELECT * FROM Jaahyt_pelaajat ORDER BY minuutit DESC;
M1888's avatar
M1888 committed
191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210
+------------+----------+-------------+----------+
| pelaaja_id | enimi    | snimi       | minuutit |
+------------+----------+-------------+----------+
|         12 | Joona    | Riekkinen   | 00:07:00 |
|         23 | Lenni    | Killinen    | 00:06:00 |
|         19 | Valtteri | Kemiläinen | 00:04:00 |
|         26 | Aleksi   | Varttinen   | 00:04:00 |
|          1 | David    | Tomasek     | 00:02:00 |
|          7 | Niko     | Parkkinen   | 00:02:00 |
|          3 | Anton    | Stråka     | 00:02:00 |
|         11 | Mikael   | Seppälä   | 00:02:00 |
|         17 | Patrik   | Puistola    | 00:02:00 |
|          9 | Aleksi   | Klemetti    | 00:02:00 |
|          4 | Anttoni  | Honka       | 00:02:00 |
|         28 | Niklas   | Peltomäki  | 00:02:00 |
|          5 | Joonas   | Viinikainen | 00:02:00 |
|         20 | Hugo     | Haš        | 00:02:00 |
+------------+----------+-------------+----------+
14 rows in set (0.01 sec)

M1888's avatar
M1888 committed
211
mysql> SELECT * FROM Jaahyt_joukkue ORDER BY minuutit DESC;
M1888's avatar
M1888 committed
212 213 214 215 216 217 218 219 220
+------------+---------+----------+
| joukkue_id | nimi    | minuutit |
+------------+---------+----------+
|          4 | Ässät | 00:12:00 |
|          2 | KalPa   | 00:11:00 |
|          1 | JYP     | 00:10:00 |
|          3 | Tappara | 00:08:00 |
+------------+---------+----------+
4 rows in set (0.03 sec)
M1888's avatar
M1888 committed
221
```