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:
| Funkcja | Dział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:
| imie | nazwisko | wartosc_zamowien |
|---|---|---|
| Jan | Kowalski | 579.97 |
| Anna | Nowak | 1499.98 |
| Marek | Wiśniewski | 299.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:
| kategoria | srednia_cena |
|---|---|
| Instrumenty | 899.99 |
| Akcesoria | 179.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:
| Klauzula | Działa na | Przykład |
|---|---|---|
| WHERE | pojedyncze wiersze | WHERE cena > 100 |
| HAVING | całe grupy | HAVING 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:
| kategoria | srednia_cena |
|---|---|
| Instrumenty | 899.99 |
| Akcesoria | 179.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
- Policz wszystkich klientów w tabeli
Klienci. - Oblicz średnią cenę produktów.
- Znajdź najdroższy i najtańszy produkt.
- Policz, ilu klientów podało adres e-mail.
Zadanie 2 – grupowanie danych
- Oblicz sumę zamówień dla każdego klienta.
- Pokaż średnią cenę produktów w każdej kategorii.
- Policz, ile zamówień złożył każdy klient.
- Policz, ilu klientów pochodzi z każdego miasta (jeśli tabela ma kolumnę
miasto).
Zadanie 3 – HAVING i sortowanie
- Wyświetl tylko te kategorie produktów, których średnia cena przekracza 200 zł.
- Posortuj te kategorie malejąco po średniej cenie.
- 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
- Wyświetl listę wszystkich zamówień: imię i nazwisko klienta, produkt, cena, data zamówienia.
- Pokaż tylko zamówienia z września 2025.
- Policz dzienną wartość sprzedaży (suma cen) dla każdego dnia.
- Pokaż 3 najdroższe pojedyncze zamówienia (po cenie produktu).
Zadanie 5 – podzapytania i przypadki “brak danych”
- Wyświetl klientów, którzy nie złożyli żadnego zamówienia.
- Pokaż produkty droższe niż średnia cena w ich własnej kategorii.
- Pokaż produkty, których nikt jeszcze nie zamówił.
- Pokaż klientów, którzy kupili co najmniej 2 różne produkty w tej samej kategorii.
Zadanie 6 – raporty, ranking i widok
- Dla każdej kategorii policz łączną wartość sprzedaży i posortuj malejąco.
- Policz średnią cenę zamawianych produktów dla każdego miasta klienta.
- Wypisz miasto z największą liczbą zamówień (TOP 1).
- Utwórz widok „Widok_Zamowienia_Szczegoly” z kolumnami: id_zamowienia, data, imię, nazwisko, miasto, produkt, cena, kategoria.
*** Do Zadań 3-6
Podsumowanie
| Funkcja / Klauzula | Działanie | Przykład |
|---|---|---|
| COUNT() | liczy rekordy | COUNT(*) |
| SUM() | suma wartości | SUM(cena) |
| AVG() | średnia | AVG(cena) |
| MAX() | największa wartość | MAX(cena) |
| MIN() | najmniejsza wartość | MIN(cena) |
| GROUP BY | grupowanie danych | GROUP BY kategoria |
| HAVING | filtr grup | HAVING AVG(cena)>100 |
| ORDER BY | sortowanie wyników | ORDER 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');

