3. blokk · Lekérdezések — SELECT · 7b. témakör
Eddig egyes sorokat kérdeztünk le — most összesítünk. Hány termék van?
Mennyi az átlagár kategóriánként? Az aggregáló függvények (COUNT, SUM, AVG,
MIN, MAX), a GROUP BY csoportosítás és a
HAVING csoportszűrés a riportok és statisztikák alapja — végig
élőben kipróbálhatóan.
Az aggregáló (összesítő) függvények sok sorból egyetlen értéket számolnak ki. Ahelyett, hogy az egyes termékeket látnánk, a függvény az egész halmazról ad egy összesített számot (darab, összeg, átlag…).
| Függvény | Mit számol | Példa |
|---|---|---|
COUNT() | Sorok száma | Hány termék van? |
SUM() | Értékek összege | Összes készlet darabszáma |
AVG() | Átlag | Átlagos ár |
MIN() | Legkisebb érték | Legolcsóbb termék ára |
MAX() | Legnagyobb érték | Legdrágább termék ára |
-- Egyetlen összesített sor a teljes táblából: SELECT COUNT(*) AS termekek_szama, AVG(ar) AS atlagar, MAX(ar) AS legdragabb FROM termek;
A COUNT sorokat számol, de fontos a háromféle változata:
| Forma | Mit számol |
|---|---|
COUNT(*) | Az összes sort (a NULL-okat is). |
COUNT(oszlop) | Csak azokat a sorokat, ahol az adott oszlop nem NULL. |
COUNT(DISTINCT oszlop) | Az oszlop különböző (egyedi) értékeinek számát (a NULL-t ez sem számolja). |
-- Hány termékhez van egyáltalán megadva kategória? (a NULL-okat kihagyja) SELECT COUNT(kategoria_id) FROM termek; -- Hány KÜLÖNBÖZŐ kategóriában van termék? SELECT COUNT(DISTINCT kategoria_id) FROM termek;
Ha az oszlopban van NULL, a COUNT(oszlop) kevesebbet adhat, mint
a COUNT(*)! Ha „hány sor van” a kérdés, használj
COUNT(*)-ot.
Ezek számértékeken dolgoznak (a MIN/MAX
szövegen és dátumon is). A NULL értékeket figyelmen kívül hagyják.
-- A 3-as kategória árstatisztikái: SELECT SUM(keszlet) AS ossz_keszlet, AVG(ar) AS atlagar, MIN(ar) AS legolcsobb, MAX(ar) AS legdragabb FROM termek WHERE kategoria_id = 3;
Az AVG gyakran sok tizedesjegyet ad (pl.
73052.5). A ROUND(érték, tizedesjegyek)
függvény a kívánt pontosságra kerekít. A második paraméter a megtartandó tizedesjegyek száma —
ha elhagyod, egész számra kerekít.
-- Átlagár egész forintra kerekítve: SELECT ROUND(AVG(ar)) AS atlagar FROM termek; -- Ugyanaz, két tizedesjegyre kerekítve: SELECT ROUND(AVG(ar), 2) AS atlagar FROM termek; -- A ROUND önállóan is működik, nem csak aggregátumon (pl. bruttó ár): SELECT nev, ROUND(ar * 1.27) AS brutto FROM termek;
Felfelé kerekítés: CEIL() (vagy
CEILING()); lefelé: FLOOR();
tizedesjegyek levágása kerekítés nélkül: TRUNCATE(érték, n).
Pénznél jellemzően a ROUND(..., 2) a jó választás. (A lenti
gyakorlóban a ROUND, CEIL és FLOOR működik; a TRUNCATE MySQL-függvény, ott
élesben próbáld ki.)
Eddig a teljes táblát összesítettük egyetlen sorba. A GROUP BY
csoportokra bontja a sorokat (pl. kategóriánként), és az aggregáló függvény
minden csoportra külön lefut. Így kapunk csoportonkénti darabszámot, átlagot
stb.
-- Hány termék van az egyes kategóriákban? SELECT kategoria_id, COUNT(*) AS db FROM termek GROUP BY kategoria_id; -- Átlag-, min- és maxár kategóriánként: SELECT kategoria_id, COUNT(*) AS db, ROUND(AVG(ar)) AS atlagar, MIN(ar) AS legolcsobb, MAX(ar) AS legdragabb FROM termek GROUP BY kategoria_id;
„Csoportonként” → GROUP BY. Ha a kérdésben szerepel az
„...onként / ...enként” (kategóriánként, vásárlónként, naponta), szinte biztos,
hogy GROUP BY kell hozzá.
Ha van GROUP BY, akkor a SELECT-ben
szereplő minden oszlop vagy szerepel a GROUP BY-ban, vagy
aggregáló függvényben van. Különben a szerver nem tudja, melyik értéket
mutassa a csoportból.
-- HIBÁS: a nev nincs sem a GROUP BY-ban, sem aggregálva SELECT kategoria_id, nev, COUNT(*) FROM termek GROUP BY kategoria_id; -- melyik nevet mutassa a sok termékből?? -- HELYES: csak csoportosító oszlop + aggregátum SELECT kategoria_id, COUNT(*) AS db FROM termek GROUP BY kategoria_id;
A MySQL az 5.7-es verzió óta alapból szigorú: az
ONLY_FULL_GROUP_BY mód be van kapcsolva, így a fenti hibás
lekérdezésre hibaüzenetet kapsz. Régebbi verziókon (vagy kikapcsolt módnál) a
szerver csendben egy „véletlenszerű” értéket adott vissza a csoportból — ez csendes hibák
forrása volt. A lenti SQLite-gyakorló szintén elnéző: futtasd le, és látni fogod, hogy
önkényesen kiválasztott nevet mutat! Éles MySQL-en viszont hibát kapnál — tartsd be a
szabályt mindig.
A WHERE az egyes sorokat szűri — de aggregátumra
(pl. COUNT(*) > 2) nem hivatkozhatunk benne. Erre való a
HAVING: a csoportokat szűri, az aggregálás
után.
-- Mely kategóriákban van 1-nél több termék? SELECT kategoria_id, COUNT(*) AS db FROM termek GROUP BY kategoria_id HAVING COUNT(*) > 1; -- Mely kategóriák átlagára haladja meg a 20 000 Ft-ot? SELECT kategoria_id, ROUND(AVG(ar)) AS atlagar FROM termek GROUP BY kategoria_id HAVING AVG(ar) > 20000;
| WHERE | HAVING | |
|---|---|---|
| Mit szűr | Egyes sorokat | Csoportokat |
| Mikor fut | Az aggregálás előtt | Az aggregálás után |
| Használhat aggregátumot? | Nem | Igen |
A kettő együtt is használható: előbb a WHERE kiszűri a nem kívánt
sorokat, majd a GROUP BY csoportosít, végül a
HAVING szűri a csoportokat.
-- Csak a készleten lévő termékeket nézzük (WHERE), majd a 8000 Ft feletti -- átlagárú kategóriákat tartjuk meg (HAVING): SELECT kategoria_id, COUNT(*) AS db, ROUND(AVG(ar)) AS atlagar FROM termek WHERE keszlet > 0 GROUP BY kategoria_id HAVING AVG(ar) > 8000;
A kulcsszavak kötött sorrendje:
SELECT … FROM … WHERE … GROUP BY … HAVING … ORDER BY … LIMIT.
Csoportosított eredményt is rendezhetsz ORDER BY-jal, akár az
aggregátum szerint (pl. ORDER BY db DESC).
Összesíts a webshopon! A chipek aggregáló és csoportosító mintákat töltenek be — futtasd,
majd alakítsd át (pl. más oszlop szerint csoportosíts, vagy módosítsd a
HAVING feltételt).
A motor SQLite-alapú (a böngészőben fut). Az aggregáló függvények, a
GROUP BY és a HAVING gyakorlatilag
azonosak a MySQL-lel. (Internetkapcsolat szükséges a motor betöltéséhez.)
⭐ alap · ⭐⭐ haladó · ⭐⭐⭐ kihívás — a kódírós feladatokat futtasd is le a fenti élő gyakorlóban!
Sorold fel az öt alapvető aggregáló függvényt!
Egészítsd ki: a WHERE az ____________ szűri, a
HAVING pedig a ____________.
Hogyan kapod meg, hány különböző kategóriában van termék? Futtasd is le!
Mit ad vissza ez a lekérdezés? Előbb gondold végig, aztán futtasd le!
SELECT kategoria_id, COUNT(*) AS db FROM termek GROUP BY kategoria_id;
Miért hibás (vagy megbízhatatlan) ez a lekérdezés?
SELECT kategoria_id, nev, COUNT(*) FROM termek GROUP BY kategoria_id;
Mi a különbség a két lekérdezés eredménye között? (Futtasd le mindkettőt!)
SELECT kategoria_id, COUNT(*) FROM termek GROUP BY kategoria_id HAVING COUNT(*) > 1; SELECT kategoria_id, COUNT(*) FROM termek WHERE keszlet > 0 GROUP BY kategoria_id;
Kódírás: írj lekérdezést, amely megadja a termékek átlagárát és a legdrágább termék árát!
Kódírás: add meg kategóriánként a termékek darabszámát és átlagárát, a darabszám szerint csökkenő sorrendben!
Kódírás: listázd azokat a kategóriákat, ahol a termékek átlagára meghaladja a 20 000 Ft-ot — csak a készleten lévő termékeket (keszlet > 0) figyelembe véve!
Kódírás: a rendeles_tetel táblából add meg
rendelésenként a rendelés végösszegét (mennyiség × egységár összege), a legnagyobb
összegtől kezdve!
Minden kérdésnél egy válasz helyes. A végén pontszámot kapsz.
| Fogalom | Jelentés |
|---|---|
| aggregáló függvény | sok sorból egy összesített értéket számol |
| COUNT(*) · COUNT(oszlop) · COUNT(DISTINCT) | minden sor · a nem-NULL sorok · az egyedi értékek száma |
| SUM · AVG · MIN · MAX | összeg · átlag · legkisebb · legnagyobb (NULL-okat kihagyva) |
| ROUND(x, n) | kerekítés n tizedesjegyre (n nélkül egészre) |
| CEIL · FLOOR · TRUNCATE | felfelé · lefelé kerekítés · tizedesjegyek levágása |
| GROUP BY | csoportokra bontás — az aggregátum csoportonként számolódik |
| SELECT-lista szabálya | minden oszlop a GROUP BY-ban van vagy aggregálva |
| ONLY_FULL_GROUP_BY | a MySQL szigorú módja (5.7 óta alapértelmezett) — hibát dob a szabály megsértésére |
| HAVING | a csoportok szűrése az aggregálás után — aggregátumra is |
| záradéksorrend | SELECT → FROM → WHERE → GROUP BY → HAVING → ORDER BY → LIMIT |
COUNT,
SUM, AVG,
MIN, MAX.COUNT(*) minden sort, COUNT(oszlop)
a nem-NULL sorokat, COUNT(DISTINCT …) az egyedi értékeket
számolja.GROUP BY csoportokra bontja a sorokat; az aggregátum
csoportonként számolódik.HAVING a csoportokat szűri (aggregátumra is), a
WHERE az egyes sorokat (aggregátum nélkül).WHERE → GROUP BY → HAVING → ORDER BY.