SQL GROUP BY i Funkcje Agregujące (COUNT, SUM, AVG)
Do tej pory wiesz, jak pobierać dane (SELECT) i je łączyć (JOIN). Ale co, jeśli zadanie nie pyta 'kto kupił produkt', tylko 'ILE produktów kupił KAŻDY klient?' Albo 'jaka jest ŚREDNIA cena w KAŻDEJ kategorii?' Do tego służy GROUP BY. Jest to jedna z najpotężniejszych koncepcji w SQL, pozwalająca 'zwinąć' (zagregować) wiele wierszy w jeden wiersz podsumowujący. Zamiast widzieć 1000 pojedynczych zamówień, możesz zobaczyć 5 wierszy ze sumą zamówień dla każdego z 5 klientów. Na maturze zadania z GROUP BY pojawiają się bardzo często.
Dlaczego to ważne? Na maturze zadania typu 'policz...', 'znajdź sumę...', 'oblicz średnią dla każdego...' to standard. GROUP BY w połączeniu z funkcjami agregującymi (COUNT, SUM, AVG) to Twój podstawowy zestaw narzędzi do tworzenia podsumowań i statystyk, o które CKE prosi w niemal każdym arkuszu z bazą danych.
Teoria
Klauzula GROUP BY grupuje wiersze, które mają te same wartości w określonych kolumnach, w jeden wiersz podsumowujący. Prawie zawsze używa się jej z funkcjami agregującymi, które wykonują obliczenia na każdej grupie.
Jak to działa?
- Funkcje Agregujące (co liczymy):
COUNT(*)lubCOUNT(kolumna): Zlicza wiersze w grupie.SUM(kolumna): Oblicza sumę wartości w danej kolumnie dla grupy.AVG(kolumna): Oblicza średnią arytmetyczną w grupie.MIN(kolumna): Znajduje najmniejszą wartość w grupie.MAX(kolumna): Znajduje największą wartość w grupie.- Klauzule (jak liczymy):
GROUP BY [kolumna]- Mówi bazie, jak tworzyć grupy. Np.GROUP BY klasastworzy osobne 'worki' na uczniów z '1A', '1B', '2A' itd.HAVING [warunek]- To jest 'WHERE' dla grup. Filtruje wyniki po zgrupowaniu. Np.HAVING COUNT(*) > 20pokaże tylko te klasy, które mają więcej niż 20 uczniów.- Złota Zasada SQL: Jeśli w zapytaniu jest
GROUP BY, to w klauzuliSELECTmogą znaleźć się tylko: 1) kolumny wymienione wGROUP BYlub 2) funkcje agregujące.
Złożoność: Nie dotyczy w sensie O(n). Wydajność zależy od indeksowania kolumny, po której grupujemy. Na potrzeby matury, każde poprawnie napisane zapytanie GROUP BY będzie wystarczająco szybkie.
Implementacja
COUNT(*) - Zliczanie wierszy w grupach
SQL-- Tabela: Uczniowie (id, imie, klasa)
-- Pytanie: Ilu uczniów jest w każdej klasie?
SELECT klasa, COUNT(*) AS LiczbaUczniow
FROM Uczniowie
GROUP BY klasa;`GROUP BY klasa` tworzy grupy ('1A', '1B', '2A'...). `COUNT(*)` zlicza, ile wierszy (uczniów) wpadło do każdej grupy. `AS` pozwala nadać ładną nazwę nowej kolumnie.
SUM() i AVG() - Sumowanie i obliczanie średniej
SQL-- Tabela: Produkty (id, nazwa, kategoria, cena)
-- Pytanie: Jaka jest łączna wartość i średnia cena produktów w każdej kategorii?
SELECT kategoria, SUM(cena) AS LacznaWartosc, AVG(cena) AS SredniaCena
FROM Produkty
GROUP BY kategoria;Podobnie, grupujemy po kategorii. `SUM(cena)` dodaje ceny wszystkich produktów w danej grupie (np. 'Nabiał'), a `AVG(cena)` liczy ich średnią.
MIN() i MAX() - Znajdowanie skrajnych wartości
SQL-- Tabela: Oceny (id_ucznia, przedmiot, ocena)
-- Pytanie: Jaka jest najlepsza i najgorsza ocena z każdego przedmiotu?
SELECT przedmiot, MIN(ocena) AS Najgorsza, MAX(ocena) AS Najlepsza
FROM Oceny
GROUP BY przedmiot;Grupujemy po przedmiocie. `MIN(ocena)` i `MAX(ocena)` znajdują najmniejszą i największą wartość w kolumnie 'ocena' dla każdej oddzielnej grupy (przedmiotu).
Klauzula HAVING - Filtrowanie *po* grupowaniu
SQL-- Pytanie: Pokaż klasy, które mają WIĘCEJ niż 30 uczniów.
SELECT klasa, COUNT(*) AS LiczbaUczniow
FROM Uczniowie
GROUP BY klasa
HAVING COUNT(*) > 30;Nie możemy tu użyć `WHERE COUNT(*) > 30`, ponieważ `WHERE` filtruje wiersze *przed* grupowaniem. `HAVING` jest stworzone do filtrowania grup *po* tym, jak `COUNT(*)` je policzy.
Przykładowe Zadania Maturalne
Masz bazę danych 'Sklep', a w niej tabelę 'Zamowienia' (id_zamowienia, id_klienta, kwota). Napisz zapytanie SQL, które wyświetli identyfikatory id_klienta oraz łączną sumę wydatków (kwota) dla każdego klienta. Pomiń klientów, których łączna suma wydatków jest mniejsza niż 1000 zł.
Wskazówka: Potrzebujesz GROUP BY id_klienta, aby stworzyć grupy dla każdego klienta. Użyj SUM(kwota), aby policzyć sumę dla każdej grupy. Na koniec użyj HAVING, aby przefiltrować grupy i pokazać tylko te z sumą > 1000.
Pokaż szkic rozwiązania
1. Wybierz klienta i sumę jego kwot: `SELECT id_klienta, SUM(kwota) AS LacznaSuma` 2. Wskaż tabelę: `FROM Zamowienia` 3. Zgrupuj wiersze per klient: `GROUP BY id_klienta` 4. Przefiltruj grupy (nie wiersze!): `HAVING SUM(kwota) > 1000;` 5. Pełne zapytanie: `SELECT id_klienta, SUM(kwota) AS LacznaSuma FROM Zamowienia GROUP BY id_klienta HAVING SUM(kwota) > 1000;`
W bazie danych znajdują się tabele 'Urzadzenia' (kod_u, nazwa_u, typ_u) oraz 'Instalacje' (data_i, kod_u, kod_k). Napisz zapytanie, które dla każdego typu urządzenia (typ_u) poda liczbę instalacji aplikacji na tym typie urządzenia.
Wskazówka: To zadanie wymaga złączenia (JOIN) dwóch tabel, a następnie grupowania (GROUP BY) i zliczania (COUNT). Musisz połączyć 'Urzadzenia' z 'Instalacje' przez kod_u. Następnie zgrupuj wyniki po typ_u i policz (COUNT) liczbę instalacji w każdej grupie.
Pokaż szkic rozwiązania
1. Wybierz typ urządzenia i liczbę wierszy: `SELECT U.typ_u, COUNT(*)` 2. Wskaż tabelę bazową: `FROM Instalacje AS I` 3. Dołącz tabelę z typami: `JOIN Urzadzenia AS U ON I.kod_u = U.kod_u` 4. Zgrupuj wyniki po typie: `GROUP BY U.typ_u;` 5. Pełne zapytanie: `SELECT U.typ_u, COUNT(*) AS LiczbaInstalacji FROM Instalacje AS I JOIN Urzadzenia AS U ON I.kod_u = U.kod_u GROUP BY U.typ_u;`
Częste Błędy
❌ Mylenie WHERE z HAVING
To najczęstszy błąd. Uczeń pisze WHERE COUNT() > 10. To nie zadziała. WHERE filtruje wiersze przed grupowaniem, a COUNT jest obliczany w trakcie* grupowania.
Poprawka: Pamiętaj: WHERE filtruje przed GROUP BY (dane wejściowe). HAVING filtruje po GROUP BY (wyniki grupowania). Poprawnie: HAVING COUNT(*) > 10.
❌ Błąd 'non-aggregated column'
Piszesz: SELECT klasa, imie, COUNT(*) FROM Uczniowie GROUP BY klasa;. To błąd. Baza nie wie, które 'imie' ma wyświetlić dla grupy '1A' (jest ich 30).
Poprawka: Złota Zasada: Wszystko, co jest w SELECT, musi być albo funkcją agregującą (np. COUNT(*), AVG(imie) - co nie ma sensu), albo musi być w klauzuli GROUP BY.
❌ Zapominanie o GROUP BY
Piszesz: SELECT klasa, COUNT() FROM Uczniowie;. Wiele baz danych zwróci błąd, a inne (jak MySQL) mogą zwrócić bezsensowny wynik (jedną klasę i łączną liczbę wszystkich* uczniów).
Poprawka: Jeśli używasz funkcji agregującej (jak COUNT) i chcesz ją policzyć dla każdej klasy, musisz dodać GROUP BY klasa.
❌ Różnica między COUNT(*) a COUNT(kolumna)
COUNT() zlicza wszystkie wiersze w grupie. COUNT(kolumna) zlicza tylko te wiersze w grupie, w których kolumna nie jest pusta* (nie jest NULL).
Poprawka: Jeśli chcesz policzyć 'ilu uczniów jest w klasie', bezpieczniej użyć COUNT(*). Jeśli chcesz policzyć 'ilu uczniów ma wpisanego maila', użyj COUNT(email).
Kluczowe Wnioski
GROUP BYsłuży do 'zwijania' wielu wierszy w jeden wiersz podsumowujący (np. per klasa, per kategoria).- Używa się go zawsze z funkcjami agregującymi:
COUNT(zlicz),SUM(sumuj),AVG(średnia),MIN/MAX. - Złota Zasada: Kolumny w
SELECTmuszą albo być wGROUP BY, albo być funkcją agregującą. WHEREfiltruje wiersze przed grupowaniem.HAVINGfiltruje grupy po grupowaniu (np.HAVING COUNT(*) > 5).- Kolejność wykonywania to:
FROM->WHERE->GROUP BY->HAVING->SELECT->ORDER BY.
Najczęściej Zadawane Pytania
❓ Jaka jest dokładnie różnica między WHERE a HAVING?
`WHERE` działa na pojedynczych wierszach, *zanim* zostaną one połączone w grupy. `HAVING` działa na całych grupach, *po* tym jak zostaną utworzone i policzone (np. przez `COUNT`). `WHERE klasa = '1A'` jest OK, `WHERE COUNT(*) > 10` jest źle. `HAVING COUNT(*) > 10` jest OK.
❓ Czy mogę użyć `GROUP BY` bez funkcji agregującej?
Technicznie tak (`SELECT klasa FROM Uczniowie GROUP BY klasa;`), ale nie ma to sensu. Zwróci to po prostu unikalne nazwy klas, co można szybciej osiągnąć przez `SELECT DISTINCT klasa FROM Uczniowie;`. `GROUP BY` jest stworzone do pracy z agregatami.
❓ Czy mogę grupować po wielu kolumnach?
Tak. `GROUP BY kraj, miasto` stworzy grupy dla każdej unikalnej kombinacji kraju i miasta (np. 'Polska, Warszawa', 'Polska, Kraków', 'Niemcy, Berlin').
❓ Co to jest `COUNT(DISTINCT kolumna)`?
To specjalna forma `COUNT`. `COUNT(kolumna)` zliczy wszystkie wystąpienia. `COUNT(DISTINCT kolumna)` zliczy tylko *unikalne* wartości w tej kolumnie. Np. `COUNT(DISTINCT klasa)` policzy, ile jest różnych klas.
❓
Chcesz opanować wszystkie tematy maturalne?
Dołącz do kursu i zyskaj dostęp do interaktywnych lekcji, edytora kodu i setek zadań.