NULL и трёхзначная логика
↑ 1.1 Реляционная модель и SQL · ← Предыдущая · → Следующая
Зачем это на собесе
NULL — источник самых неочевидных ошибок; на нём строят каверзные вопросы.
Что такое NULL
Отсутствие значения / неизвестное значение, а не ноль и не пустая строка. Любое сравнение с NULL даёт UNKNOWN.
Трёхзначная логика
| A | B | A AND B | A OR B |
|---|---|---|---|
| TRUE | UNKNOWN | UNKNOWN | TRUE |
| FALSE | UNKNOWN | FALSE | UNKNOWN |
| UNKNOWN | UNKNOWN | UNKNOWN | UNKNOWN |
NOT UNKNOWN — UNKNOWN. WHERE пропускает только TRUE, строки с UNKNOWN отбрасываются.
SELECT NULL = NULL; -- NULL (не true)
SELECT NULL IS NULL; -- true
SELECT 1 = NULL; -- NULL
SELECT NULL IS DISTINCT FROM NULL; -- false (NULL-безопасное сравнение)Правила
- проверка:
IS NULL/IS NOT NULL, а не= NULL; x <> 5не вернёт строки, гдеx IS NULL; нужноx IS DISTINCT FROM 5;NOT INс NULL в списке — пустой результат; используйтеNOT EXISTS;- арифметика и конкатенация с NULL дают NULL (
'a' || NULL→ NULL, функцияconcatигнорирует NULL); - агрегаты (
sum,avg,count(col)) игнорируют NULL;count(*)— нет; ORDER BY: NULL считаются больше любого значения (NULLS LASTпо умолчанию при ASC);GROUP BY,DISTINCT,UNIONсчитают NULL равными между собой;UNIQUEдопускает несколько NULL (можно отключить черезNULLS NOT DISTINCT, PostgreSQL 15+);CHECKпропускает NULL (UNKNOWN не нарушает ограничение): ставьтеNOT NULL.
Функции
coalesce(a, b, 'по умолчанию') -- первое не-NULL
nullif(a, 0) -- NULL, если a = 0 (защита от деления на ноль: x / nullif(y, 0))
a IS DISTINCT FROM bКогда NULL допустим
Реально «неизвестно» или «неприменимо» (дата закрытия у открытого тикета). Избегайте NULL для флагов (используйте boolean NOT NULL DEFAULT false) и там, где есть осмысленное значение по умолчанию.
Индексы
B-tree индексирует NULL (можно искать IS NULL). Частичный индекс WHERE deleted_at IS NULL — частый приём.
Вопросы с ответами
Почему
WHERE col <> 5не находит строки с NULL?Сравнение NULL с 5 даёт UNKNOWN, а WHERE берёт только TRUE. Нужно
col IS DISTINCT FROM 5или явноOR col IS NULL.
Что вернёт
SELECT count(*), count(col)для таблицы с NULL?
count(*)— все строки,count(col)— только строки, гдеcolне NULL.
Как защититься от деления на ноль?
x / nullif(y, 0)вернёт NULL вместо ошибки; при необходимости обернуть вcoalesce.