NULLIF — это функция «если два значения равны — верни NULL, иначе — первое». На первый взгляд звучит странно (зачем нам специально делать NULL?). Но на практике это идеальный инструмент для двух задач:
- Безопасное деление — превратить «возможный 0 в знаменателе» в
NULL, чтобы не было ошибки.
- Очистка плейсхолдеров — превратить
'', 'unknown', 0, -1 (всё что используют как «нет данных») в нормальный NULL.
NULLIF — антипод COALESCE: одна функция убирает NULL'ы, вторая — создаёт их там, где это удобнее.
Зачем нужен NULLIF
Главный случай — деление без падения запроса:
SELECT total / count FROM stats;
NULLIF(count, 0) превратит 0 в NULL. А деление на NULL в Postgres даёт NULL (не ошибку):
SELECT total / NULLIF(count, 0) FROM stats;
Дальше можно обернуть в COALESCE если хочешь «0 при пусто»:
SELECT COALESCE(total / NULLIF(count, 0), 0) FROM stats;
Базовый синтаксис
NULLIF(значение1, значение2)
- Если
значение1 = значение2 — возвращает NULL.
- Иначе — возвращает
значение1.
SELECT NULLIF(5, 5);
SELECT NULLIF(5, 0);
SELECT NULLIF('', '');
SELECT NULLIF('a', 'b');
Очистка плейсхолдеров
В реальных данных часто встречаются «псевдо-NULL» — значения, которые формально не NULL, но смысл у них такой же:
'' (пустая строка вместо «нет email»)
'unknown', 'N/A', '-' (текстовые плейсхолдеры)
0, -1 (числовые плейсхолдеры для «не указано»)
NULLIF превращает их в настоящий NULL:
SELECT name, NULLIF(email, '') AS email_clean
FROM users;
SELECT name, NULLIF(age, -1) AS age_clean
FROM users;
После такой очистки агрегаты, IS NULL-проверки и COUNT(column) работают «правильно» — они умеют обращаться с NULL'ами, а с плейсхолдерами надо помнить про каждый фильтр отдельно.
Пример с таблицей
Таблица forms:
COUNT(email) нам не покажет «у скольки заполнен email» — потому что '' это валидное значение, не NULL.
SELECT COUNT(email) FROM forms;
SELECT COUNT(NULLIF(email, '')) FROM forms;
То же для phone:
SELECT COUNT(NULLIF(phone, '')) AS users_with_phone FROM forms;
NULLIF + COALESCE — мощная пара
Когда хочешь обработать «и NULL, и пустые», и поставить дефолт — комбо:
SELECT
COALESCE(NULLIF(nickname, ''), full_name, 'Гость') AS display_name
FROM users;
Логика:
- Если
nickname = '' — NULLIF превратит в NULL.
COALESCE пойдёт дальше: возьмёт full_name если есть.
- Если и
full_name пустой/NULL (потребуется ещё один NULLIF, если full_name тоже может быть '') — поставит 'Гость'.
Безопасный CTR (click-through rate)
Реалистичный пример — расчёт CTR (отношение кликов к показам):
SELECT
campaign_id,
ROUND(100.0 * clicks / NULLIF(impressions, 0), 2) AS ctr_percent
FROM ad_stats;
Если у кампании 0 показов — NULLIF(impressions, 0) → NULL, и весь ctr → NULL. Без ошибки. Без NULLIF запрос упадёт на первой кампании без impressions.
Частые ошибки новичков
1. Забывают, что NULLIF сравнивает только на полное равенство. NULLIF(' ', '') НЕ превратит «пробел» в NULL — это разные строки. Если хочешь нормализовать пробелы, сначала TRIM:
NULLIF(TRIM(value), '')
2. NULLIF с числами и строками — типы. NULLIF(age, '0') — Postgres попробует сравнить число и строку. Лучше — оба числа: NULLIF(age, 0).
3. Деление без NULLIF. SELECT total / count пройдёт первые 1000 раз, на 1001-м клиенте с count = 0 упадёт. Привычка: любое деление, где знаменатель не гарантированно > 0 — оборачивай в NULLIF(divisor, 0).
4. Думают, что NULLIF(NULL, NULL) вернёт NULL. Технически да, но это бесполезно. NULLIF(x, x) всегда вернёт NULL, потому что NULL = NULL в SQL даёт NULL (не TRUE), и логика «если равны → NULL» не сработает на двух NULL'ах. На практике это никогда не нужно.
5. Использовать NULLIF в WHERE для проверки «не плейсхолдер». Можно, но обычно длиннее, чем нужно:
WHERE NULLIF(email, '') IS NOT NULL
WHERE email IS NOT NULL AND email <> ''
WHERE email <> ''
NULLIF блестит в SELECT и арифметике, не в WHERE.
Мини-резюме
NULLIF(a, b) возвращает NULL, если a = b. Иначе — a.
- Главное применение — безопасное деление:
total / NULLIF(count, 0).
- Очистка плейсхолдеров:
NULLIF(value, ''), NULLIF(age, -1).
- В паре с
COALESCE — мощный fallback с защитой от пустых строк.
- Сравнивает только на полное равенство — для нормализации пробелов сначала
TRIM.
NULLIF— это функция «если два значения равны — верни NULL, иначе — первое». На первый взгляд звучит странно (зачем нам специально делать NULL?). Но на практике это идеальный инструмент для двух задач:NULL, чтобы не было ошибки.'','unknown',0,-1(всё что используют как «нет данных») в нормальныйNULL.NULLIF— антиподCOALESCE: одна функция убирает NULL'ы, вторая — создаёт их там, где это удобнее.Зачем нужен NULLIF
Главный случай — деление без падения запроса:
-- Если count = 0, упадёт с division by zero SELECT total / count FROM stats;NULLIF(count, 0)превратит0вNULL. А деление наNULLв Postgres даётNULL(не ошибку):SELECT total / NULLIF(count, 0) FROM stats; -- → если count = 0, результат NULL, без ошибки -- → если count = 7, результат 17 / 7 = 2.43Дальше можно обернуть в
COALESCEесли хочешь «0 при пусто»:SELECT COALESCE(total / NULLIF(count, 0), 0) FROM stats;Базовый синтаксис
NULLIF(значение1, значение2)значение1 = значение2— возвращаетNULL.значение1.SELECT NULLIF(5, 5); -- NULL SELECT NULLIF(5, 0); -- 5 SELECT NULLIF('', ''); -- NULL SELECT NULLIF('a', 'b'); -- 'a'Очистка плейсхолдеров
В реальных данных часто встречаются «псевдо-NULL» — значения, которые формально не NULL, но смысл у них такой же:
''(пустая строка вместо «нет email»)'unknown','N/A','-'(текстовые плейсхолдеры)0,-1(числовые плейсхолдеры для «не указано»)NULLIFпревращает их в настоящийNULL:-- Превратить пустые строки в NULL SELECT name, NULLIF(email, '') AS email_clean FROM users; -- Превратить -1 в NULL для возраста SELECT name, NULLIF(age, -1) AS age_clean FROM users;После такой очистки агрегаты,
IS NULL-проверки иCOUNT(column)работают «правильно» — они умеют обращаться с NULL'ами, а с плейсхолдерами надо помнить про каждый фильтр отдельно.Пример с таблицей
Таблица
forms:COUNT(email)нам не покажет «у скольки заполнен email» — потому что''это валидное значение, не NULL.-- "Сколько строк с email" — но '' тоже считается SELECT COUNT(email) FROM forms; -- 3 (даже у Ани, у которой email = '') -- Чисто — превращаем '' в NULL, COUNT отфильтрует SELECT COUNT(NULLIF(email, '')) FROM forms; -- 2То же для phone:
SELECT COUNT(NULLIF(phone, '')) AS users_with_phone FROM forms;NULLIF + COALESCE — мощная пара
Когда хочешь обработать «и NULL, и пустые», и поставить дефолт — комбо:
SELECT COALESCE(NULLIF(nickname, ''), full_name, 'Гость') AS display_name FROM users;Логика:
nickname = ''—NULLIFпревратит вNULL.COALESCEпойдёт дальше: возьмётfull_nameесли есть.full_nameпустой/NULL (потребуется ещё один NULLIF, еслиfull_nameтоже может быть'') — поставит'Гость'.Безопасный CTR (click-through rate)
Реалистичный пример — расчёт CTR (отношение кликов к показам):
SELECT campaign_id, ROUND(100.0 * clicks / NULLIF(impressions, 0), 2) AS ctr_percent FROM ad_stats;Если у кампании 0 показов —
NULLIF(impressions, 0)→NULL, и весьctr→NULL. Без ошибки. БезNULLIFзапрос упадёт на первой кампании без impressions.Частые ошибки новичков
1. Забывают, что
NULLIFсравнивает только на полное равенство.NULLIF(' ', '')НЕ превратит «пробел» в NULL — это разные строки. Если хочешь нормализовать пробелы, сначалаTRIM:NULLIF(TRIM(value), '')2.
NULLIFс числами и строками — типы.NULLIF(age, '0')— Postgres попробует сравнить число и строку. Лучше — оба числа:NULLIF(age, 0).3. Деление без
NULLIF.SELECT total / countпройдёт первые 1000 раз, на 1001-м клиенте сcount = 0упадёт. Привычка: любое деление, где знаменатель не гарантированно > 0 — оборачивай вNULLIF(divisor, 0).4. Думают, что
NULLIF(NULL, NULL)вернёт NULL. Технически да, но это бесполезно.NULLIF(x, x)всегда вернёт NULL, потому чтоNULL = NULLв SQL даётNULL(не TRUE), и логика «если равны → NULL» не сработает на двух NULL'ах. На практике это никогда не нужно.5. Использовать
NULLIFв WHERE для проверки «не плейсхолдер». Можно, но обычно длиннее, чем нужно:-- Длинно WHERE NULLIF(email, '') IS NOT NULL -- Короче WHERE email IS NOT NULL AND email <> '' -- Или совсем коротко при отсутствии NULL'ов WHERE email <> ''NULLIFблестит вSELECTи арифметике, не вWHERE.Мини-резюме
NULLIF(a, b)возвращаетNULL, еслиa = b. Иначе —a.total / NULLIF(count, 0).NULLIF(value, ''),NULLIF(age, -1).COALESCE— мощный fallback с защитой от пустых строк.TRIM.