SELECT: niġbdu d-data li neħtieġu

CASE: loġika dritt fis-SELECT

22 min
What you'll learn
  • timmarka r-riżultat b'fergħat CASE WHEN ... THEN ... ELSE ... END dritt fis-SELECT
  • tagħraf il-forma ta' tfittxija ta' CASE mis-sempliċi u tispjega għaliex WHEN NULL fil-forma sempliċi qatt ma jaħdem
  • tordna r-ringieli skont l-ordni tan-negozju, billi tqiegħed CASE ġewwa ORDER BY

Jekk — mela

Arkivista veru mhux biss jaqra — jimmarka. Fuq il-kapsuli bid-data jiddendlu tikketti tad-dawl, biex min jiġi fil-maħżen warajk jifhem ir-riżultat b'daqqa t'għajn. Fil-lingwaġġ tal-mistoqsijiet it-tikketti jiddendel CASE.

CASE iżid fil-mistoqsija fergħa żgħira: «jekk il-kundizzjoni hija vera — uri ħaġa, inkella oħra». Hekk tista' dritt fir-riżultat tbiddel il-prezzijiet minn products f'segmenti ċari, u l-istatusi minn orders — f'tikketti tan-negozju.

CASE
  WHEN kundizzjoni1 THEN valur1
  WHEN kundizzjoni2 THEN valur2
  ELSE valur_awtomatiku
END

Il-kundizzjonijiet jiġu verifikati minn fuq għal isfel: jaħdem l-ewwel WHEN adattat. Is-sintassi tidher itwal minn filtru ordinarju, iżda r-riżultat mill-ewwel jitkellem bil-lingwaġġ tal-bniedem: rħis, medju, għali.

QUERY: L-immarkar huwa l-opinjoni tal-arkivista miktuba fuq il-fatti. Dendel it-tikketti b'tali mod li ma jkollokx għax tistħi minnhom wara mitt sena.

Silwett ta' kadett idendel tikketti tad-dawl ta' tliet kuluri fuq il-kapsuli bid-data li jtiru
CASE — it-tikketti tal-arkivista: kull ringiela tieħu marka skont l-ewwel kundizzjoni li taħdem.
price = 4990WHENprice < 1000yes'cheap'noWHENprice < 5000yes'mid'ELSE'pricey'the first true WHEN wins
CASE jivverifika l-fergħat minn fuq għal isfel: ir-ringiela tieħu l-valur tal-ewwel WHEN li jaħdem, il-bqija jmur lil ELSE.
Naqsmu l-prodotti f'segmenti ta' prezz skont price — u l-katalgu jinqara bħal rapport lest.
Query result
namepricesegment
Пазл 1000 деталей890.00дешёвый
Футболка базовая990.00дешёвый
Мягкая игрушка кот1290.00дешёвый
SQL для профи1290.00дешёвый
Чистый код1490.00дешёвый
Алгоритмы на практике1690.00средний
Плед флисовый1890.00средний
Конструктор кубики1990.00средний
Настольная лампа2190.00средний
Веб-камера HD2790.00средний
Худи оверсайз2990.00средний
Радиоуправляемая машина3290.00средний
Портативная колонка3490.00средний
Набор посуды3590.00средний
Джинсы слим3990.00средний
Кофеварка капельная4290.00дорогой
Механическая клавиатура4590.00дорогой
Кроссовки беговые5490.00дорогой
Беспроводные наушники5990.00дорогой
Смарт-часы8990.00дорогой

CASE проверяет ветки сверху вниз

Самая важная деталь CASE:

срабатывает первый подходящий WHEN.

Как только SQL нашёл ветку, условие которой вернуло TRUE, он берёт значение после THEN и дальше ветки уже не проверяет.

Посмотрим на пример:

CASE
  WHEN price < 4000 THEN 'не дорогой'
  WHEN price < 1500 THEN 'дешёвый'
  ELSE 'дорогой'
END

На первый взгляд кажется, что товар за 900 должен получить метку 'дешёвый'.

Но он её не получит.

Почему?

Для цены 900 первое условие уже верно:

price < 4000

Значит, CASE сразу вернёт:

не дорогой

и до ветки:

WHEN price < 1500 THEN 'дешёвый'

уже не дойдёт.

Поэтому порядок веток — это не оформление. Это часть логики.

Для диапазонов обычно идут от более узкого условия к более широкому:

CASE
  WHEN price < 1500 THEN 'дешёвый'
  WHEN price < 4000 THEN 'средний'
  ELSE 'дорогой'
END

Так товар за 900 попадёт в первую ветку, а товар за 2500 — во вторую.

Что делает ELSE

ELSE — это запасной вариант.

Он срабатывает, если ни один WHEN не подошёл.

Например:

CASE
  WHEN price < 1500 THEN 'дешёвый'
  WHEN price < 4000 THEN 'средний'
  ELSE 'дорогой'
END

Если цена 7200, первые два условия не подходят:

7200 < 1500  -- нет
7200 < 4000  -- нет

Значит, вернётся значение из ELSE:

дорогой

А что будет, если ELSE не написать?

CASE
  WHEN price < 1500 THEN 'дешёвый'
  WHEN price < 4000 THEN 'средний'
END

Если цена 7200, ни один WHEN не сработает. Запасного варианта нет.

В такой ситуации CASE вернёт NULL.

То есть:

если ни одна ветка не подошла и ELSE отсутствует, результатом будет NULL.

Для отчётов часто лучше писать явный ELSE, чтобы результат был понятнее человеку:

ELSE 'без сегмента'

или:

ELSE 'дорогой'

Выбор зависит от бизнес-логики.

CASE — это выражение, а не отдельная команда

CASE возвращает значение.

Это значит, что его можно использовать там, где SQL ждёт значение:

  • в SELECT;
  • в ORDER BY;
  • иногда в WHERE;
  • внутри вычислений;
  • внутри в следующих модулях.

В этом уроке главный сценарий — SELECT.

SELECT
    name,
    price,
    CASE
      WHEN price < 1500 THEN 'дешёвый'
      WHEN price < 4000 THEN 'средний'
      ELSE 'дорогой'
    END AS segment
FROM products;

Здесь CASE работает как вычисляемый столбец.

Можно думать о нём так:

для каждой строки посчитай новое значение по условиям

У каждой строки получится свой результат.

У товара за 900 — 'дешёвый'.
У товара за 2500 — 'средний'.
У товара за 7200 — 'дорогой'.

CASE не фильтрует строки сам по себе. Он не похож на WHERE.

WHERE решает:

оставить строку или убрать?

CASE решает:

какое значение показать для этой строки?

CASE для бизнесовых меток

CASE часто используют, чтобы превратить технические значения в понятные человеку подписи.

Например, в таблице orders есть статусы:

idstatus
1paid
2pending
3cancelled
4refunded

Для базы такие значения удобны: короткие, стабильные, на английском.

Но в отчёте можно показать русские метки:

SELECT
    id,
    status,
    CASE
      WHEN status = 'paid' THEN 'оплачен'
      WHEN status = 'pending' THEN 'ожидает оплаты'
      WHEN status = 'cancelled' THEN 'отменён'
      WHEN status = 'refunded' THEN 'возврат'
      ELSE 'неизвестный статус'
    END AS status_label
FROM orders;

Результат:

idstatusstatus_label
1paidоплачен
2pendingожидает оплаты
3cancelledотменён
4refundedвозврат

Такой запрос не меняет status в таблице.

Он просто добавляет рядом понятную расшифровку.

Это удобно для отчётов, выгрузок, и учебных задач, где результат должен говорить языком человека.

Поисковая форма CASE

Форма, которую мы использовали выше, называется поисковой:

CASE
  WHEN условие1 THEN значение1
  WHEN условие2 THEN значение2
  ELSE значение_по_умолчанию
END

Её называют searched CASE.

В каждой ветке после WHEN можно писать полноценное условие:

WHEN price < 1500 THEN 'дешёвый'
WHEN status = 'paid' THEN 'оплачен'
WHEN stock = 0 THEN 'нет в наличии'
WHEN price < 1500 AND stock > 0 THEN 'дешёвый и есть в наличии'

Поисковая форма гибкая. В ней можно:

  • сравнивать разные столбцы;
  • использовать AND и OR;
  • проверять NULL через IS NULL;
  • строить диапазоны;
  • описывать сложные бизнес-правила.

Например:

SELECT
    name,
    price,
    stock,
    CASE
      WHEN stock = 0 THEN 'нет в наличии'
      WHEN price < 1500 AND stock > 0 THEN 'дешёвый товар в наличии'
      WHEN price >= 1500 AND stock > 0 THEN 'товар в наличии'
      ELSE 'проверь данные'
    END AS product_note
FROM products;

Здесь каждая ветка — отдельное условие.

Именно поисковая форма чаще всего удобнее для новичков, потому что она прямо показывает логику.

Простая форма CASE

У CASE есть вторая форма — простая.

Она удобна, когда мы сравниваем один и тот же столбец с несколькими точными значениями.

Например, вместо такого запроса:

CASE
  WHEN status = 'paid' THEN 'оплачен'
  WHEN status = 'pending' THEN 'ожидает'
  WHEN status = 'cancelled' THEN 'отменён'
  WHEN status = 'refunded' THEN 'возврат'
  ELSE 'другой статус'
END

можно написать короче:

CASE status
  WHEN 'paid' THEN 'оплачен'
  WHEN 'pending' THEN 'ожидает'
  WHEN 'cancelled' THEN 'отменён'
  WHEN 'refunded' THEN 'возврат'
  ELSE 'другой статус'
END

Здесь выражение status написано один раз:

CASE status

А дальше перечислены значения, с которыми его нужно сравнить:

WHEN 'paid' THEN 'оплачен'
WHEN 'pending' THEN 'ожидает'

Простая форма читается как таблица соответствий:

statusstatus_label
paidоплачен
pendingожидает
cancelledотменён
refundedвозврат

Она хорошо подходит для простого маппинга:

если значение равно этому — покажи такую подпись.

Простая и поисковая форма: в чём разница

Сравним две формы рядом.

Поисковая форма:

CASE
  WHEN status = 'paid' THEN 'оплачен'
  WHEN status = 'pending' THEN 'ожидает'
  ELSE 'другой статус'
END

Простая форма:

CASE status
  WHEN 'paid' THEN 'оплачен'
  WHEN 'pending' THEN 'ожидает'
  ELSE 'другой статус'
END

Результат может быть одинаковым.

Но логика записи разная.

В простой форме SQL сам сравнивает выражение после CASE со значениями после WHEN:

status = 'paid'
status = 'pending'

В поисковой форме ты сам пишешь условия полностью:

WHEN status = 'paid'
WHEN status = 'pending'

Простая форма короче, когда условия однотипные.

Поисковая форма гибче, когда условия разные.

Например, такую логику простой формой нормально не выразить:

CASE
  WHEN status IS NULL THEN 'статус не указан'
  WHEN status = 'paid' AND paid_at IS NOT NULL THEN 'оплачен'
  WHEN status = 'pending' THEN 'ожидает'
  ELSE 'проверь заказ'
END

Здесь есть проверка NULL, проверка другого столбца paid_at и составное условие через AND.

Для таких случаев выбирают поисковую форму.

Практическое правило:

Если нужно сопоставить один столбец с набором точных значений — можно использовать простой CASE.

Если нужны условия, диапазоны, NULL, несколько столбцов или AND/OR — используй поисковый CASE.

Почему WHEN NULL в простом CASE не работает

У простой формы есть важный подводный камень.

Допустим, в orders.status иногда бывает NULL.

Новичок может написать так:

CASE status
  WHEN 'paid' THEN 'оплачен'
  WHEN NULL THEN 'статус не указан'
  ELSE 'другой статус'
END

Кажется, что ветка:

WHEN NULL THEN 'статус не указан'

должна сработать, если status равен NULL.

Но она не сработает никогда.

Почему?

Простая форма CASE status WHEN ... сравнивает status со значениями веток через обычное равенство =.

То есть ветка WHEN NULL по смыслу превращается в:

status = NULL

А из урока про NULL ты уже знаешь:

status = NULL

не даёт TRUE.

Оно даёт UNKNOWN.

А ветка WHEN срабатывает только когда условие даёт TRUE.

Поэтому для проверки NULL нужна поисковая форма:

CASE
  WHEN status IS NULL THEN 'статус не указан'
  WHEN status = 'paid' THEN 'оплачен'
  WHEN status = 'pending' THEN 'ожидает'
  ELSE 'другой статус'
END

Здесь проверка написана правильно:

status IS NULL

Главное правило:

NULL в CASE проверяют через IS NULL, а значит — поисковой формой.

Простая форма CASE превращает технические статусы заказов в понятные подписи.
Query result
idstatusstatus_label
1paidоплачен
2paidоплачен
3cancelledотменён
4paidоплачен
5paidоплачен
6paidоплачен
7pendingожидает
8paidоплачен
9paidоплачен
10paidоплачен
11paidоплачен
12paidоплачен
13pendingожидает
14paidоплачен
15paidоплачен
16paidоплачен
17pendingожидает
18paidоплачен
19paidоплачен
20paidоплачен

Значения THEN и ELSE должны быть совместимы

CASE возвращает одно значение.

Но разные ветки могут возвращать разные варианты этого значения.

Например:

CASE
  WHEN price < 1500 THEN 'дешёвый'
  WHEN price < 4000 THEN 'средний'
  ELSE 'дорогой'
END

Все ветки возвращают текст. Это нормально.

'дешёвый'
'средний'
'дорогой'

А вот такой CASE выглядит подозрительно:

CASE
  WHEN price < 1500 THEN 'дешёвый'
  ELSE 0
END

В одной ветке возвращается текст:

'дешёвый'

В другой — число:

0

Базе нужно решить, какой тип будет у результата CASE: текстовый или числовой. В разных ситуациях это может привести к ошибке или неочевидному приведению типов.

Поэтому хорошая привычка:

ветки THEN и ELSE должны возвращать значения одного понятного типа.

Если делаешь текстовую метку — возвращай текст во всех ветках:

CASE
  WHEN price < 1500 THEN 'дешёвый'
  ELSE 'не дешёвый'
END

Если делаешь числовой ранг — возвращай числа во всех ветках:

CASE
  WHEN status = 'paid' THEN 1
  WHEN status = 'pending' THEN 2
  ELSE 3
END

Это особенно важно в ORDER BY, где CASE часто возвращает числовой приоритет.

CASE в ORDER BY: сортировка по бизнес-порядку

Иногда обычная сортировка не подходит.

Например, есть статусы заказов:

paid
pending
cancelled
refunded

Если отсортировать их по алфавиту:

SELECT id, status
FROM orders
ORDER BY status;

получится технический порядок по тексту.

Но бизнесу может быть нужен другой порядок:

  1. сначала оплаченные;
  2. потом ожидающие;
  3. потом отменённые;
  4. потом возвраты;
  5. потом всё остальное.

Алфавит не знает этого смысла. Зато его можно задать через CASE:

SELECT id, status
FROM orders
ORDER BY
  CASE status
    WHEN 'paid' THEN 1
    WHEN 'pending' THEN 2
    WHEN 'cancelled' THEN 3
    WHEN 'refunded' THEN 4
    ELSE 5
  END,
  id;

Здесь CASE возвращает числовой приоритет:

statuspriority
paid1
pending2
cancelled3
refunded4
другое5

ORDER BY сортирует по этому приоритету.

А id в конце нужен для стабильного порядка внутри одинаковых статусов:

ORDER BY CASE ... END, id

Так строки с одинаковым статусом будут отсортированы по id, а не в неопределённом порядке.

Сортируем заказы не по алфавиту статуса, а по бизнес-приоритету: оплаченные выше ожидающих, потом отменённые и возвраты.
Query result
idstatus
1paid
2paid
4paid
5paid
6paid
8paid
9paid
10paid
11paid
12paid
14paid
15paid
16paid
18paid
19paid
20paid
21paid
22paid
24paid
27paid

Можно показать метку и сортировать по той же логике

Иногда удобно одновременно:

  • показать человеку понятную метку;
  • отсортировать строки по бизнес-порядку.

Например:

SELECT
    id,
    status,
    CASE status
      WHEN 'paid' THEN 'оплачен'
      WHEN 'pending' THEN 'ожидает'
      WHEN 'cancelled' THEN 'отменён'
      WHEN 'refunded' THEN 'возврат'
      ELSE 'другой статус'
    END AS status_label
FROM orders
ORDER BY
    CASE status
      WHEN 'paid' THEN 1
      WHEN 'pending' THEN 2
      WHEN 'cancelled' THEN 3
      WHEN 'refunded' THEN 4
      ELSE 5
    END,
    id;

В SELECT CASE создаёт текстовую метку:

status_label

В ORDER BY другой CASE создаёт числовой приоритет сортировки.

Почему не сортировать по самой русской метке?

Потому что алфавитный порядок меток снова может не совпасть с бизнес-логикой.

Например, по алфавиту 'возврат' может оказаться раньше 'оплачен', но бизнесу нужно наоборот.

Поэтому для отображения используют текст, а для порядка — числовой ранг.

Главное правило CASE

CASE проверяет ветки сверху вниз и возвращает результат первого WHEN, который дал TRUE.

CASE
  WHEN price < 1500 THEN 'дешёвый'
  WHEN price < 4000 THEN 'средний'
  ELSE 'дорогой'
END

Порядок условий важен.

Если ни один WHEN не сработал и ELSE не написан, результатом будет NULL.

Для проверки NULL используй поисковую форму:

CASE
  WHEN status IS NULL THEN 'статус не указан'
  ELSE 'статус есть'
END

Не рассчитывай на WHEN NULL в простой форме.

Interview question

Mistoqsija mill-intervista: b'xiex CASE sempliċi huwa differenti minn dak ta' tfittxija, u għaliex il-fergħa WHEN NULL f'CASE sempliċi qatt ma taħdem?

Tweġiba b'saħħitha: CASE sempliċi (CASE x WHEN a THEN …) iqabbel espressjoni waħda mal-valuri tal-fergħat permezz ta' =; dak ta' tfittxija (CASE WHEN kundizzjoni THEN …) jaċċetta f'kull fergħa kundizzjoni arbitrarja. WHEN NULL fil-forma sempliċi jinfetaħ f'x = NULL, li jagħti , u mhux TRUE — il-fergħa hija mejta. jiġi verifikat bil-forma ta' tfittxija: WHEN x IS NULL THEN …. Il-forma ta' tfittxija kapaċi tagħmel dak kollu li tagħmel is-sempliċi, għalhekk f'każijiet dubjużi tagħżel lilha.

Check yourself
Что вернёт CASE, если ни один WHEN не сработал и ELSE не написан?
Check yourself
Какой WHEN сработает первым?
CASE
  WHEN price < 4000 THEN 'не дорогой'
  WHEN price < 1500 THEN 'дешёвый'
  ELSE 'дорогой'
END
Если price = 900.
Check yourself
Какая форма нужна, чтобы правильно проверить NULL?
Check yourself
Где можно использовать CASE, чтобы отсортировать статусы в бизнес-порядке?

КВЕРИ: CASE — это не просто «если — то». Это способ объяснить архиву, как человек должен читать сырые факты.