КПКейсПрактика
Все материалы
Продуктовая аналитикагайдстарт

SQL CASE, COALESCE и NULL: как не сломать сегменты и метрики

Разбираем NULL, CASE и COALESCE на задачах аналитика: как группировать пустые значения, создавать сегменты и показывать ноль вместо пропущенных данных.

КПКейсПрактика20 июля 2026 г.10 мин
Два аналитика сортируют карточки данных, отделяя неизвестные значения от понятных сегментов SQL.

Пустое значение в отчёте редко остаётся просто пустым. Оно может означать неизвестный канал, отсутствие платежа, ещё не наступивший период или ошибку загрузки. SQL различает NULL и обычный текст, поэтому неосторожное условие легко убирает часть пользователей из выборки или превращает “нет данных” в ложный ноль.

Коротко

NULL означает отсутствие известного значения, а не пустую строку и не ноль. `CASE` создаёт новое значение по условиям, `COALESCE` выбирает первое непустое значение. Вместе они помогают сделать бизнес-правила явными и сохранить правильный знаменатель метрики.

  • Для проверки NULL используй `IS NULL` или `IS NOT NULL`, а не `= NULL`.
  • `COALESCE(value, 0)` превращает пропуск в ноль только там, где это действительно означает ноль.
  • `CASE` полезен для сегментов, флагов и читаемых категорий.
  • Не смешивай неизвестный канал и органический канал без отдельного решения.
  • Проверяй, изменился ли знаменатель после фильтра по NULL.

NULL — не пустая строка

В таблице `users` у части пользователей может быть неизвестный channel. Это не то же самое, что `channel = ''`, и не то же самое, что `channel = 'organic'`. NULL говорит: значение неизвестно или не было записано.

Три похожих, но разных значения
ЗначениеСмыслКак проверить
NULLзначение отсутствует или неизвестноchannel IS NULL
пустая строкаполе записано, но строка пустаяchannel = ''
0числовой нольamount = 0
Проверить пропущенные каналы
select
  count(*) filter (where channel is null) as missing_channel,
  count(*) filter (where channel = '') as empty_channel,
  count(*) as all_users
from users;

Почему channel = NULL не работает

SQL использует трёхзначную логику: условие может быть TRUE, FALSE или UNKNOWN. Сравнение любого значения с NULL не возвращает TRUE, поэтому `where channel = null` не найдёт пропуски. Нужна специальная проверка `IS NULL`.

Найти пользователей без канала
select
  user_id,
  signup_date,
  channel
from users
where channel is null
order by signup_date, user_id;
Проверка фильтра

Если после добавления WHERE количество пользователей стало меньше, чем ожидалось, отдельно посмотри NULL-группу. Пропуски часто прячутся именно там.

CASE: создать сегмент из условия

`CASE` возвращает значение в зависимости от условий. Он подходит, когда в исходной таблице нет готовой бизнес-категории: можно разделить пользователей по каналу, возрасту когорты или активности. Важно задать `ELSE`, иначе все неописанные случаи станут NULL.

Сгруппировать каналы привлечения
select
  user_id,
  channel,
  case
    when channel = 'paid' then 'paid'
    when channel in ('organic', 'referral') then 'owned'
    when channel is null then 'unknown'
    else 'other'
  end as channel_group
from users
order by user_id;
  • Условия CASE проверяются сверху вниз.
  • Первое совпавшее условие определяет результат.
  • `ELSE` защищает от неожиданных NULL в новой колонке.
  • Категории должны быть взаимоисключающими, если потом считаешь их сумму.

COALESCE: показать отсутствие как ноль

После LEFT JOIN у пользователя без платежа поле `amount` будет NULL. Для отчёта о выручке это обычно нужно показать как 0, но только после того, как ты сохранил пользователя в знаменателе. `COALESCE` выбирает первое значение, которое не является NULL.

Выручка по пользователям, включая тех, кто не платил
select
  u.user_id,
  u.channel,
  coalesce(sum(p.amount), 0) as revenue
from users as u
left join payments as p using (user_id)
group by u.user_id, u.channel
order by revenue desc, u.user_id;
NULL и ноль — не всегда одно и то же

Для суммы платежей отсутствие строк часто можно показать нулём. Для даты последнего платежа NULL обычно означает “платежа не было” и не должен превращаться в произвольную дату.

Как не потерять сегмент в GROUP BY

GROUP BY соберёт все NULL-значения в одну группу. Это полезно для контроля качества, но не всегда хорошо для бизнес-отчёта. Сначала покажи unknown отдельно, оцени размер группы и только потом решай, можно ли распределить её по другим категориям.

Что делать с пропуском
СитуацияРабочее решение
Канал неизвестенпоказать unknown и исправить трекинг
Платежей нетCOALESCE(sum(amount), 0) для денежной метрики
Дата возврата неизвестнаоставить NULL и не подставлять фиктивную дату
Категория не предусмотренаCASE с явным ELSE other
Размер сегментов с явной группой unknown
select
  coalesce(channel, 'unknown') as channel_group,
  count(*) as users_count
from users
group by coalesce(channel, 'unknown')
order by users_count desc;

Частые ошибки

Большинство проблем возникает не из-за синтаксиса, а из-за подмены смысла. Ноль, неизвестность и отсутствие строки могут выглядеть одинаково в таблице, но приводят к разным продуктовым выводам.

  • Проверять NULL через `= NULL`.
  • Использовать COALESCE для дат и создавать ложные значения.
  • Забыть ELSE в CASE и получить новые пропуски.
  • Отфильтровать NULL до расчёта и уменьшить знаменатель.
  • Склеить unknown с organic без проверки качества канала.
  • Считать сумму сегментов и не заметить, что часть строк ушла в NULL.

Мини-практика и следующий шаг

Найди пользователей с неизвестным channel, затем создай channel_group через CASE. После этого присоедини payments и выведи ноль для пользователей без платежей. В конце сравни сумму пользователей по группам с общим количеством строк.

Продолжить чтение
Вся библиотека