SQLNULLIFNULLtutorial

Что такое NULLIF в SQL?

NULLIF — это «верни NULL, если два значения равны». Простыми словами: безопасное деление через NULLIF(x, 0), очистка плейсхолдеров типа '' или 'unknown', связка с COALESCE для аккуратной обработки данных. С таблицами и частыми ошибками.

7 мин чтенияСправочникSQL · NULLIF · NULL · tutorial

NULLIF — это функция для ситуации: «если два значения равны, верни NULL, иначе верни первое значение».

Звучит немного необычно: зачем специально превращать что-то в NULL? Но в реальной работе это очень полезный приём. NULLIF помогает там, где в данных есть «опасные» или «псевдопустые» значения.

Чаще всего NULLIF используют для двух задач:

  1. Безопасное деление — чтобы запрос не падал из-за деления на ноль.
  2. Очистка данных — чтобы превращать пустые строки, unknown, N/A, 0, -1 и другие заглушки в настоящий NULL.

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

  • COALESCE заменяет NULL на нормальное значение;
  • NULLIF наоборот создаёт NULL, когда встречает значение-заглушку.

Они как две стороны одной медали: одна функция подставляет запасной вариант, другая аккуратно убирает мусор.

Синтаксис NULLIF

Базовый синтаксис простой:

NULLIF(value1, value2)

Правило такое:

  • если value1 равно value2, результат будет NULL;
  • если они не равны, результатом будет value1.

Примеры:

SELECT NULLIF(5, 5);

Результат:

NULL

Потому что первое значение равно второму.

SELECT NULLIF(5, 0);

Результат:

5

Потому что значения разные, поэтому функция вернула первое значение.

Ещё несколько примеров:

SELECT NULLIF('', '');
SELECT NULLIF('cat', 'dog');
SELECT NULLIF(100, 100);
SELECT NULLIF(100, 50);

Результаты будут такими:

NULL
cat
NULL
100

Главная мысль: NULLIF смотрит на два значения. Если они совпали — прячет первое значение за NULL. Если не совпали — оставляет первое как есть.

Зачем нужен NULLIF

Самый популярный пример — деление.

Представь таблицу со статистикой:

SELECT total / item_count
FROM stats;

На первый взгляд всё нормально. Но что будет, если item_count окажется равен 0?

В PostgreSQL деление на ноль приведёт к ошибке. И запрос упадёт целиком, даже если проблема была всего в одной строке.

Вот здесь и помогает NULLIF:

SELECT total / NULLIF(item_count, 0)
FROM stats;

Что происходит:

  • если item_count = 0, выражение NULLIF(item_count, 0) вернёт NULL;
  • деление на NULL не падает с ошибкой, а даёт NULL;
  • если item_count не равен нулю, деление выполняется как обычно.

Например:

SELECT 17.0 / NULLIF(0, 0);

Результат:

NULL

А здесь знаменатель нормальный:

SELECT 17.0 / NULLIF(7, 0);

Результат:

2.4285714285714286

То есть NULLIF не «лечит» данные магически. Он просто говорит базе: «если знаменатель нулевой, считай результат неизвестным, но не ломай весь запрос».

Безопасное деление на практике

Допустим, у нас есть таблица ad_stats со статистикой рекламных кампаний:

campaign_id clicks impressions
1 120 1000
2 0 0
3 45 300

Нужно посчитать CTR — процент кликов от показов.

Без защиты запрос может быть таким:

SELECT
  campaign_id,
  ROUND(100.0 * clicks / impressions, 2) AS ctr_percent
FROM ad_stats;

Но если у кампании impressions = 0, запрос упадёт из-за деления на ноль.

Правильнее так:

SELECT
  campaign_id,
  ROUND(100.0 * clicks / NULLIF(impressions, 0), 2) AS ctr_percent
FROM ad_stats;

Теперь логика спокойная:

campaign_id clicks impressions ctr_percent
1 120 1000 12.00
2 0 0 NULL
3 45 300 15.00

Для второй кампании CTR не равен нулю. Его просто нельзя честно посчитать, потому что показов не было. Поэтому NULL здесь даже лучше, чем искусственный 0.

0 мог бы означать: «показы были, но никто не кликнул».
NULL означает: «показателя нет, считать нечего».

Это важная разница.

Если вместо NULL нужен 0

Иногда в отчёте хочется показывать не NULL, а 0. Например, чтобы красиво вывести таблицу на дашборде.

Тогда NULLIF можно соединить с COALESCE:

SELECT
  campaign_id,
  COALESCE(ROUND(100.0 * clicks / NULLIF(impressions, 0), 2), 0) AS ctr_percent
FROM ad_stats;

Как это читается:

  1. NULLIF(impressions, 0) защищает от деления на ноль.
  2. Если результат деления стал NULL, COALESCE заменяет его на 0.

Но здесь важно понимать смысл. Для аналитики NULL часто честнее, чем 0. А для интерфейса или простого отчёта 0 может быть удобнее.

Очистка пустых строк через NULLIF

Второй частый сценарий — очистка данных.

В идеальном мире отсутствие значения хранится как NULL. Но в реальных таблицах часто встречается иначе:

  • пустая строка;
  • текстовая заглушка;
  • минус один;
  • ноль;
  • прочерк;
  • слово unknown.

Например, пользователь не указал email, а в таблицу записали пустую строку.

id name email
1 Anna
2 Bob bob@example.com
3 Vera vera@example.com

Для человека пустая строка в email выглядит как «email не указан». Но для SQL это не NULL. Это обычное значение — просто строка без символов.

Из-за этого могут появиться неожиданные результаты.

Например:

SELECT COUNT(email) AS emails_count
FROM forms;

COUNT(email) считает все не-NULL значения. Пустая строка — не NULL, поэтому она тоже попадёт в подсчёт.

Результат будет:

3

Хотя реально email заполнен только у двух пользователей.

Исправляем через NULLIF:

SELECT COUNT(NULLIF(email, '')) AS emails_count
FROM forms;

Теперь пустая строка превращается в NULL, а COUNT её не считает.

Результат:

2

Вот это уже похоже на правду.

Очистка числовых заглушек

Та же история бывает с числами.

Например, в таблице users возраст неизвестен, но вместо NULL кто-то записал -1.

id name age
1 Anna 28
2 Bob -1
3 Vera 34

Если посчитать средний возраст напрямую, -1 испортит результат:

SELECT AVG(age) AS avg_age
FROM users;

Лучше сначала превратить заглушку в NULL:

SELECT AVG(NULLIF(age, -1)) AS avg_age
FROM users;

Почему это работает?

Агрегатная функция AVG игнорирует NULL. Значит, пользователь с неизвестным возрастом не будет портить среднее значение.

То же самое можно делать с другими плейсхолдерами:

SELECT NULLIF(score, 0) AS score_clean
FROM exam_results;

Но здесь нужно быть осторожным. Если 0 — это настоящий результат, превращать его в NULL нельзя. NULLIF хорош только тогда, когда ты точно понимаешь смысл значения-заглушки.

NULLIF для красивого имени пользователя

Очень частая задача в продуктовых таблицах: показать пользователю имя.

Есть nickname, есть full_name, а если ничего нет — нужно вывести запасной вариант.

Проблема: nickname может быть не NULL, а пустой строкой.

SELECT
  COALESCE(NULLIF(nickname, ''), full_name, 'Guest') AS display_name
FROM users;

Разберём по шагам:

  1. NULLIF(nickname, '') превращает пустой nickname в NULL.
  2. COALESCE смотрит дальше и берёт full_name.
  3. Если full_name тоже NULL, возвращается Guest.

Если full_name тоже может быть пустой строкой, его тоже стоит обработать:

SELECT
  COALESCE(
    NULLIF(nickname, ''),
    NULLIF(full_name, ''),
    'Guest'
  ) AS display_name
FROM users;

Теперь запрос устойчивее: он понимает и настоящий NULL, и пустые строки.

NULLIF и пробелы

Важный момент: NULLIF сравнивает значения буквально.

Пустая строка и строка из пробелов — разные вещи.

SELECT NULLIF('   ', '');

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

Почему? Потому что строка ' ' не равна строке ''.

Если нужно считать строку из пробелов пустой, сначала используй TRIM:

SELECT NULLIF(TRIM(email), '') AS email_clean
FROM users;

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

Пример:

SELECT NULLIF(TRIM('   '), '');

Результат:

NULL

Такой приём часто используют при очистке данных из форм, CSV-файлов и внешних интеграций.

NULLIF в SELECT и в WHERE

NULLIF особенно хорошо смотрится в SELECT, когда нужно красиво подготовить значение:

SELECT
  id,
  NULLIF(TRIM(email), '') AS email_clean
FROM users;

Или в арифметике:

SELECT
  total_amount / NULLIF(orders_count, 0) AS avg_order_amount
FROM customer_stats;

В WHERE его тоже можно использовать, но часто есть более простые варианты.

Например, так можно выбрать пользователей с непустым email:

SELECT *
FROM users
WHERE NULLIF(email, '') IS NOT NULL;

Но обычно понятнее написать явно:

SELECT *
FROM users
WHERE email IS NOT NULL
  AND email <> '';

А если ты точно знаешь, что email не бывает NULL, можно ещё короче:

SELECT *
FROM users
WHERE email <> '';

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

  • в SELECT и вычислениях NULLIF часто делает запрос чище;
  • в WHERE иногда лучше написать условие напрямую, чтобы оно читалось проще.

NULLIF и типы данных

У NULLIF оба значения должны нормально сравниваться между собой.

Хорошо:

SELECT NULLIF(age, 0)
FROM users;

Здесь age — число, и 0 — тоже число.

А вот так лучше не писать:

SELECT NULLIF(age, '0')
FROM users;

Здесь age может быть числом, а '0' — строка. PostgreSQL иногда сможет привести типы сам, но лучше не заставлять базу угадывать.

Хорошая привычка: сравнивать число с числом, строку со строкой, дату с датой.

SELECT NULLIF(status, 'unknown') AS status_clean
FROM orders;
SELECT NULLIF(discount_percent, 0) AS discount_percent_clean
FROM orders;

Чем меньше неявных преобразований типов, тем понятнее и надёжнее запрос.

Нюанс с NULL

NULLIF хорошо работает с обычными значениями, но важно помнить про особую логику NULL.

Например:

SELECT NULLIF(NULL, NULL);

Результат будет:

NULL

Но в реальной работе такой пример почти не нужен.

Почему здесь есть нюанс? В SQL выражение NULL = NULL не считается истинным. NULL означает «неизвестно», а два неизвестных значения нельзя честно назвать равными.

Поэтому лучше воспринимать NULLIF как инструмент для обычных значений-заглушек:

NULLIF(email, '')
NULLIF(age, -1)
NULLIF(impressions, 0)
NULLIF(status, 'unknown')

А для проверок на сам NULL используй IS NULL и IS NOT NULL.

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

Забыть про деление на ноль

Самая опасная ошибка — писать деление без защиты, когда знаменатель может быть нулём.

Плохо:

SELECT revenue / orders_count AS avg_order_revenue
FROM shops;

Лучше:

SELECT revenue / NULLIF(orders_count, 0) AS avg_order_revenue
FROM shops;

Если знаменатель гарантированно больше нуля, NULLIF не нужен. Но если такой гарантии нет, лучше защититься.

Чистить пустую строку, но забыть про пробелы

Такой запрос уберёт только настоящую пустую строку:

SELECT NULLIF(email, '') AS email_clean
FROM users;

Но строку из пробелов он не уберёт.

Надёжнее так:

SELECT NULLIF(TRIM(email), '') AS email_clean
FROM users;

Превращать полезный ноль в NULL

Не каждый ноль — плохой.

Например, если clicks = 0, это может быть честная статистика: показы были, кликов не было. Такой ноль не нужно превращать в NULL.

А вот impressions = 0 в знаменателе опасен, потому что на него нельзя делить.

Сравни:

SELECT
  clicks,
  impressions,
  clicks / NULLIF(impressions, 0) AS click_rate
FROM ad_stats;

Здесь мы не трогаем clicks. Мы защищаем только знаменатель.

Ожидать, что NULLIF исправит все данные

NULLIF сравнивает только с одним конкретным значением.

SELECT NULLIF(status, 'unknown') AS status_clean
FROM orders;

Этот запрос превратит в NULL только unknown.

Но он не обработает N/A, none, - или пустую строку. Если заглушек много, может понадобиться CASE:

SELECT
  CASE
    WHEN status IN ('unknown', 'N/A', 'none', '-') THEN NULL
    ELSE status
  END AS status_clean
FROM orders;

NULLIF прекрасен, когда нужно убрать одну конкретную заглушку. Если правил много, лучше использовать CASE.

Главное

NULLIF — маленькая, но очень практичная функция. Она помогает писать запросы, которые не падают на плохих данных и аккуратно отличают настоящее значение от заглушки.

Запомни главный шаблон:

NULLIF(value, placeholder)

Если value совпал с placeholder, получится NULL. Если не совпал — останется value.

Самые полезные применения:

total / NULLIF(item_count, 0)
COUNT(NULLIF(email, ''))
NULLIF(TRIM(name), '')
COALESCE(NULLIF(nickname, ''), 'Guest')

Для новичка NULLIF особенно ценен тем, что делает запросы безопаснее. Вместо того чтобы надеяться, что в данных не будет нулей, пустых строк и странных заглушек, ты явно описываешь, как с ними поступать. А это уже шаг от «запрос вроде работает» к нормальному, взрослому SQL.

Закрепи на практике

Решай задачи в SQL-тренажёре с мгновенной проверкой и подсказками.

Открыть тренажёр