Przejdźmy jeszcze raz przez kolejność wykonywania operacji w SQL, a także przez to, z czym może i nie może pracować WHERE i HAVING. To ważny temat, na którym opiera się wiele niuansów działania zapytań SQL. Musisz to naprawdę dobrze ogarnąć.
To czym właściwie WHERE różni się od HAVING, kiedy używać jednego, a kiedy drugiego i jak one się ze sobą łączą? To pomoże Ci nie pogubić się w logice zapytań i skutecznie filtrować dane. Podsumujmy naszą wiedzę o WHERE i HAVING.
Czym jest WHERE?
WHERE — to warunek, który służy do filtrowania wierszy przed grupowaniem lub użyciem funkcji agregujących. Czyli najpierw z tabeli wybierane są wiersze spełniające kryteria, a dopiero potem na tych danych robimy grupowanie.
👉 Wyobraź sobie, że zbierasz owoce na targu. WHERE — to filtr, który pozwala Ci od razu odrzucić zgniłe jabłka, zanim zaczniesz je sortować według rozmiaru czy koloru.
Przykład:
SELECT *
FROM students
WHERE age > 18;
To zapytanie wybierze wszystkich studentów starszych niż 18 lat przed wykonaniem jakichkolwiek innych operacji.
Czym jest HAVING?
HAVING — to filtr, który stosujemy po zgrupowaniu danych (GROUP BY). Pozwala nakładać warunki na zgrupowane dane, np. zostawić tylko te grupy, gdzie średnia ocen studentów jest wyższa niż 80.
👉 Wracając do przykładu z jabłkami. HAVING — to filtr, którego używasz już po posortowaniu jabłek do koszyków (grup). Teraz interesują Cię np. tylko te koszyki (grupy), gdzie jest więcej niż dziesięć jabłek.
Przykład:
SELECT koszyk, COUNT(*)
FROM jablka
GROUP BY koszyk
HAVING COUNT(*) > 10;
To zapytanie wybierze tylko te koszyki, gdzie liczba jabłek jest większa niż 10.
Główne różnice:
| Cecha | WHERE |
HAVING |
|---|---|---|
| Zastosowanie | Filtruje wiersze przed grupowaniem | Filtruje grupy po grupowaniu |
| Praca z agregacją | Nie można używać funkcji agregujących | Można używać funkcji agregujących |
| Cel | Usuwa zbędne wiersze do grupowania | Usuwa grupy, które nie spełniają warunku |
Kolejność wykonywania WHERE, GROUP BY i HAVING
Żeby lepiej zrozumieć, jak działają WHERE i HAVING, zobaczmy na kolejność wykonywania zapytań SQL:
- Najpierw wykonuje się
FROM, wybierane są wiersze z tabeli. - Potem stosowany jest
WHERE, filtrowane są tylko te wiersze, które spełniają warunki. - Później następuje grupowanie przez
GROUP BY. Dostajemy nową tabelę z danymi grup. - Stosowany jest
HAVING, filtrowane są grupy, które spełniają warunki. - Na końcu wybierane są wyniki
SELECT.
Schematycznie wygląda to tak:
1. FROM → 2. WHERE → 3. GROUP BY → 4. HAVING → 5. SELECT
Przykład:
SELECT department, AVG(age) AS avg_age
FROM students
WHERE age > 18
GROUP BY department
HAVING AVG(age) > 20;
Co się tutaj dzieje:
- W tabeli
studentswybierane są wiersze, gdzieage > 18(używany jestWHERE). - Pozostałe wiersze są grupowane według
department. - Dla każdej grupy liczony jest średni wiek studentów.
- Grupy, gdzie średni wiek jest mniejszy lub równy 20, są odrzucane przez
HAVING. - Wyniki są wyświetlane.
Przykłady łączonego użycia
Przykład 1: Filtrowanie przed i po grupowaniu
Warunek: znaleźć wydziały, gdzie jest więcej niż 5 studentów, biorąc pod uwagę tylko studentów starszych niż 18 lat.
Wyjściowa tabela students
| id | name | department | age | gpa |
|---|---|---|---|---|
| 1 | Alex Lin | ComputerSci | 20 | 3.8 |
| 2 | Maria Chi | Math | 22 | 3.5 |
| 3 | Anna Song | ComputerSci | 19 | 4.0 |
| 4 | Otto Art | Math | 17 | 3.9 |
| 5 | Liam Park | Physics | 21 | 3.7 |
| 6 | Jane Doe | ComputerSci | 23 | 3.6 |
| 7 | Tom Brown | Math | 25 | 3.4 |
| 8 | Sara White | Math | 19 | 3.8 |
| 9 | John Smith | ComputerSci | 20 | 3.7 |
| 10 | Emily Green | Physics | 18 | 3.9 |
| 11 | Mark Blue | ComputerSci | 21 | 3.5 |
| 12 | Zoe Black | Math | 22 | 3.6 |
| 13 | Max Gray | ComputerSci | 20 | 3.9 |
| 14 | Eva Gold | Math | 23 | 3.7 |
| 15 | Nick Silver | Physics | 19 | 3.8 |
Zapytanie:
SELECT department, COUNT(*) AS student_count
FROM students
WHERE age > 18
GROUP BY department
HAVING COUNT(*) > 5;
Wynik: -- Wynik zapytania
| department | student_count |
|---|---|
| ComputerSci | 6 |
Wyjaśnienie:
- Najpierw usuwamy wiersze, gdzie
age <= 18(warunekWHERE). - Grupujemy dane według wydziałów (
GROUP BY department). - Liczymy liczbę studentów w każdej grupie.
- Odrzucamy grupy, gdzie studentów jest mniej lub równo 5 (
HAVING COUNT(*) > 5).
Przykład 2: Błąd przy użyciu WHERE zamiast HAVING
Warunek: Znaleźć wydziały, gdzie średni wiek jest większy niż 22 lata.
Niepoprawne zapytanie:
SELECT department, AVG(age) AS avg_age
FROM students
WHERE AVG(age) > 22
GROUP BY department;
Błąd: SQL nie pozwala używać funkcji agregujących AVG w WHERE, bo na tym etapie agregaty jeszcze nie są policzone.
Poprawne zapytanie:
SELECT department, AVG(age) AS avg_age
FROM students
GROUP BY department
HAVING AVG(age) > 22;
Tutaj warunek AVG(age) > 22 stosowany jest po grupowaniu.
Praktyczne wskazówki
Jeśli musisz filtrować wiersze, używaj WHERE. Przykład: Znaleźć wszystkich pracowników z pensją powyżej 5000.
SELECT *
FROM employees
WHERE salary > 5000;
Jeśli musisz filtrować grupy, używaj HAVING. Przykład: Znaleźć działy z łączną pensją powyżej 100 000.
SELECT department, SUM(salary) AS total_salary
FROM employees
GROUP BY department
HAVING SUM(salary) > 100000;
Łącz WHERE i HAVING dla bardziej złożonych warunków.
Przykład: Znaleźć kraje z liczbą mieszkańców powyżej 10 milionów, biorąc pod uwagę tylko miasta, gdzie populacja jest większa niż 1 milion.
SELECT country, SUM(population) AS total_population
FROM cities
WHERE population > 1000000
GROUP BY country
HAVING SUM(population) > 10000000;
Typowe błędy i jak ich unikać
Jednym z najczęstszych błędów jest mylenie WHERE i HAVING. Na przykład próba użycia funkcji agregującej w WHERE:
SELECT department, COUNT(*)
FROM students
WHERE COUNT(*) > 10
GROUP BY department;
Takie zapytanie zwróci błąd, bo obliczenia agregujące nie są dostępne na etapie WHERE. Poprawne podejście — użyć HAVING:
SELECT department, COUNT(*)
FROM students
GROUP BY department
HAVING COUNT(*) > 10;
Kolejny błąd to wybór złych warunków do WHERE. Na przykład:
SELECT department, AVG(age) AS avg_age
FROM students
WHERE avg_age > 20
GROUP BY department;
Tutaj warunek avg_age > 20 jest niepoprawny, bo avg_age jeszcze nie jest policzony. Rozwiązanie — przenieść ten warunek do HAVING:
SELECT department, AVG(age) AS avg_age
FROM students
GROUP BY department
HAVING AVG(age) > 20;
Mam nadzieję, że teraz masz jasność, czym różnią się WHERE i HAVING, jak ich poprawnie używać i jak unikać typowych błędów. Ta wiedza przyda Ci się przy tworzeniu złożonych raportów, filtrowaniu danych do analizy i optymalizacji zapytań. Czyli do większości prawdziwych zapytań SQL, które będziesz pisać :)
GO TO FULL VERSION