Funkcje agregujące i grupowanie danych w SQL

Wyobraź sobie, że masz listę wszystkich zamówień w sklepie muzycznym.
Każde zamówienie to jeden wiersz: klient, produkt, ilość, cena.
Ale czasem nie chcesz widzieć każdego zamówienia z osobna – tylko podsumowanie, np.

  • ilu klientów złożyło zamówienia,
  • jaka była średnia wartość zakupów,
  • który produkt był najdroższy.

Do takich obliczeń służą właśnie funkcje agregujące i grupowanie danych (GROUP BY).


1. Czym są funkcje agregujące?

Funkcje agregujące wykonują obliczenia na zestawie danych i zwracają jedną wartość jako wynik.
Nie analizują pojedynczych rekordów, tylko całe zbiory (np. całą kolumnę).

Najczęściej używane funkcje to:

FunkcjaDziałanie
COUNT()Liczy rekordy
SUM()Oblicza sumę
AVG()Oblicza średnią
MAX()Wybiera największą wartość
MIN()Wybiera najmniejszą wartość

2. Przykłady funkcji agregujących

COUNT – liczenie rekordów

Policz wszystkich klientów:

SELECT COUNT(*) AS liczba_klientow FROM Klienci;

Policz tylko tych klientów, którzy mają adres e-mail:

SELECT COUNT(email) AS klienci_z_mailem FROM Klienci;

SUM – suma wartości

Oblicz łączną wartość wszystkich zamówień:

SELECT SUM(ilosc * Produkty.cena) AS suma_zamowien
FROM Zamowienia
JOIN Produkty ON Zamowienia.id_produktu = Produkty.id_produktu;

Wynik to całkowita kwota sprzedaży w sklepie.


AVG – średnia wartość

Średnia cena wszystkich produktów:

SELECT AVG(cena) AS srednia_cena FROM Produkty;

MAX – największa wartość

Najdroższy produkt:

SELECT MAX(cena) AS najdrozszy_produkt FROM Produkty;

MIN – najmniejsza wartość

Pierwsza data zamówienia:

SELECT MIN(data_zamowienia) AS pierwsze_zamowienie FROM Zamowienia;

3. Grupowanie danych – GROUP BY

Często chcemy, żeby funkcje agregujące działały osobno dla każdej grupy danych.
Na przykład: obliczyć łączną wartość zamówień dla każdego klienta osobno.

Do tego używamy klauzuli GROUP BY.

Przykład:

Suma zamówień dla każdego klienta:

SELECT Klienci.imie, Klienci.nazwisko,
       SUM(ilosc * Produkty.cena) AS wartosc_zamowien
FROM Zamowienia
JOIN Klienci ON Zamowienia.id_klienta = Klienci.id_klienta
JOIN Produkty ON Zamowienia.id_produktu = Produkty.id_produktu
GROUP BY Klienci.imie, Klienci.nazwisko;

Wynik:

imienazwiskowartosc_zamowien
JanKowalski579.97
AnnaNowak1499.98
MarekWiśniewski299.99

Inny przykład:

Średnia cena produktów w każdej kategorii:

SELECT kategoria, AVG(cena) AS srednia_cena
FROM Produkty
GROUP BY kategoria;

Wynik:

kategoriasrednia_cena
Instrumenty899.99
Akcesoria179.99

4. Filtrowanie grup – HAVING

Klauzula HAVING działa podobnie do WHERE, ale dotyczy grup, a nie pojedynczych wierszy.

Przykład:

Pokaż tylko te kategorie, w których średnia cena przekracza 100 zł:

SELECT kategoria, AVG(cena) AS srednia_cena
FROM Produkty
GROUP BY kategoria
HAVING AVG(cena) > 100;

Porównanie:

KlauzulaDziała naPrzykład
WHEREpojedyncze wierszeWHERE cena > 100
HAVINGcałe grupyHAVING AVG(cena) > 100

5. Łączenie funkcji w złożone zapytania

Można połączyć wiele elementów: GROUP BY, HAVING, ORDER BY.

Przykład:

Pokaż średnią cenę produktów w każdej kategorii, tylko tych, gdzie średnia przekracza 100 zł, posortowane malejąco:

SELECT kategoria, AVG(cena) AS srednia_cena
FROM Produkty
GROUP BY kategoria
HAVING AVG(cena) > 100
ORDER BY srednia_cena DESC;

Wynik:

kategoriasrednia_cena
Instrumenty899.99
Akcesoria179.99

6. Inne przydatne klauzule

DISTINCT – usuwa duplikaty

SELECT DISTINCT kategoria FROM Produkty;

ORDER BY – sortuje wynik

SELECT imie, nazwisko FROM Klienci
ORDER BY nazwisko ASC;

7. Zadania praktyczne

Zadanie 1 – funkcje agregujące

  1. Policz wszystkich klientów w tabeli Klienci.
  2. Oblicz średnią cenę produktów.
  3. Znajdź najdroższy i najtańszy produkt.
  4. Policz, ilu klientów podało adres e-mail.

Zadanie 2 – grupowanie danych

  1. Oblicz sumę zamówień dla każdego klienta.
  2. Pokaż średnią cenę produktów w każdej kategorii.
  3. Policz, ile zamówień złożył każdy klient.
  4. Policz, ilu klientów pochodzi z każdego miasta (jeśli tabela ma kolumnę miasto).

Zadanie 3 – HAVING i sortowanie

  1. Wyświetl tylko te kategorie produktów, których średnia cena przekracza 200 zł.
  2. Posortuj te kategorie malejąco po średniej cenie.
  3. Wyświetl klientów, którzy złożyli więcej niż jedno zamówienie: SELECT Klienci.imie, Klienci.nazwisko, COUNT(Zamowienia.id_zamowienia) AS liczba FROM Klienci JOIN Zamowienia ON Klienci.id_klienta = Zamowienia.id_klienta GROUP BY Klienci.imie, Klienci.nazwisko HAVING COUNT(Zamowienia.id_zamowienia) > 1;

Zadanie 4 – JOIN + filtry na datach

  1. Wyświetl listę wszystkich zamówień: imię i nazwisko klienta, produkt, cena, data zamówienia.
  2. Pokaż tylko zamówienia z września 2025.
  3. Policz dzienną wartość sprzedaży (suma cen) dla każdego dnia.
  4. Pokaż 3 najdroższe pojedyncze zamówienia (po cenie produktu).

Zadanie 5 – podzapytania i przypadki “brak danych”

  1. Wyświetl klientów, którzy nie złożyli żadnego zamówienia.
  2. Pokaż produkty droższe niż średnia cena w ich własnej kategorii.
  3. Pokaż produkty, których nikt jeszcze nie zamówił.
  4. Pokaż klientów, którzy kupili co najmniej 2 różne produkty w tej samej kategorii.

Zadanie 6 – raporty, ranking i widok

  1. Dla każdej kategorii policz łączną wartość sprzedaży i posortuj malejąco.
  2. Policz średnią cenę zamawianych produktów dla każdego miasta klienta.
  3. Wypisz miasto z największą liczbą zamówień (TOP 1).
  4. Utwórz widok „Widok_Zamowienia_Szczegoly” z kolumnami: id_zamowienia, data, imię, nazwisko, miasto, produkt, cena, kategoria.

*** Do Zadań 3-6


Podsumowanie

Funkcja / KlauzulaDziałaniePrzykład
COUNT()liczy rekordyCOUNT(*)
SUM()suma wartościSUM(cena)
AVG()średniaAVG(cena)
MAX()największa wartośćMAX(cena)
MIN()najmniejsza wartośćMIN(cena)
GROUP BYgrupowanie danychGROUP BY kategoria
HAVINGfiltr grupHAVING AVG(cena)>100
ORDER BYsortowanie wynikówORDER BY srednia_cena DESC

Funkcje agregujące i grupowanie to potężne narzędzia SQL.
Dzięki nim możesz tworzyć raporty, podsumowania i analizy danych – od prostych zestawień po pełne statystyki sprzedaży.
To pierwszy krok do „myślenia analitycznego” w bazach danych.

Polecenia Bazy Danych do zadań:

Struktura bazy danych

  • Klienci – dane klientów
  • Kategorie – grupy produktów
  • Produkty – towary należące do kategorii
  • Zamowienia – powiązania klientów z produktami

KOD SQL – CREATE TABLE

CREATE DATABASE Sklep;
USE Sklep;

-- Tabela z klientami
CREATE TABLE Klienci (
    id_klienta INT AUTO_INCREMENT PRIMARY KEY,
    imie VARCHAR(30),
    nazwisko VARCHAR(40),
    miasto VARCHAR(40),
    email VARCHAR(60)
);

-- Tabela z kategoriami produktów
CREATE TABLE Kategorie (
    id_kategorii INT AUTO_INCREMENT PRIMARY KEY,
    nazwa_kategorii VARCHAR(40)
);

-- Tabela z produktami
CREATE TABLE Produkty (
    id_produktu INT AUTO_INCREMENT PRIMARY KEY,
    nazwa VARCHAR(50),
    cena DECIMAL(8,2),
    id_kategorii INT,
    FOREIGN KEY (id_kategorii) REFERENCES Kategorie(id_kategorii)
);

-- Tabela z zamówieniami
CREATE TABLE Zamowienia (
    id_zamowienia INT AUTO_INCREMENT PRIMARY KEY,
    id_klienta INT,
    id_produktu INT,
    data_zamowienia DATE,
    FOREIGN KEY (id_klienta) REFERENCES Klienci(id_klienta),
    FOREIGN KEY (id_produktu) REFERENCES Produkty(id_produktu)
);

KOD SQL – INSERT INTO (dane przykładowe)

-- Klienci
INSERT INTO Klienci (imie, nazwisko, miasto, email) VALUES
('Jan', 'Nowak', 'Warszawa', 'jan.nowak@gmail.com'),
('Anna', 'Kowalska', 'Gdańsk', 'anna.kowalska@wp.pl'),
('Piotr', 'Wiśniewski', 'Kraków', NULL),
('Kasia', 'Zielińska', 'Warszawa', 'kasia.zielinska@o2.pl'),
('Tomasz', 'Lewandowski', 'Poznań', NULL);

-- Kategorie
INSERT INTO Kategorie (nazwa_kategorii) VALUES
('Elektronika'),
('RTV'),
('AGD'),
('Książki'),
('Zabawki');

-- Produkty
INSERT INTO Produkty (nazwa, cena, id_kategorii) VALUES
('Laptop', 3500.00, 1),
('Smartfon', 2800.00, 1),
('Telewizor', 4200.00, 2),
('Mikser kuchenny', 250.00, 3),
('Lodówka', 1900.00, 3),
('Harry Potter', 59.90, 4),
('Książka Python', 89.90, 4),
('Klocki Lego', 299.00, 5),
('Puzzle 1000', 45.00, 5);

-- Zamówienia
INSERT INTO Zamowienia (id_klienta, id_produktu, data_zamowienia) VALUES
(1, 1, '2025-09-10'),
(1, 6, '2025-09-12'),
(2, 2, '2025-09-15'),
(2, 8, '2025-09-16'),
(3, 4, '2025-09-18'),
(3, 9, '2025-09-19'),
(4, 3, '2025-09-20'),
(4, 7, '2025-09-22'),
(4, 8, '2025-09-25'),
(5, 5, '2025-09-26');