programowanieProgramista Backend, Analityk BI

Jak zaimplementować niezawodną agregację z unikalnymi warunkami (agregacja warunkowa) w SQL przy tworzeniu raportów z wieloma filtrami? Jakie są subtelności przy łączeniu HAVING, CASE i funkcji agregujących?

Zdaj rozmowy kwalifikacyjne z asystentem AI Hintsage

Odpowiedź.

Agregacja z warunkami to klasyczne zadanie dla raportowania. Początkowo do obliczania różnych wskaźników tworzono oddzielne zapytania z filtrami. Wkrótce pojawiło się bardziej kompaktowe rozwiązanie — agregacja warunkowa z użyciem CASE wewnątrz funkcji agregujących (np. SUM(CASE WHEN ...)). Problem pojawia się przy łączeniu filtrów, grupowania i nagłówków wyników: łatwo można uzyskać błędne sumy lub błędnie zinterpretować wynik.

Rozwiązanie: używać agregacji warunkowej wewnątrz funkcji agregujących. Na przykład, aby wyświetlić dla wszystkich pracowników liczbę zamówień "w trakcie" i "zakończone":

SELECT employee_id, SUM(CASE WHEN status = 'processing' THEN 1 ELSE 0 END) as processing_count, SUM(CASE WHEN status = 'done' THEN 1 ELSE 0 END) as done_count FROM Orders GROUP BY employee_id;

Kluczowe cechy:

  • Niezależnie od filtru WHERE wszystkie potrzebne sumy są liczone w jednym zapytaniu.
  • CASE wewnątrz SUM pozwala na budowanie skomplikowanych raportów wielokrotnych.
  • HAVING stosuje się do już agregowanych danych, do filtrowania wyników grupowania.

Pytania z podstępem.

Co się stanie, jeśli warunek CASE zwróci NULL zamiast 0?

Funkcja agregująca SUM ignoruje NULL. Dlatego, jeśli napiszemy CASE WHEN ... THEN 1 END, pominięte wiersze nie zostaną uwzględnione. Lepiej zawsze wyraźnie ustawiać ELSE 0.

SUM(CASE WHEN status = 'processing' THEN 1 ELSE 0 END)

Czy filtr WHERE może zmienić łączną sumę według statusów przy agregacji warunkowej?

Tak: jeśli główny filtr WHERE ogranicza wybór (na przykład, WHERE region = 'west'), obliczenia będą prowadzone tylko na przefiltrowanych danych. Dla globalnych wyników użyj podzapytania lub usuń filtr.

Czy można użyć HAVING do filtrowania wierszy przed grupowaniem?

Nie. HAVING filtruje już zgrupowane dane według agregatów. Filtrowanie oryginalnych wierszy — przez WHERE.

Typowe błędy i antywzorce

  • Brak ELSE, CASE zwraca NULL, wyniki błędne.
  • Mieszają WHERE i HAVING, otrzymując nie te wiersze.
  • Używają kilku zapytań zamiast jednego z CASE.

Przykład z życia

Negatywny przypadek

W raporcie analitycznym użyto:

SUM(CASE WHEN status = 'approved' THEN 1 END)

Było dużo NULL, łączone sumy były zaniżone. Z powodu filtru WHERE część potrzebnych wierszy została utracona.

Zalety:

  • Kod krótki.

Wady:

  • Niepoprawne wyniki, niewłaściwie wytłumaczony raport biznesowi.

Pozytywny przypadek

Użyto:

SUM(CASE WHEN status = 'approved' THEN 1 ELSE 0 END)

Kod utrzymywano w jednym zapytaniu, filtry na wynik — przez HAVING.

Zalety:

  • Poprawne, przejrzyste wskaźniki.
  • Łatwo dodać nowy status.

Wady:

  • Zapytania stają się dłuższe, potrzebna ostrożność z filtrami.