programowanieProgramista Backend

Jak zaimplementować warunkową agregację w SQL (na przykład, aby zliczyć sumy według różnych statusów w jednym zapytaniu) i jakie pułapki mogą się z tym wiązać?

Zdaj rozmowy kwalifikacyjne z asystentem AI Hintsage

Odpowiedź.

Warunkowa agregacja jest używana, gdy zachodzi potrzeba agregacji wartości (na przykład sumy lub liczby) wg różnych warunków w ramach jednego zapytania. Historycznie, aby osiągnąć podobne cele, programiści SQL musieli pisać wiele podzapytań lub robić połączenia i grupowanie oddzielnie dla każdej kategorii, co prowadziło do mniej czytelnego i mniej wydajnego kodu.

Problem polegał na trudności w jednoczesnym obliczaniu kilku agregatów z różnymi warunkami — każdy pojedynczy agregat zazwyczaj wymaga filtrowania, a proste zastosowanie SUM() lub COUNT() nie uwzględnia odpowiedniego warunku w obrębie funkcji agregacyjnej.

Rozwiązanie — zastosowanie konstrukcji CASE w obrębie funkcji agregacyjnej, co pozwala "dzielić" agregaty według różnych warunków w locie, bez potrzeby stosowania wielu złączeń:

Przykład kodu:

SELECT department, SUM(CASE WHEN status = 'approved' THEN amount ELSE 0 END) AS approved_sum, SUM(CASE WHEN status = 'pending' THEN amount ELSE 0 END) AS pending_sum, COUNT(CASE WHEN status = 'rejected' THEN 1 END) AS rejected_count FROM payments GROUP BY department;

Kluczowe cechy:

  • Umożliwia agregację według wielu warunków jednocześnie w jednym zapytaniu.
  • Działa we wszystkich dialektach SQL (dokładne wsparcie składni może się różnić).
  • Może być stosowane nie tylko z SUM, ale także przy COUNT, AVG itp.

Pytania z pułapkami.

Czy można się obyć bez konstrukcji CASE dla warunkowej agregacji?

Niepoprawna odpowiedź — użycie WHERE w zapytaniu bezpośrednio z funkcją agregacyjną. W rzeczywistości WHERE filtruje wiersze przed agregacją, a nie wewnątrz każdej kolumny agregacyjnej.

Przykład kodu (nieprawidłowy sposób):

SELECT COUNT(*) FROM payments WHERE status = 'approved'; SELECT COUNT(*) FROM payments WHERE status = 'pending';

Te zapytania nie mogą być połączone w jeden wiersz wyniku według różnych warunków bez CASE.

Co się stanie, jeśli użyjesz NULL w wyrażeniu warunkowym?

Jeśli CASE nie zwraca wartości alternatywnej, to domyślnie będzie to NULL, a funkcja agregacyjna zignoruje NULL.

Przykład kodu:

SUM(CASE WHEN status = 'approved' THEN amount END) -- Jeśli status nie jest 'approved', to NULL, a wiersz jest ignorowany w SUM

Czy można użyć IF zamiast CASE?

W niektórych dialektach SQL (na przykład MySQL) jest to możliwe, ale taki kod staje się nieprzenośny między różnymi bazami danych. W uniwersalnych zapytaniach zawsze stosuj CASE.

Typowe błędy i antywzorce

  • Użycie WHERE zamiast zagnieżdżonej logiki wewnątrz CASE prowadzi do konieczności wykonywania wielu pojedynczych zapytań, co pogarsza wydajność.
  • Zapominając o ustawieniu ELSE 0 w CASE, można uzyskać błędną ilość/sumę z powodu pominięć związanych z NULL.
  • Kopiowanie i wklejanie CASE bez odpowiedniej kontroli prowadzi do błędów logicznych i nieprawidłowych obliczeń.

Przykład z życia

Negatywny przypadek

Analityk próbował zliczyć sumy według każdego statusu, pisząc kilka oddzielnych podzapytań. Zewnętrzny raport stał się skomplikowany, a dodanie nowych statusów wymagało każdorazowo przepisania kodu.

Zalety:

  • Łatwe do wdrożenia dla jednej metryki. Wady:
  • Trudne do skalowania, niska wydajność i powtarzalność kodu.

Pozytywny przypadek

Programista użył CASE w jednym wspólnym zapytaniu, tworząc uniwersalny raport z możliwością rozbudowy o nowe statusy poprzez prostą edycję jednego zapytania.

Zalety:

  • Wysoka czytelność, łatwość skalowania, wydajność. Wady:
  • Wymaga znajomości składni CASE i jej niuansów w różnych DBMS.