GROUP BY zwija wiersze w grupy
Funkcje agregujące, takie jak COUNT, SUM i AVG, sprowadzają wiele wierszy do jednej liczby. GROUP BY pozwala zrobić to dla każdej kategorii: jedna liczba na klienta, na miesiąc, na status. Każda unikalna wartość (lub kombinacja wartości) staje się jednym wierszem wyniku.
Trzech klientów, trzy wiersze na wyjściu. Sześciu oryginalnych wierszy już nie ma: zostały zwinięte w grupy dla poszczególnych klientów, a COUNT(*) i SUM(amount) policzono w każdej z nich.
Model myślowy: GROUP BY customer mówi „traktuj wszystkie wiersze z tym samym klientem jako jedną grupę”. Agregaty działają potem na każdej grupie osobno.
Co można umieścić na liście SELECT
Na tym ludzie często się potykają. Przy GROUP BY każda kolumna na liście SELECT musi być albo w klauzuli GROUP BY, albo wewnątrz funkcji agregującej. W przeciwnym razie wartość jest niejednoznaczna: z którego wiersza grupy miałaby pochodzić?
Gdyby napisać SELECT region, rep, SUM(amount) z GROUP BY region, SQLite bez oporu by to wykonał (jest pobłażliwy tam, gdzie inne bazy to odrzucają), ale rep zostałby wybrany z grupy dowolnie. Dostałbyś jedno nazwisko przedstawiciela na region bez gwarancji, które. Nie polegaj na tym: grupuj po każdej niezagregowanej kolumnie, którą wyświetlasz.
HAVING filtruje grupy po agregacji
WHERE filtruje wiersze przed grupowaniem. HAVING filtruje grupy po grupowaniu. To cała różnica i dlatego nie możesz umieścić COUNT(*) > 1 w klauzuli WHERE: w chwili, gdy działa WHERE, liczność jeszcze nie istnieje.
Cleo złożyła tylko jedno zamówienie, więc jej grupa zostaje odfiltrowana. Zostają Ada i Boris. Warunek jest sprawdzany na zagregowanej wartości każdej grupy, a nie na pojedynczych wierszach.
W HAVING możesz bezpośrednio odwoływać się do aliasów kolumn z listy SELECT, SQLite na to pozwala:
Często czyta się to lepiej niż powtarzanie SUM(amount) w klauzuli HAVING.
WHERE a HAVING: używaj obu razem
Te dwie klauzule nie wykluczają się. WHERE zawęża, które wiersze biorą udział w grupowaniu, a HAVING zawęża, które grupy trafiają do wyniku. Większość prawdziwych zapytań używa obu.
Czytaj od góry do dołu w kolejności wykonania:
WHERE status = 'paid': całkowicie odrzuć zwrócone zamówienia.GROUP BY customer: pogrupuj pozostałe wiersze według klienta.SUM(amount)jest liczone dla każdej grupy.HAVING SUM(amount) > 75: zostaw tylko grupy, które spełniają warunek.
Boris (80 + 20 = 100) i Cleo (200) przechodzą dalej. Jedyne opłacone zamówienie Ady to 50, co nie spełnia progu.
Wiele warunków i wiele kolumn grupujących
HAVING przyjmuje te same operatory logiczne co WHERE (AND, OR, NOT), a grupować możesz po więcej niż jednej kolumnie, żeby dostać podgrupy:
Każda para (region, quarter) to osobna grupa. Klauzula HAVING wymaga zarówno sumy powyżej 100, jak i co najmniej dwóch transakcji. Kwalifikują się tylko ('North', 'Q1') i ('South', 'Q2').
Praktyczny wzorzec: szukanie duplikatów
Zapytanie GROUP BY ... HAVING COUNT(*) > 1 to standardowy sposób na znalezienie powtarzających się wartości w kolumnie:
Pojawiają się dwa duplikaty. Dalej zwykle decydujesz, czy scalić konta, dodać ograniczenie UNIQUE, czy wyczyścić dane, ale zapytanie wykrywające ma za każdym razem ten sam kształt.
HAVING bez GROUP BY
To nietypowe, ale poprawne. Bez GROUP BY cały zbiór wyników jest traktowany jako jedna grupa, a HAVING filtruje go w całości: dostajesz albo wszystkie zagregowane wartości, albo nic:
Jedyny wiersz wyniku się pojawia, bo suma wynosi 160. Zmień próg na > 200, a zapytanie nie zwróci żadnego wiersza. W praktyce prawie zawsze łączysz HAVING z GROUP BY, ale dobrze wiedzieć, że język tego nie wymaga.
Krótkie podsumowanie
GROUP BYzwija wiersze w grupy według klucza, a agregaty działają wewnątrz każdej grupy.- Każda niezagregowana kolumna w
SELECTpowinna pojawić się wGROUP BY. WHEREfiltruje wiersze przed grupowaniem,HAVINGfiltruje grupy po nim.- Agregaty takie jak
COUNT(*)iSUM(...)należą doHAVING, nigdy doWHERE. HAVINGprzyjmuje złożone warunki i może odwoływać się do aliasów zSELECT.
Dalej: klucze obce
Agregowanie jednej tabeli jest przydatne, ale większość prawdziwych schematów rozkłada dane na wiele tabel: zamówienia tu, klienci tam, produkty jeszcze gdzie indziej. Klucze obce to sposób na połączenie tych tabel tak, żeby relacje pozostały spójne. To temat następnego rozdziału.
Najczęściej zadawane pytania
Jaka jest różnica między WHERE a HAVING w SQLite?
WHERE filtruje pojedyncze wiersze przed grupowaniem. HAVING filtruje grupy po agregacji. WHERE amount > 100 zostawia więc tylko wiersze powyżej 100, a HAVING SUM(amount) > 100 zostawia tylko grupy, których suma przekracza 100. Funkcje agregujące, takie jak COUNT czy SUM, są niedozwolone w WHERE: do tego służy HAVING.
Czy można użyć HAVING bez GROUP BY w SQLite?
Tak. Bez GROUP BY SQLite traktuje cały zbiór wyników jako jedną grupę, a HAVING filtruje ją w całości. Zapytanie zwraca wtedy jeden wiersz albo żadnego. W praktyce zdarza się to rzadko: zwykle jeśli masz HAVING, masz też GROUP BY.
Jak filtrować grupy po COUNT w SQLite?
Umieść agregat w HAVING, a nie w WHERE. Na przykład SELECT customer_id, COUNT(*) FROM orders GROUP BY customer_id HAVING COUNT(*) > 1 zwraca klientów z więcej niż jednym zamówieniem. W SQLite możesz też w HAVING odwołać się do aliasu kolumny z listy SELECT.