Miks mõnikord peab koos kokkuvõttefunktsiooni kasutamisega kasutama GROUP BY klauslit ja mõnikord ei pea?

Postitas Erki Eessaar 13.06.2024 15:47 (muudeti 25.06.2024 14:45)
Järgnevad koodinäited põhinevad enamasti MS Accessil, kuid kirjeldatud põhimõtted kehtivad SQLis üldiselt.

Oletame, et ülesandeks on leida iga aasta kohta, millal on alanud mõni reserveerimine, sellel aastal alanud reserveerimiste arv.

SELECT Year(alguse_aeg) AS aasta, Count(*) AS arv
FROM Reserveerimine;
 
See lause on vale, sest puudub eeskiri (GROUP BY klauslis), kuidas moodustada ridade rühmad, milles olevad read tuleb kokku loendada. Kuna SELECT klauslis on veerg, millele kokkuvõttefunktsiooni ei rakendata (aasta), siis ei saa eeldada, et kõik tabeli read on selleks paigutatud ühte rühma. 

Teiste sõnadega, kui SELECT klauslis on kokkuvõttefunktsioonide väljakutseid + üks või mitu veergu, millele ei rakendu kokkuvõttefunktsioon, siis peavad kõik need veerud olema ka nimetatud GROUP BY klauslis.

Õige oleks

SELECT Year(alguse_aeg) AS aasta, Count(*) AS arv
FROM Reserveerimine
GROUP BY Year(alguse_aeg);

PostgreSQLis saab GROUP BY klasuslis kasutada ka veerule SELECT klauslis määratud aliast.

SELECT Extract(year from alguse_aeg) AS aasta, Count(*) AS arv 
FROM Reserveerimine 
GROUP BY aasta;
 
Kurioosumina võib välja tuua, et populaarset andmebaasisüsteemi MySQL saab panna tööle erinevates režiimides, sh sellises, mis lubab käivitada algselt välja toodud lauset. Vaikimisi seda lauset MySQL 8-s ei täideta.

SET SESSION sql_mode = 'traditional';
 
SELECT Year(alguse_aeg) AS aasta, Count(*) AS arv
FROM Reserveerimine;

Lause täidetakse. Tulemuseks on tabel, kus on üks rida ja kaks veergu. Väljas aasta on üks (esimene?) aasta , väljas arv on kõikide reserveerimiste koguarv.
 
SET SESSION sql_mode = 'ansi';
 
SELECT Year(alguse_aeg) AS aasta, Count(*) AS arv
FROM Reserveerimine;

Lauset ei täideta.

Järgnevas lauses aga pole GROUP BY klauslit vaja, sest KÕIKIDESSE tulemusse kuuluvatesse veergudesse leitakse andmed kokkuvõttefunktsiooniga. Kuna SELECT klauslis pole ühtegi kokkuvõttefunktsiooniga katmata veergu, siis saab eeldada, et ridade rühma, mille järgi koondväärtused leitakse, moodustavad kõik tabeli read.

SELECT Round(Avg(lopu_aeg - alguse_aeg),2) AS avg_pikkus,
Min(lopu_aeg - alguse_aeg) AS min_pikkus,
Max(lopu_aeg - alguse_aeg) AS max_pikkus
FROM Reserveerimine AS R, Hotell AS H
WHERE R.hotelli_nr = H.hotelli_nr
AND linn = 'Tallinn'
AND lopu_aeg - alguse_aeg > 2
 
Eelnev näide on samaväärne selle lausega - rühmitamine e grupeerimine tühjal veergude hulgal e kõik tabeli read kuuluvad ühte rühma. Sellist süntaksi SQL standard lubab, aga MS Access ei toeta.
 
SELECT Round(Avg(lopu_aeg - alguse_aeg),2) AS avg_pikkus,
Min(lopu_aeg - alguse_aeg) AS min_pikkus,
Max(lopu_aeg - alguse_aeg) AS max_pikkus
FROM Reserveerimine AS R, Hotell AS H
WHERE R.hotelli_nr = H.hotelli_nr
AND linn = 'Tallinn'
AND lopu_aeg - alguse_aeg > 2
GROUP BY ();

Hinda postitust:

Keskmine hinne : Pole veel hinnanguid!