Pewnie już spotkałeś się z wyrażeniami warunkowymi w językach programowania: if-else, switch-case i podobne konstrukcje. W SQL mamy swój własny sposób na warunki: to wyrażenie CASE. Pozwala ono podejmować decyzje bezpośrednio w zapytaniach: jeśli warunek jest spełniony, zrób jedno, jeśli nie — coś innego.
Konstrukcja CASE jest szczególnie przydatna, gdy musisz pracować z danymi, które mogą mieć wartości NULL. Składnia jest prosta jak ściąga, zobaczmy:
CASE
WHEN warunek1 THEN wynik1
WHEN warunek2 THEN wynik2
...
ELSE wynik_domyslny
END
Brzmi logicznie, nie? "Jeśli jest tak, zrób to, w przeciwnym razie zrób coś innego". Łatwo zapamiętać: "WHEN — to jeśli, THEN — co zrobić, ELSE — co zrobić, jeśli nic nie pasuje".
Przykład: klasyfikacja produktów
Wyobraź sobie, że mamy tabelę products, w której jest kolumna price, i chcemy podzielić produkty na grupy w zależności od ich ceny.
| id | name | price |
|---|---|---|
| 1 | Magic Wand | 120 |
| 2 | Potion Set | 45 |
| 3 | Crystal Ball | 75 |
| 4 | Enchanted Map | NULL |
| 5 | Broomstick | 99 |
| 6 | Spell Book | 180 |
SELECT
name AS product_name,
price,
CASE
WHEN price IS NULL THEN 'Nieznany'
WHEN price < 50 THEN 'Budżetowy'
WHEN price BETWEEN 50 AND 100 THEN 'Standardowy'
ELSE 'Premium'
END AS price_category
FROM products;
Co tu się dzieje?
- Jeśli cena
priceproduktu nie jest podanaNULL, wyświetlamy kategorię jako'Nieznany'. - Jeśli cena jest mniejsza niż 50, produkt jest budżetowy
'Budżetowy'. - Jeśli cena jest w zakresie od 50 do 100, trafia do kategorii
'Standardowy'. - Wszystko inne — to produkty premium
'Premium'.
Oto wynik:
| product_name | price | price_category |
|---|---|---|
| Magic Wand | 120 | Premium |
| Potion Set | 45 | Budżetowy |
| Crystal Ball | 75 | Standardowy |
| Enchanted Map | NULL | Nieznany |
| Broomstick | 99 | Standardowy |
| Spell Book | 180 | Premium |
SQL, jak czarodziej, czyta wiersze tabeli products, stosuje się do każdego i sprytnie klasyfikuje.
Praca z NULL w CASE WHEN
Często zdarza się, że w danych są brakujące wartości (siema, NULL), i trzeba je czymś zastąpić. Na przykład mamy tabelę users z kolumną email, i dla użytkowników bez maila chcemy wyświetlić 'Nie podano'.
| user_id | name | |
|---|---|---|
| 1 | Alex Lin | alex@example.com |
| 2 | Maria Chi | NULL |
| 3 | Anna Song | anna@magic.com |
| 4 | Otto Art | NULL |
| 5 | John Smith | john@wizard.org |
SELECT
user_id,
name,
CASE
WHEN email IS NULL THEN 'Nie podano'
ELSE email
END AS email_address
FROM users;
| user_id | name | email_address |
|---|---|---|
| 1 | Alex Lin | alex@example.com |
| 2 | Maria Chi | Nie podano |
| 3 | Anna Song | anna@magic.com |
| 4 | Otto Art | Nie podano |
| 5 | John Smith | john@wizard.org |
SQL tutaj działa jak szaman, ożywiając półpuste wiersze. Jeśli email nie istnieje, zamienia go na tekst 'Nie podano'. Jeśli email jest — po prostu go zostawia.
Wyrażenia warunkowe z liczbami
Czasem trzeba nie tylko podmienić wartości, ale też zbudować nową logikę. Na przykład, mamy tabelę students z kolumnami score (ocena) i name.
| name | score |
|---|---|
| Alex Lin | 95 |
| Maria Chi | 82 |
| Anna Song | 48 |
| Otto Art | NULL |
| John Smith | 67 |
| Zoe Black | 30 |
Chcemy ocenić studentów jako "Świetny", "Zaliczony" i "Niezaliczony", w zależności od ich punktów.
SELECT
name AS student_name,
score,
CASE
WHEN score IS NULL THEN 'Brak Wyniku'
WHEN score >= 90 THEN 'Świetny'
WHEN score >= 50 THEN 'Zaliczony'
ELSE 'Niezaliczony'
END AS performance_category
FROM students;
| student_name | score | performance_category |
|---|---|---|
| Alex Lin | 95 | Świetny |
| Maria Chi | 82 | Zaliczony |
| Anna Song | 48 | Niezaliczony |
| Otto Art | NULL | Brak Wyniku |
| John Smith | 67 | Zaliczony |
| Zoe Black | 30 | Niezaliczony |
SQL grzecznie rozdziela studentów, jak surowy egzaminator: 90 punktów i więcej — tylko "Świetny", od 50 — "Zaliczony". A jak punktów brakuje... no, wiadomo.
Grupowanie i obsługa NULL
Praca z grupami danych — to kolejny obszar, gdzie CASE WHEN błyszczy. Wyobraź sobie, że mamy tabelę orders, i chcemy policzyć łączną liczbę zamówień według statusu, wliczając zamówienia ze statusem NULL.
| order_id | status |
|---|---|
| 1 | Completed |
| 2 | Pending |
| 3 | NULL |
| 4 | Shipped |
| 5 | Completed |
| 6 | NULL |
| 7 | Pending |
| 8 | Completed |
| 9 | Shipped |
| 10 | NULL |
SELECT
CASE
WHEN status IS NULL THEN 'Brak Statusu'
ELSE status
END AS order_status,
COUNT(*)
FROM orders
GROUP BY
CASE
WHEN status IS NULL THEN 'Brak Statusu'
ELSE status
END;
To zapytanie elegancko obsługuje puste statusy NULL i wstawia 'Brak Statusu' zamiast nich, a także liczy łączną liczbę zamówień w każdej grupie. Oto wynik:
| order_status | count |
|---|---|
| Completed | 3 |
| Pending | 2 |
| Shipped | 2 |
| Brak Statusu | 3 |
Praktyczne przypadki: "Sztuczki" z CASE WHEN
Przykład 1: Sortowanie z uwzględnieniem NULL
Czasem wartości NULL trzeba wyświetlić albo na początku, albo na końcu posortowanej listy. To często się przydaje np. w listach zadań, gdzie priorytetowe zadania mają być wyżej, a zadania bez priorytetu (NULL) na końcu.
| task_id | task_name | priority |
|---|---|---|
| 1 | Fix bugs | 1 |
| 2 | Update documentation | 3 |
| 3 | Plan sprint | NULL |
| 4 | Code review | 2 |
| 5 | Organize meeting | NULL |
| 6 | Deploy release | 1 |
SELECT
task_name,
priority,
CASE
WHEN priority IS NULL THEN 1
ELSE 0
END AS priority_sort
FROM tasks
ORDER BY priority_sort ASC, priority ASC;
Dodaliśmy tu "wirtualną" kolumnę priority_sort, która ustawia NULL na końcu, a resztę sortuje rosnąco.
| task_name | priority | priority_sort |
|---|---|---|
| Deploy release | 1 | 0 |
| Fix bugs | 1 | 0 |
| Code review | 2 | 0 |
| Update documentation | 3 | 0 |
| Plan sprint | NULL | 1 |
| Organize meeting | NULL | 1 |
Przykład 2: Obliczenia z uwzględnieniem NULL
Wyobraź sobie, że liczymy końcową sumę zamówienia w tabeli orders, gdzie w kolumnie discount zniżka może być NULL, jeśli jej nie ma.
| order_id | total_price | discount |
|---|---|---|
| 101 | 100 | 10 |
| 102 | 200 | NULL |
| 103 | 150 | 15 |
| 104 | 120 | NULL |
| 105 | 80 | 5 |
Musimy zamienić NULL na 0, żeby obliczenia się nie wywaliły.
SELECT
order_id,
total_price,
discount,
total_price -
CASE
WHEN discount IS NULL THEN 0
ELSE discount
END AS final_price
FROM orders;
| order_id | total_price | discount | final_price |
|---|---|---|---|
| 101 | 100 | 10 | 90 |
| 102 | 200 | NULL | 200 |
| 103 | 150 | 15 | 135 |
| 104 | 120 | NULL | 120 |
| 105 | 80 | 5 | 75 |
Ten magiczny trik z CASE pozwala mieć pewność, że NULL nie rozwali nam obliczeń.
Dla orderid = 101: discount = 10. finalprice = 100 - 10 = 90. Dla orderid = 102: discount = NULL. CASE zwraca 0. finalprice = 200 - 0 = 200. Dla orderid = 103: discount = 15. finalprice = 150 - 15 = 135. Dla orderid = 104: discount = NULL. CASE zwraca 0. finalprice = 120 - 0 = 120. Dla orderid = 105: discount = 5. finalprice = 80 - 5 = 75.
Przykład 3: Wyświetlanie statusów użytkowników
W codziennej pracy często trzeba pokazać status użytkownika (np. "Aktywny" albo "Oczekuje") lub wskazać brak danych. Na przykład w tabeli users jest kolumna last_login, gdzie trzymana jest data ostatniego logowania.
| user_id | name | last_login |
|---|---|---|
| 1 | Alex Lin | 2024-12-10 |
| 2 | Maria Chi | 2025-04-20 |
| 3 | Anna Song | NULL |
| 4 | Otto Art | 2025-05-01 |
| 5 | Liam Park | 2025-05-25 |
SELECT
user_id,
name,
CASE
WHEN last_login IS NULL THEN 'Nigdy nie logował się'
WHEN last_login < CURRENT_DATE - INTERVAL '30 days' THEN 'Nieaktywny'
ELSE 'Aktywny'
END AS user_status
FROM users;
Z tym zapytaniem system admina ożywa: użytkowników bez logowania nazywa "Nigdy nie logował się", a tych, którzy dawno nie byli, uznaje za "Nieaktywny". Reszta jest aktywna!
| user_id | name | user_status |
|---|---|---|
| 1 | Alex Lin | Nieaktywny |
| 2 | Maria Chi | Nieaktywny |
| 3 | Anna Song | Nigdy nie logował się |
| 4 | Otto Art | Nieaktywny |
| 5 | Liam Park | Aktywny |
Typowe błędy i jak ich unikać
Brak ELSE: Jeśli nie dodasz ELSE, SQL po prostu zwróci NULL, jeśli żaden warunek nie będzie spełniony. To nie zawsze to, czego chcesz. Dlatego lepiej zawsze jawnie podać ELSE, nawet jeśli jesteś pewien, że ogarnąłeś wszystkie przypadki.
CASE
WHEN condition THEN result
-- ELSE 'Wartość domyślna' -- nie zapomnij!
END
Złożone warunki bez nawiasów: Jeśli masz kilka złożonych warunków z AND, OR albo NOT, zawsze używaj nawiasów. Bez nich twój query może zacząć "myśleć" źle.
CASE
WHEN (column1 IS NOT NULL AND column2 > 5) THEN 'Poprawny'
ELSE 'Niepoprawny'
END
Praca z NULL: Pamiętaj, że NULL nigdy nie jest równy (=) czemukolwiek. Na przykład:
CASE
WHEN column = NULL THEN 'Nope!' -- Błąd!
WHEN column IS NULL THEN 'Poprawnie!' -- O to chodzi.
END
GO TO FULL VERSION