ś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