O czym jeszcze nie gadaliśmy, to jak przefiltrować grupy po zastosowaniu agregatów? Czasem nie potrzebujemy wszystkich wydziałów — tylko te, gdzie studentów jest ponad setka. Albo chcemy zobaczyć tylko te działy, gdzie średnia pensja przekracza 50 000. Dzisiaj poznasz filtrowanie danych zagregowanych za pomocą HAVING.
Po co nam HAVING, skoro mamy WHERE? Przecież można po prostu wrzucić WHERE po GROUP BY :)
To nie takie proste! Po pierwsze, kolejność operatorów w SQL jest sztywna i WHERE wykonuje się przed GROUP BY.
A może da się go przesunąć po GROUP BY?
Też nie! Bardzo często trzeba przefiltrować wiersze tabeli przed grupowaniem. Potem zrobić grupowanie na przefiltrowanych danych. A potem odrzucić jakieś niepotrzebne dane już po grupowaniu.
To może po prostu wziąć operator WHERE, skopiować go, nazwać HAVING i wrzucić po GROUP BY?
Tak, dokładnie tak to działa! :)
Różnica między HAVING a WHERE
WHERE filtruje wiersze przed grupowaniem.
Wyobraź sobie, że wybierasz ciasta po smaku: truskawkowe i czekoladowe zostają, reszta odpada. To zadanie dla WHERE.
HAVING filtruje po tym, jak dane zostały pogrupowane i funkcje agregujące zrobiły swoje czary-mary.
Na przykład już pogrupowałeś ciasta po stołach, policzyłeś ich ilość i teraz chcesz zostawić tylko te stoły, gdzie ciast jest więcej niż trzy.
Czyli HAVING używasz do filtrowania danych na poziomie grup.
Składnia HAVING
Składnia jest prawie taka sama jak w WHERE, ale działa trochę inaczej:
SELECT kolumny, funkcje_agregujące
FROM tabela
GROUP BY kolumny
HAVING warunek;
Etapy wykonania:
- Najpierw wiersze są filtrowane przez
WHERE. - Potem dane są grupowane przez
GROUP BY. - Do wyników grupowania stosowane są funkcje agregujące.
- Na końcu wynik jest filtrowany przez
HAVING.
Przykłady użycia HAVING
Przykład 1: Filtrowanie wydziałów z dużą liczbą studentów
Chcesz się dowiedzieć, które wydziały na uniwerku mają ponad 100 studentów. Załóżmy, że mamy tabelę students:
| id | name | faculty |
|---|---|---|
| 1 | Alice | Engineering |
| 2 | Bob | Engineering |
| 3 | Charlie | Arts |
| 4 | Daisy | Business |
| 5 | ... | ... |
Zapytanie:
SELECT faculty, COUNT(*) AS student_count
FROM students
GROUP BY faculty
HAVING COUNT(*) > 100;
Co tu się dzieje:
- Najpierw grupujemy studentów po kolumnie
facultyprzezGROUP BY. - Potem funkcja agregująca
COUNT(*)liczy ilu studentów jest na każdym wydziale. - Na końcu
HAVINGodrzuca wszystkie wydziały, gdzie studentów jest 100 lub mniej.
Wynik:
| faculty | student_count |
|---|---|
| Engineering | 150 |
| Arts | 120 |
Przykład 2: Działy z wysoką średnią pensją
Chcesz znaleźć tylko te działy, gdzie średnia pensja pracowników przekracza 50 000. Załóżmy, że mamy tabelę employees:
| id | name | department | salary |
|---|---|---|---|
| 1 | Alice | IT | 60000 |
| 2 | Bob | HR | 45000 |
| 3 | Charlie | IT | 70000 |
| 4 | Daisy | HR | 52000 |
| 5 | ... | ... | ... |
Zapytanie:
SELECT department, AVG(salary) AS avg_salary
FROM employees
GROUP BY department
HAVING AVG(salary) > 50000;
Wynik:
| department | avg_salary |
|---|---|
| IT | 65000 |
Pamiętaj: HAVING działa na wynikach, które zostały policzone po GROUP BY.
Kolejność wykonywania WHERE, GROUP BY i HAVING
Filtrowanie przez WHERE i HAVING odbywa się na różnych etapach. Żeby lepiej to ogarnąć, zobacz krok po kroku jak działa zapytanie:
WHERE: filtrowanie wierszy.Na tym etapie przetwarzane są wszystkie wiersze tabeli. Jeśli wiersz nie przejdzie warunku
WHERE, nie idzie dalej.GROUP BY: grupowanie wierszy.Po filtrowaniu wiersze są łączone w grupy na podstawie kolumn z
GROUP BY.Funkcje agregujące:
Do pogrupowanych danych stosowane są funkcje agregujące, jak
COUNT(),AVG(),SUM()itd.HAVING: filtrowanie grup.Na tym etapie przetwarzane są tylko wyniki agregatów. Warunki
HAVINGdotyczą tylko grup.
Specyfika działania HAVING
Specyfika 1: Praca z agregatami
Najważniejsza różnica HAVING od WHERE — to praca z funkcjami agregującymi. Na przykład:
SELECT department, AVG(salary)
FROM employees
GROUP BY department
HAVING AVG(salary) > 50000;
W tym zapytaniu AVG(salary) nie da się użyć w WHERE, bo WHERE działa na wierszach przed grupowaniem. Zapytanie typu:
SELECT department, AVG(salary)
FROM employees
WHERE AVG(salary) > 50000
GROUP BY department;
wywali błąd: aggregate functions are not allowed in WHERE.
Specyfika 2: Filtrowanie bez grupowania
Możesz użyć HAVING nawet bez jawnego GROUP BY. Wtedy zapytanie traktuje wszystko jak jedną grupę — wszystkie rekordy:
SELECT AVG(salary) AS avg_salary
FROM employees
HAVING AVG(salary) > 50000;
Praktyczny przykład
Załóżmy, że mamy sklep i tabelę sprzedaży sales:
| id | product_id | sales_amount |
|---|---|---|
| 1 | 101 | 200.00 |
| 2 | 102 | 300.00 |
| 3 | 101 | 400.00 |
| 4 | 103 | 150.00 |
Zapytanie: znajdź produkty z łączną sprzedażą powyżej 500.
SELECT product_id, SUM(sales_amount) AS total_sales
FROM sales
GROUP BY product_id
HAVING SUM(sales_amount) > 500;
Wynik:
| product_id | total_sales |
|---|---|
| 101 | 600.00 |
Typowe błędy
Użycie agregatów w WHERE:
Na przykład:
SELECT department, AVG(salary)
FROM employees
WHERE AVG(salary) > 50000
GROUP BY department;
Błąd: funkcji agregujących nie można używać w WHERE.
Błędy z NULL:
Jeśli dane zawierają NULL, filtrowanie może dać nieoczekiwane wyniki. Na przykład:
SELECT department, SUM(salary)
FROM employees
GROUP BY department
HAVING SUM(salary) > 0;
Jeśli kolumna salary zawiera tylko NULL, wynik może być zerowy albo pusty.
Gratulacje. Na tym etapie możesz już śmiało filtrować dane zagregowane! Pamiętaj, że HAVING to Twój klucz do analityki na poziomie grup, gdzie zwykłe WHERE już nie wystarcza.
GO TO FULL VERSION