NULL-безопасное сравнение в SQL, или как избежать ловушек

Рассказываем, как избежать неприятностей при сравнении NULL значений в SQL-запросах.

224 открытий2К показов
NULL-безопасное сравнение в SQL, или как избежать ловушек

SQL, будучи универсальным языком для работы с данными, имеет свою специфику. Одной из таких особенностей является значение NULL, которое обозначает отсутствие данных в ячейке таблицы. Понимание того, как NULL ведет себя при сравнении, критично для написания корректных и эффективных SQL-запросов.

Особенности сравнения NULL

Ключевая особенность NULL заключается в том, что любое сравнение с ним (равно, больше, меньше и т.д.) всегда дает результат NULL. Это связано с тем, что неизвестное значение нельзя сравнить с чем-либо определенным. Например:

			SELECT 2 = NULL;     -- NULL
SELECT NULL > 5;     -- NULL
SELECT 'a' > NULL;    -- NULL
SELECT NULL = NULL; -- NULL
		

Такое поведение может привести к неожиданным результатам, если его не учитывать. Убедитесь в этом на сайте SQLize.online

Почему так происходит?

Представьте, что вы спрашиваете: «Неизвестное число больше пяти?». Ответ очевиден: мы не можем этого знать. Точно так же в SQL: если мы сравниваем какое-либо значение с неизвестным (NULL), то результат будет неизвестным. Так же невозможно сравнивать NULL-значения между собой. Согласитесь, вопрос о равенстве или неравенстве не известных величин не имеет смыла, и ответом на такой вопрос будет «неизвестно», или NULL (в терминах языка SQL).

В чём проблема?

Представьте задачу: в базе данных предприятия имеется таблица EMPLOYEE, одна из колонок которой — DEPARTMENT — отображает отдел, в котором трудится сотрудник. Если нужно найти всех сотрудников отдела продаж (Sales), то проблем не возникает.

			SELECT * FROM EMPLOYEES WHERE DEPARTMENT = 'Sales';
		

Что, если нужно выполнить обратную задачу — найти всех сотрудников, кроме отдела продаж. Пустяки, скажет кто-то из вас и напишет запрос:

			SELECT * FROM EMPLOYEES WHERE DEPARTMENT <> 'Sales';
		

и будет уволен, так как в список на премию не попал генеральный директор! У него в колонке DEPARTMENT стоит значение NULL, так как он не относится ни к одному из подразделений.

Как с этим жить? Традиционные подходы к работе с NULL

До появления специализированных операторов для сравнения с NULL, разработчики использовали комбинации операторов IS NULL и IS NOT NULL для проверки значений на пустоту, а также функции вроде COALESCE для обработки таких ситуаций. Например, чтобы найти всех сотрудников, не работающих в отделе кадров:

			SELECT * FROM EMPLOYEES WHERE DEPARTMENT <> 'HR' OR department IS NULL;

SELECT * FROM EMPLOYEES WHERE COALESCE(DEPARTMENT, '') <> 'HR';
		

Согласитесь, не самый понятный код.

NULL-безопасное сравнение

Для упрощения работы с NULL в разных базах данных были введены специальные операторы. Например в PostgreSQL — IS DISTINCT FROM/IS NOT DISTINCT FROM. Он позволяет сравнивать значения, включая NULL, и возвращает TRUE, если значения различны. Таким образом решение предыдущей задачи сокращается до следующего:

			SELECT * FROM EMPLOYEES WHERE DEPARTMENT IS DISTINCT FROM 'HR';
		

Этот запрос вернет всех сотрудников, у которых отдел отличается от HR, включая тех, у кого отдел не указан (NULL).

Разработчики MySQL и MariaDB пошли дальше и вместо текста ввели короткий оператор <=>, являющийся полным аналогом рператора IS DISTINCT FROM. Таким образом, в MySQL решение будет ещё лаконичней:

			SELECT * FROM EMPLOYEES WHERE DEPARTMENT <=> 'HR';
		

В чём преимущества NULL-безопасного сравнения?

  • Повышенная читаемость — запросы становятся более понятными.
  • Меньше ошибок — снижается риск получения некорректных результатов.
  • Универсальность — работает с любыми типами данных.

Рекомендации:

  • Используйте NULL-безопасное сравнение по умолчанию.
  • Будьте внимательны при использовании стандартных операторов сравнения (=, != и др.) с NULL.
  • Изучите особенности работы с NULL в вашей СУБД.

Хотите углубить свои знания?

Рекомендуем платформу SQLtest.online для практических занятий и отработки навыков написания SQL-запросов.

Следите за новыми постами
Следите за новыми постами по любимым темам
224 открытий2К показов