średniSQLSQL

SQL GROUP BY i Funkcje Agregujące (COUNT, SUM, AVG)

15 min czytania
SQL
Zaktualizowano: 3.11.2025

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?

  1. Funkcje Agregujące (co liczymy):
  2. `COUNT(*)` lub `COUNT(kolumna)`: Zlicza wiersze w grupie.
  3. `SUM(kolumna)`: Oblicza sumę wartości w danej kolumnie dla grupy.
  4. `AVG(kolumna)`: Oblicza średnią arytmetyczną w grupie.
  5. `MIN(kolumna)`: Znajduje najmniejszą wartość w grupie.
  6. `MAX(kolumna)`: Znajduje największą wartość w grupie.
  7. Klauzule (jak liczymy):
  8. `GROUP BY [kolumna]` - Mówi bazie, jak tworzyć grupy. Np. `GROUP BY klasa` stworzy osobne 'worki' na uczniów z '1A', '1B', '2A' itd.
  9. `HAVING [warunek]` - To jest 'WHERE' dla grup. Filtruje wyniki *po* zgrupowaniu. Np. `HAVING COUNT(*) > 20` pokaże tylko te klasy, które mają więcej niż 20 uczniów.
  10. Złota Zasada SQL: Jeśli w zapytaniu jest `GROUP BY`, to w klauzuli `SELECT` mogą znaleźć się *tylko*: 1) kolumny wymienione w `GROUP BY` lub 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

Matura 2026Zadanie Zadanie Typu Maturalnego 1

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;`
Matura 2025Zadanie 7.1 (Matura Czerwiec 2025)

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 BY` sł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 `SELECT` muszą albo być w `GROUP BY`, albo być funkcją agregującą.
  • `WHERE` filtruje wiersze *przed* grupowaniem.
  • `HAVING` filtruje 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ń.

Powiązane Tematy