Объяснение значений NULL в SQL: IS NULL, COALESCE, NULLIF и ловушки, которых следует избегать

  1. Главная
  2. Блог
  3. Базы данных и SQL

Базы данных и SQL

Объяснение значений NULL в SQL: IS NULL, COALESCE, NULLIF и ловушки, которых следует избегать

NULL вызывает некоторые из самых тихих ошибок в SQL: запросы выполняются без ошибок, но теряют строки или искажают средние значения. Узнайте, как на самом деле ведет себя NULL, на проверенных примерах и как с ним безопасно обращаться.

9 мин чтения
Объяснение значений SQL NULL: IS NULL, COALESCE, NULLIF и ловушки, которых следует избегать

Главное

  • NULL означает отсутствующее или неизвестное значение. Это не ноль и не пустая строка.
  • Проверьте это с помощью IS NULL, потому что = NULL никогда ничему не соответствует.
  • Большинство вычислений с NULL дают NULL, а WHERE сохраняет только те строки, которые определенно верны.
  • Агрегаты пропускают NULL, что может незаметно изменить среднее значение.

Запрос выполняется без ошибок и возвращает разумное число, но строки отсутствуют, а среднее значение неверно. Значения SQL NULL очень часто являются причиной. NULL не ведет себя как обычное значение, и это бросается в глаза как новичкам, так и опытным разработчикам.

В этом руководстве показано, как именно ведет себя NULL. Мы выполнили каждый запрос в SQLite 3.50, и показанные результаты получены в результате этого запуска. Большая часть такого поведения соответствует стандартному SQL, и мы отмечаем, где базы данных различаются, например порядок сортировки NULL и обработка пустых строк в Oracle.

Что такое НУЛЬ?

NULL — это маркер, означающий, что значение отсутствует или неизвестно. Это не значение в обычном понимании, поэтому это разные вещи:

Значение Значение Пример столбца «Телефон»
НОЛЬ Неизвестно или не указано Мы не знаем номер
'' Известное пустое текстовое значение Отправлено как пустое
0 Число ноль Не актуально для текста, но реально для сумм

Разница особенно важна для сумм. Бонус 0 означает, что человек ничего не получил. НУЛЕВОЙ бонус означает, что никто еще не знает.

Пример таблицы

CREATE TABLE employees (
  id INT, name TEXT, phone TEXT, bonus INT, manager_id INT
);

INSERT INTO employees VALUES
 (1,'Meera','555-0101',500,NULL),
 (2,'Arjun',NULL,300,1),
 (3,'Sara','',NULL,1),
 (4,'Ravi',NULL,NULL,2);

Обратите внимание, что у Сары есть пустая строка для телефона, а у Арджуна и Рави — NULL. К этим двум относятся совершенно по-разному.

Тестирование на NULL

Вот ошибку, которую поначалу допускают почти все:

SELECT name FROM employees WHERE phone = NULL;
-- (no rows)

SELECT name FROM employees WHERE phone IS NULL;
-- Arjun
-- Ravi

SELECT name FROM employees WHERE phone IS NOT NULL;
-- Meera
-- Sara

Первый запрос является действительным SQL и не выдает ошибок. Он просто ничего не возвращает, потому что сравнение чего-либо с NULL никогда не является истинным. Сара появляется под IS NOT NULL, поскольку пустая строка является реальным значением. Однако в Oracle пустая строка рассматривается как NULL, что является заметным исключением.

Трехзначная логика

В большинстве языков программирования условие является истинным или ложным. SQL добавляет третий результат: неизвестно. Если вы не знаете значение, вы не можете сказать, равно ли оно 5. И неизвестно, что два неизвестных значения равны, поэтому NULL = NULL неверно. В нашем тесте все они вернули NULL:

SELECT NULL = NULL, NULL <> 1, 1 + NULL, 'a' || NULL;

-- NULL | NULL | NULL | NULL

Комбинирование условий следует строгим правилам. Вот результаты нашего прогона, где 1 — правда, а 0 — ложь:

Выражение Результат Почему
ИСТИНА И НУЛЬ НОЛЬ Зависит от неизвестной части
ЛОЖЬ И НУЛЬ Ложь Ложь, что бы ни было неизвестное
ИСТИНА ИЛИ НУЛЬ Правда Правда, каким бы неизвестным ни было
ЛОЖЬ ИЛИ НУЛЬ НОЛЬ Зависит от неизвестной части
НЕ НУЛЬ НОЛЬ Противоположность неизвестному неизвестно

Строки, которые исчезают из WHERE

WHERE сохраняет строку, только если ее условие true. Строки, результат которых неизвестен, удаляются вместе со строками, имеющими ложные значения. Это приводит к удивительным результатам:

SELECT name FROM employees WHERE phone <> '555-0101';

-- Sara

Вы могли бы ожидать появления Арджуна и Рави, поскольку их телефон определенно не с этим номером. Но их телефон неизвестен, поэтому результат неизвестен, и они исчезают. Чтобы включить их, скажите об этом явно: WHERE phone <> '555-0101' OR phone IS NULL. Отрицание тоже не помогает:

SELECT name FROM employees WHERE NOT (bonus > 400);

-- Arjun

У Сары и Рави нет бонусов, поэтому они не появляются ни в bonus > 400, ни в его противоположности. Это ключевой момент, который следует помнить: условие и его отрицание не всегда охватывают каждую строку.

COALESCE: обеспечить запасной вариант

COALESCE возвращает первое значение, отличное от NULL, в своем списке и работает во всех основных базах данных:

SELECT COALESCE(NULL, NULL, 'third', 'fourth');
-- third

SELECT name, COALESCE(phone, 'no phone') FROM employees;
-- Meera | 555-0101
-- Arjun | no phone
-- Sara  |
-- Ravi  | no phone

Сара по-прежнему отображается пустой, поскольку пустая строка не равна NULL. Подобные функции существуют, например IFNULL в MySQL и SQLite, ISNULL в SQL Server и NVL в Oracle. COALESCE — портативный вариант. Наше предыдущее замечание о расчетах применимо и здесь. bonus + 100 дает NULL для всех, у кого нет бонуса, поэтому напишите COALESCE(bonus, 0) + 100, если считать недостающий бонус нулевым для вашего бизнеса.

NULLIF: превратить значение в NULL

NULLIF(a, b) возвращает NULL, если два значения равны, в противном случае возвращает a. Два варианта использования особенно полезны. Он может очищать значения заполнителей:

SELECT name, NULLIF(phone, '') FROM employees WHERE id = 3;
-- Sara | NULL

SELECT name FROM employees
WHERE COALESCE(NULLIF(phone, ''), '') = '';
-- Arjun
-- Sara
-- Ravi

Этот второй запрос — удобный способ найти значения, которые «отсутствуют или пусты». Он также может защитить от деления на ноль, которое вызывает ошибку во многих базах данных:

SELECT total / NULLIF(quantity, 0) FROM sales;

Когда quantity равно 0, это дает NULL, а не ошибку. В нашем тесте SELECT 100 / NULLIF(0, 0) вернул NULL.

Агрегаты пропускают NULL

Именно здесь NULL наносит наибольший ущерб отчетам:

SELECT COUNT(*), COUNT(bonus), SUM(bonus), AVG(bonus),
       AVG(COALESCE(bonus, 0))
FROM employees;

-- 4 | 2 | 800 | 400.0 | 200.0
Выражение Результат Что это значит
COUNT(*) 4 Все строки
COUNT(bonus) 2 Только строки с бонусом
SUM(bonus) 800 NULL игнорируются
AVG(bonus) 400 800 разделить на 2, а не на 4
AVG(COALESCE(бонус, 0)) 200 800 разделить на 4

Так какой же средний бонус: 400 или 200? Это зависит от того, что означает NULL в ваших данных. Если NULL означает «бонус не был выплачен», то верно значение 200. Если это означает «еще не решено», то 400, возможно, и правы. База данных не может принять это решение за вас, поэтому вопрос должен быть адресован тому, кто разбирается в бизнесе.

Связанная ловушка: SUM без значений имеет значение NULL, а не 0. Когда мы суммировали только строки без бонуса, мы получили NULL, а COUNT вернул 0. Код приложения, ожидающий числа, может привести к сбою на этом, поэтому оберните его: COALESCE(SUM(bonus), 0).

Совет. Если итоговые данные в отчете не соответствуют действительности, сравните COUNT(*) с COUNT(column). Любой разрыв между ними сразу скажет вам, сколько NULL имеет этот столбец.

ГРУППИРОВАТЬ ПО, сортировать и РАЗЛИЧАТЬ

Хотя значения NULL «не равны» при сравнении, эти операции рассматривают их как одну группу:

SELECT bonus, COUNT(*) FROM employees GROUP BY bonus ORDER BY bonus;
-- NULL | 2
--  300 | 1
--  500 | 1

SELECT DISTINCT bonus FROM employees;
-- 500, 300, NULL

Дополнительную информацию о группировке см. в разделе SQL GROUP BY и HAVING. Порядок сортировки — это то, где базы данных действительно различаются. SQLite и MySQL рассматривают NULL как наименьшее значение, а PostgreSQL и Oracle — как наибольшее. Таким образом, тот же ORDER BY bonus DESC может помещать NULL последними в один и первым в другой. PostgreSQL, Oracle и современный SQLite позволяют вам заявить об этом напрямую, используя NULLS FIRST или NULLS LAST.

Присоединяется

Условие соединения с использованием = никогда не соответствует NULL, поэтому строки с ключом NULL исключаются из внутреннего соединения:

SELECT e.name FROM employees e
JOIN employees m ON m.id = e.manager_id;
-- Arjun, Sara, Ravi    (Meera is gone)

SELECT e.name, m.name FROM employees e
LEFT JOIN employees m ON m.id = e.manager_id;
-- Meera | NULL
-- Arjun | Meera
-- Sara  | Meera
-- Ravi  | Arjun

У Миры нет менеджера, поэтому внутреннее соединение автоматически удаляет эту строку. LEFT JOIN сохраняет его, а также создает собственные NULL для недостающей стороны. Подробнее читайте в объяснениях SQL-соединений.

Ловушка «НЕ В»

Один NULL в списке NOT IN уничтожает все результаты:

SELECT name FROM employees WHERE manager_id NOT IN (1);
-- Ravi

SELECT name FROM employees WHERE manager_id NOT IN (1, NULL);
-- (no rows)

Вторая форма означает manager_id <> 1 AND manager_id <> NULL, и эта последняя часть никогда не может быть правдой. Это особенно опасно для подзапросов, где NULL может прийти незамеченным. Вместо этого используйте NOT EXISTS, как описано в нашем руководстве по подзапросам SQL. Мира также отсутствует в первом результате, поскольку manager_id этой строки имеет значение NULL.

Проектирование с NULL

  • Используйте NOT NULL по умолчанию. Разрешите NULL только тогда, когда значение действительно может отсутствовать.
  • Добавьте разумные значения по умолчанию. Например, quantity INT NOT NULL DEFAULT 0.
  • Не используйте магические значения. Дата 1900-01-01 или возраст -1, заменяющий «неизвестно», испортят ваши расчеты.
  • Знайте поведение UNIQUE. Большинство баз данных допускают наличие нескольких NULL в столбце UNIQUE, и наш тест вставил два значения без ошибок. SQL Server является исключением, поскольку обычно это допускается.
  • Помните, что первичный ключ не может иметь значение NULL. См. первичный ключ и внешний ключ.
  • Тестирование с NULL. Тестовые данные без NULL скрывают эти ошибки. Включите их намеренно, как и другие крайние случаи.

Итоги

Относитесь к NULL как к «неизвестному», и большая часть его поведения обретает смысл: неизвестно плюс один неизвестно, и неизвестное равно неизвестному, неизвестно, что оно истинно. На этой неделе проведите аудит одного из своих отчетов. Сравните столбцы с нулевым значением, используя COUNT(*) и COUNT(column), найдите любой NOT IN и решите, что NULL должен означать в каждом среднем значении.

Часто задаваемые вопросы

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

NULL — это маркер отсутствующего или неизвестного значения. Оно отличается от нуля и пустой строки и требует специальной обработки при сравнении.

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

Сравнение чего-либо с NULL дает результат «неизвестно», а не «истина», поэтому WHERE отбрасывает строку. Используйте IS NULL или IS NOT NULL, чтобы проверить это.

В чем разница между COALESCE и ISNULL?

COALESCE — это стандартный SQL, принимающий множество аргументов. ISNULL специфичен для SQL Server и принимает два значения. Для переносимого кода отдайте предпочтение COALESCE.

Включает ли COUNT значения NULL?

COUNT(*) подсчитывает все строки, включая строки со значениями NULL. COUNT(столбец) подсчитывает только строки, в которых этот столбец не равен NULL, что может дать меньшее число.

NULL — это то же самое, что пустая строка?

Нет, в большинстве баз данных. Пустая строка — это известное значение без символов, а NULL означает неизвестное. Oracle является заметным исключением, поскольку он рассматривает пустые строки как NULL.

Продолжайте учиться

Работайте с Stack Quora

Нужна помощь с тестированием, WordPress или SEO?

Мы помогаем командам выпускать лучшее программное обеспечение и развивать свои сайты. Расскажите нам, над чем вы работаете, и мы предложим четкий следующий шаг.

Продолжайте читать

Больше руководств по тестирование, искусственный интеллект и современные разработки

Понравилась эта статья? Выберите тему и сразу приступайте к нашим новейшим руководствам.

Искусственный интеллект

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

Галлюцинации ИИ объяснили: что это такое, почему языковые модели их создают, распространенные типы, как уменьшить их с помощью заземления и подсказо…

Читать статью →
DevOps

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

Переменные среды объясняются на примерах Python, которые мы запускали: их чтение и установка, почему каждое значение является строкой, файлы .env, н…

Читать статью →
Разработка ПО

Объяснение ограничения скорости API: алгоритмы, заголовки и обработка ошибок 429

Ограничение скорости API объясняется с помощью проведенного нами моделирования: сегмент токенов, фиксированное окно, скользящее окно, 429 ответов, з…

Читать статью →
Автоматизация тестирования

Издевательство в Python: unittest.mock, патч и ошибки, из-за которых тесты лгут

Издевательство на Python с помощью unittest.mock, протестировано: Mock и return_value, Side_effect, исправление использования имени, спецификация и …

Читать статью →
Тестирование ПО

Объяснение покрытия кода: покрытие линий или ветвей и что на самом деле означает 100%

Покрытие кода объяснено с помощью реального запуска покрытия.py: покрытие строк, ветвей и условий, чтение отчета, почему 100%-ное покрытие все еще м…

Читать статью →
Базы данных и SQL

DELETE, TRUNCATE и DROP в SQL: различия, откат и когда использовать каждый из них

Объяснение DELETE, TRUNCATE и DROP на примере протестированного SQL: что каждый из них удаляет, откат, триггеры, сброс идентификаторов, скорость и ч…

Читать статью →
Базы данных и SQL

Объяснение представлений SQL: создавайте, используйте и избегайте ловушек, с примерами

Представления SQL, объясненные с помощью запросов, которые мы выполняли: создание представления, результаты в реальном времени, скрытие столбцов, пр…

Читать статью →
Разработка ПО

Интерфейс TypeScript и тип: реальные различия, протестированные с помощью компилятора

Интерфейс TypeScript против типа, проверенный реальным компилятором: что могут делать оба, что может делать только один, слияние объявлений, расшире…

Читать статью →
Разработка ПО

Объяснение Python *args и **kwargs: гибкие функции с понятными примерами

Python *args и **kwargs, объясненные с помощью кода, который мы запускали: упаковка и распаковка, порядок параметров, аргументы только по ключевым с…

Читать статью →
Разработка ПО

Лямбда-функции Python: синтаксис, реальное использование и когда их не следует использовать

Лямбда-функции Python, объясненные с помощью кода, который мы запускали: синтаксис, сортировка с помощью ключа, карты и фильтра, замыкания, ловушка …

Читать статью →
Искусственный интеллект

Что такое ИИ-агенты? Как они работают: можно запустить простой цикл агента

Что такое агенты ИИ? Простое руководство по циклу агента, инструментам, памяти и ограничениям, с небольшим циклом Python, который мы запустили, реал…

Читать статью →
DevOps

Мониторинг и наблюдаемость: реальная разница с журналами, метриками и трассировками

Объяснение мониторинга и наблюдаемости: известные и неизвестные сбои, журналы, метрики и трассировки, пошаговое руководство по инцидентам при оформл…

Читать статью →
DevOps

Docker Volumes против Bind Mounts: правильное сохранение данных контейнера

Объяснение томов Docker и монтирования привязки: почему исчезают данные контейнера, именованные тома, монтирование привязки, tmpfs, примеры создания…

Читать статью →
Автоматизация тестирования

TDD против BDD: ключевые различия, одна функция, созданная для обоих способов, и когда использовать каждый

TDD и BDD объясняются с помощью одной функции скидки, созданной в обоих направлениях: красно-зеленый рефакторинг с кодом, который мы запускали, сцен…

Читать статью →
Тестирование ПО

Пользовательское приемочное тестирование (UAT): процесс, контрольный список и реальные примеры

Приемочное тестирование пользователей объяснило: кто его проводит, чем оно отличается от контроля качества, семиэтапный процесс, шаблон тестового сц…

Читать статью →
Базы данных и SQL

Объяснение значений NULL в SQL: IS NULL, COALESCE, NULLIF и ловушки, которых следует избегать

Значения SQL NULL объяснены с помощью запросов, которые мы выполнили: трехзначная логика, IS NULL, COALESCE, NULLIF, как агрегаты, соединения и сорт…

Читать статью →
Базы данных и SQL

Оператор SQL CASE WHEN: синтаксис, реальные примеры и распространенные ошибки

SQL CASE WHEN объяснен с помощью запросов, которые мы выполняли: простые формы и формы для поиска, порядок условий, повороты с помощью SUM, трюки CO…

Читать статью →
Разработка ПО

Что такое JWT? Веб-токены JSON, объясненные на рабочем примере

Что такое JWT? Объяснение JSON Web Tokens: три части: токен, созданный и измененный в коде, который мы запускали, JWT и сеансы, хранилище, токены об…

Читать статью →
Разработка ПО

Объяснение цикла событий JavaScript: стек вызовов, микрозадачи и таймеры с примерами

Цикл событий JavaScript объясняется с помощью кода, который мы запускали: стек вызовов, очереди задач и микрозадач, почему setTimeout 0 не является …

Читать статью →
Разработка ПО

Генераторы Python и объяснение доходности: экономьте память с помощью реальных примеров

Генераторы Python и доходность, объясненные с помощью кода, который мы запускали: как они приостанавливаются и возобновляются, измеряют экономию пам…

Читать статью →
Искусственный интеллект

Что такое вложения? Простое объяснение векторов и косинусного сходства

Что такое вложения? Простое руководство по векторам, косинусному подобию и семантическому поиску с небольшим примером, который мы использовали, общи…

Читать статью →
DevOps

Сине-зеленое и канареечное развертывание: различия, компромиссы и как выбрать

Объяснение сине-зеленого и канареечного развертывания: как каждый из них работает, стоимость, риски и откат, стабильное группирование пользователей,…

Читать статью →
Автоматизация тестирования

Ненадежные тесты: что их вызывает и как их исправить навсегда

Ненадежные тесты объясняются на демонстрациях, которые мы запускали: основные причины, способы подтверждения и диагностики, правильные исправления в…

Читать статью →
Тестирование ПО

Нагрузочное тестирование и стресс-тестирование: различия, метрики и примеры k6

Объяснение нагрузочного и стресс-тестирования: что доказывает каждое из них, тесты на всплески и выдержки, важные метрики, почему средние значения в…

Читать статью →
Тестирование ПО

Тестовый пример и тестовый сценарий: ключевые различия, примеры и когда использовать каждый

Объяснение тестового случая и тестового сценария: четкие определения, параллельная таблица, одна функция сброса пароля, взятая из сценариев в случаи…

Читать статью →
Базы данных и SQL

Оптимизация SQL-запросов: чтение планов выполнения и исправление медленных запросов с указанием времени

Оптимизация SQL-запросов проверена на миллионе строк: читайте планы выполнения, добавляйте правильные индексы, исправляйте неразмещаемые фильтры, ис…

Читать статью →
Базы данных и SQL

SQL UNION против UNION ALL: различия, примеры и проверка производительности

SQL UNION и UNION ALL объяснены с помощью запросов, которые мы выполнили: дубликаты, правила, которым следуют оба, реальная ошибка данных, измеренна…

Читать статью →
Разработка ПО

Учебное пособие по API JavaScript Fetch: GET, POST, обработка ошибок и тайм-ауты

Учебное пособие по API JavaScript Fetch, протестированное на локальном сервере: GET и POST, почему не выдается ошибка 404, многоразовый помощник, ст…

Читать статью →
Разработка ПО

Обработка файлов Python: чтение, запись, CSV, JSON и pathlib с примерами

Обработка файлов Python объясняется с помощью кода, который мы запускали: открытые режимы, чтение больших файлов, CSV, JSON, pathlib, двоичные файлы…

Читать статью →
Разработка ПО

Классы и объекты Python: учебник ООП с реальными примерами

Классы и объекты Python, объясненные с помощью кода, который мы запускали: __init__, методы, атрибуты класса и экземпляра, наследование, свойства, м…

Читать статью →
Искусственный интеллект

Что такое большая языковая модель? Как работают LLM, объяснено без шумихи

Что такое большая языковая модель? LLM объясняется простым языком: токены, встраивания, внимание, обучение, температурные и контекстные окна, крошеч…

Читать статью →
DevOps

Команды Linux для начинающих: основное руководство по терминалу с примерами

Команды Linux для начинающих, сгруппированные по задачам: навигация, файлы, просмотр журналов, grep, каналы, разрешения, процессы и архивы, с практи…

Читать статью →
Автоматизация тестирования

Selenium vs Playwright: ключевые различия, сравнение кода и что выбрать

Сравнение Selenium и Playwright: архитектура, ожидание, поддержка браузера и языка, отладка, одинаковый тест в обоих инструментах, советы по миграци…

Читать статью →
Тестирование ПО

Разделение эквивалентности: метод проектирования тестов, объясненный на рабочих примерах

Объяснение эквивалентного разделения: как найти действительные и недействительные разделы, рабочие примеры для возрастов, паролей и купонов, проведе…

Читать статью →
Тестирование ПО

Серьезность и приоритет в тестировании программного обеспечения: различия, матрица и реальные примеры

Объяснение серьезности и приоритета при тестировании программного обеспечения: четкие определения, четырехсторонняя матрица с примерами, шкалы уровн…

Читать статью →
Базы данных и SQL

Учебное пособие по подзапросам SQL: типы, коррелирующие подзапросы и EXISTS с примерами

Руководство по подзапросам SQL с выполненными нами запросами: скалярные подзапросы, IN, FROM и SELECT, коррелированные подзапросы, EXISTS, ловушка N…

Читать статью →
Базы данных и SQL

Объяснение транзакций SQL и ACID: COMMIT, ROLLBACK и уровни изоляции

Транзакции SQL и ACID, объясненные с помощью банковского перевода, который мы выполнили: COMMIT, ROLLBACK, точки сохранения, блокировки, уровни изол…

Читать статью →
Разработка ПО

Git merge vs rebase: отличия, примеры и когда что использовать

Git merge vs rebase с реальным выводом log, сравнительной таблицей, золотым правилом rebase и безопасным рабочим процессом для команды.

Читать статью →
Разработка ПО

Генераторы списков в Python: синтаксис, примеры и когда их избегать

Генераторы списков в Python: синтаксис, фильтры, if-else, вложенные циклы, измеренное время и случаи, когда обычный цикл for лучше.

Читать статью →
Базы данных и SQL

SQL JOIN: объяснение с примерами (Inner, Left, Right, Full)

Разберите SQL JOIN шаг за шагом на примерах таблиц и запросов: INNER, LEFT, RIGHT, FULL, CROSS и SELF, а также частые ошибки и советы по производите…

Читать статью →