background image

GROUP BY, HAVING

Dla potrzeb przykładółw utworzymy tablicę danych osobowych uczniów z różnych miast w 
Polsce.  

 

+------------------------------------------------------------------+

 

| Imię    Nazwisko    Miasto    Data_urodzenia    Telefon    Płeć  |

 

+------------------------------------------------------------------+

 

| Jan     Łukomski    Warszawa  1990-12-10        826-44-56  M     |

 

| Anna    Wierzbicka  Kraków    1987-2-21         621-66-10  K     |

 

| ...     ...         ...       ...               ...        ...   |

 

+------------------------------------------------------------------+ 

Opcja

   

GROUP BY

  

Opcja grupujca w zdaniu 

SELECT

 Wstawia się ją do zdania 

SELECT

 po opcji 

WHERE

 (lub 

po opcji 

FROM

, jeśli 

WHERE

 nie użyto), ale przed 

ORDER BY

.  

Przykłady:  

Załóżmy, że chcemy znaleźć liczby uczniów z poszczególnych miast. Np. liczbę uczniów z 
Gdańska można uzyskać łatwo:  

 

mysql> SELECT COUNT(*) FROM Uczniowie WHERE Miasto='Gdańsk';

 

+---+

 

| 1 |

 

+---+ 

Podobnie można zrobić dla Krakowa, Warszawy i innych miast. Gorzej jest, jeśli chcemy 
dokonać zbiorczych obliczeń dla różnych miast, tj. mieć listę miast i przy każdym - liczbę 
mieszkających w nim uczniów. Do rozwiązania tego zadania trzeba użyć innej opcji zdania 

SELECT

 - 

GROUP BY

.  

 

mysql> SELECT Miasto, COUNT(*) FROM Uczniowie GROUP BY Miasto 
ORDER BY 2 DESC;

 

+----------------------+

 

| Miasto    | COUNT(*) |

 

+----------------------+

 

| Warszawa  | 6        |

 

| Kraków    | 3        |

 

| Gdynia    | 2        |

 

| Bydgoszcz | 1        |

 

| Tykocin   | 1        |

 

| Otwock    | 1        |

 

| Szczecin  | 1        |

 

+----------------------+ 

Opcja 

GROUP BY

 ("grupuj wynik wg") pozwala użyć funkcji agregujących (

SUM, AVG, 

MAX, MIN, COUNT

) nie do całej tabeli, ale do grup wierszy w tabeli wynikowej. Innymi 

słowy: 

GROUP BY

 określa zakres działania funkcji np. 

COUNT

 - tu działa ona dla każdego 

miasta niezależnie (podobnie jest dla innych funkcji agregujących). Bez opcji 

GROUP BY

 

background image

funkcja 

COUNT

, a także pozostałe funkcje agregujące, działają na całej tabeli (na wszystkich 

wierszach). Dla każdej grupy, tu: dla każdego miasta, 

GROUP BY

 generuje jeden wiersz.  

Jeśli, oprócz funkcji agregującej, poda się w zapytaniu także inne kolumny, ale nie użyje się 
opcji 

GROUP BY

, zostanie zasygnalizowany błąd.  

Tak będzie np. w przypadku zapytania:  

 

mysql> SELECT Miasto, COUNT(*) FROM Uczniowie;

 

mysql> Błąd nr 1140:

 

Mieszanie funkcji grupowych (MIN(),MAX(),COUNT()...), bez określenia 
grupowania.  

Przy próbie jej wykonania wypisany zostanie komunikat o mieszaniu kolumn z funkcjami 
agregującymi, niedopuszczalnym bez użycia 

GROUP BY

. Jest to naturalne - nie bardzo 

wiadomo jak należałoby wypisać (potraktować) wynik powyższego zapytania bez 
grupowania wyników (które miasto i jaką liczbę podać).  

Przykłady:  

1. Wypisz listę miast, z liczbą uczennic z każdego miasta, uporządkowaną malejąco wg 

liczby uczennic.  

  mysql> SELECT Miasto, COUNT(*) FROM Uczniowie WHERE Płeć='K' 

GROUP BY Miasto ORDER BY 2 DESC; 

2. Wypisz dla każdego miasta jego nazwę oraz datę urodzenia najmłodszej uczennicy.  

  mysql> SELECT Miasto, MAX(Data_ur) AS Data FROM Uczniowie 

WHERE Płeć='K' GROUP BY Miasto; 

3.  Napisz zapytanie, z którego dostaniemy numery miesięcy, z liczbami uczniów, którzy 

urodzili się w tym miesiącu, uporządkowaną malejąco wg liczby uczniów, a w obrębie 
tej samej liczby urodzeń - wg numerów miesięcy, rosnąco. Kolumny mają być 
zatytułowane 'Miesiąc' i 'Liczba_uczniów'.  

 

mysql> SELECT MONTH(Data_urodzenia) AS Miesiąc, COUNT(*) AS 
Liczba_uczniów FROM Uczniowie GROUP BY 1 ORDER BY 2 DESC, 1 

ASC; 

Załóżmy teraz, że chcemy wybrać tylko te miasta, z których pochodzą co najmniej dwie 
uczennice. Możemy spróbować warunek wstawić do 

WHERE

:  

 

mysql> SELECT Miasto, COUNT(*) FROM Uczniowie WHERE Płeć='K' 
AND COUNT(*) >= 2 GROUP BY Miasto ORDER BY 2 DESC;

 

mysql> Błąd nr 1111: 

Próba wykonania zapytania daje błąd. Jest on naturalny: opcja 

WHERE

 określa warunki na 

rekordy, a nie na wynik ich przeszukiwania (dopiero na podstawie tego wyniku można 
określić, dla którego miasta liczba uczennic jest większa lub równa 2). Sprawę rozwiązuje 
użycie opcji 

HAVING

, będącej dodatkowym filtrem nakładanym na wynik działania 

GROUP 

BY

:  

background image

 

mysql> SELECT Miasto, COUNT(*) FROM Uczniowie GROUP BY Miasto 
HAVING COUNT(*) >= 2 ORDER BY 2 DESC;

 

+----------------------+

 

| Miasto    | COUNT(*) |

 

+----------------------+

 

| Warszawa  | 6        |

 

| Kraków    | 3        |

 

| Gdynia    | 2        |

 

+----------------------+ 

Załóżmy teraz, że chcemy wybrać tylko te miasta, z których pochodzą co najmniej dwie 
uczennice. Możemy spróbować warunek wstawić do WHERE:  

 

mysql> SELECT Miasto, COUNT(*) FROM Uczniowie WHERE Płeć='K' 
AND COUNT(*) >= 2 GROUP BY Miasto ORDER BY 2 DESC;

 

 

Błąd nr 1111:

 

Błędne użycie funkcji grupowych. 

Próba wykonania zapytania daje błąd. Jest on naturalny: opcja WHERE określa warunki na 
rekordy, a nie na wynik ich przeszukiwania (dopiero na podstawie tego wyniku można 
określić, dla którego miasta liczba uczennic jest większa lub równa 2). Sprawę rozwiązuje 
użycie opcji HAVING, będącej dodatkowym filtrem nakładanym na wynik działania GROUP 
BY:  

 

mysql> SELECT Miasto, COUNT(*) FROM Uczniowie GROUP BY Miasto 
HAVING COUNT(*) >= 2 ORDER BY 2 DESC;

 

 

+----------------------+

 

| Miasto    | COUNT(*) |

 

+----------------------+

 

| Warszawa  | 6        |

 

| Kraków    | 3        |

 

| Gdynia    | 2        |

 

+----------------------+ 

Gdyby chodziło o miasta, z których pochodzi tylko jeden uczeń, warunek w HAVING byłby 
COUNT(*)=1.  

Podstawowa różnica między WHERE i HAVING polega na tym, kiedy w procesie tworzenia 
tabeli wynikowej opcje te są wykorzystywane. Otóż WHERE służy do określenia, które 
wiersze z tabeli wybrać, HAVING zaś 'działa' na rezultacie wyboru (już po zastosowaniu 
WHERE). Tę różnicę widać w zapytaniach.  

Następne zapytanie  

 

mysql> SELECT COUNT(*) FROM Uczniowie WHERE Miasto LIKE 'W%'; 
+----------+

 

| COUNT(*) |

 

+----------+

 

|  7       |

 

+----------+ 

background image

wykona się poprawnie (liczba uczniów mieszkających w miastach o nazwach zaczynających 
się na literę W. WHERE wyszuka takie rekordy w tabeli Uczniowie). Błędne jest natomiast 
natomiast:  

 

 

mysql> SELECT COUNT(*) FROM Uczniowie HAVING Miasto LIKE 

'W%';

 

 

Błąd nr 1054:

 

Nieznana kolumna. 

Błąd - nieznana kolumna. W wyniku zrealizowanej selekcji - a na tym wyniku działa HAVING 
- nie ma kolumny 'Miasto' jest tylko liczba rekordów w tabeli Uczniowie.  

Natomiast dwa zapytania:  

 

 

mysql> SELECT Miasto, COUNT(*) FROM Uczniowie WHERE Miasto 
LIKE 'W%' GROUP BY Miasto;

 

+----------------------+

 

| Miasto    | COUNT(*) |

 

+----------------------+

 

| Warszawa  | 6        |

 

| Wrocław   | 1        |

 

+----------------------+ 

 

 

mysql> SELECT Miasto, COUNT(*) FROM Uczniowie GROUP BY Miasto 
HAVING Miasto LIKE 'W%';

 

+----------------------+

 

| Miasto    | COUNT(*) |

 

+----------------------+

 

| Warszawa  | 6        |

 

| Wrocław   | 1        |

 

+----------------------+ 

zwrócą ten sam wynik.  

Kiedy nie używa się opcji GROUP BY, opcja HAVING działa identycznie jak opcja WHERE 
(warunek wyboru rekordów można podać w WHERE lub w HAVING, z tym samym skutkiem 
końcowym). Problem: którą z tych opcji - przy braku GROUP BY lub w przypadku, gdy obie 
opcje można stosować np. do selekcji pojedynczych rekordów - wybrać? Zasadą ogólną jest: 
jeśli coś można wyfiltrować za pomocą WHERE, należy kryterium wyboru podać w tej opcji. 
HAVING należy używać tylko wtedy, gdy używa się opcji GROUP BY. Przyczyn tego 
zalecenia jest kilka, a jedną z nich jest efektywność: kolumny wyfiltrowane za pomocą 
WHERE nie biorą już udziału w przetwarzaniu opcji GROUP BY i HAVING i tym samym 
ogólny czas realizacji zapytania skraca się.