Открыть сервис

NULL-значения

NULL-значение (от лат. nullus — никакой, пустой) — в информатике и базах данных специальное маркерное значение, указывающее на отсутствие, неизвестность или неприменимость какого-либо данного. NULL не является ни числом, ни строкой, ни логическим значением; это отдельный тип или состояние, которое обозначает, что значение не было присвоено или не существует.

Определение и сущность

NULL-значение представляет собой концептуальную «дырку» в данных. Оно принципиально отличается от пустой строки (""), нуля (0) или пробела. Пустая строка — это строка нулевой длины, но она всё же является значением строкового типа. Нуль — это определённое числовое значение. NULL же означает полное отсутствие какого-либо значения. В реляционных базах данных NULL трактуется как «неизвестное» или «отсутствующее» значение.

Введение NULL-значений было предложено Эдгаром Коддом, создателем реляционной модели данных, для корректной обработки неполной информации. В своей работе «A Relational Model of Data for Large Shared Data Banks» (1970) он заложил основы трёхзначной логики, где помимо истины (TRUE) и лжи (FALSE) существует третье состояние — неизвестность (UNKNOWN), которое возникает при сравнении с NULL.

История

Концепция NULL впервые была реализована в ранних системах управления базами данных (СУБД) в 1970-х годах. В языке SQL, разработанном компанией IBM в 1974 году, NULL стал встроенным типом данных. Стандарт SQL-86 (ANSI X3.135-1986) официально закрепил NULL как допустимое значение в любом столбце таблицы, если не указано ограничение NOT NULL.

В языках программирования NULL появился позже. В языке C (1972) для указания на отсутствие адреса используется макрос NULL (обычно определяется как (void*)0). В объектно-ориентированных языках, таких как Java (1995) и C# (2000), null — это ссылка, не указывающая ни на один объект. В 1965 году в языке ALGOL W была введена концепция «null-ссылки», которую позже Тони Хоар назвал своей «ошибкой на миллиард долларов».

Поведение в SQL

В SQL NULL ведёт себя особым образом, что часто вызывает путаницу у начинающих разработчиков.

Арифметические операции

Любая арифметическая операция с NULL даёт NULL:

  • NULL + 5NULL
  • NULL * 0NULL
  • NULL / 2NULL

Сравнения

NULL не равен ничему, включая сам себя. Сравнение с использованием операторов =, <>, <, > всегда возвращает UNKNOWN (не TRUE и не FALSE):

  • NULL = NULLUNKNOWN
  • NULL <> NULLUNKNOWN
  • NULL > 5UNKNOWN

Для проверки на NULL используются специальные операторы:

  • IS NULL — возвращает TRUE, если значение равно NULL.
  • IS NOT NULL — возвращает TRUE, если значение не равно NULL.
  • ISNULL() (в некоторых СУБД, например, в SQL Server) — функция, заменяющая NULL на указанное значение.
  • COALESCE() — стандартная функция SQL, возвращающая первое не-NULL значение из списка.

Логические операции

В трёхзначной логике SQL:

  • TRUE AND UNKNOWNUNKNOWN
  • FALSE AND UNKNOWNFALSE
  • TRUE OR UNKNOWNTRUE
  • FALSE OR UNKNOWNUNKNOWN
  • NOT UNKNOWNUNKNOWN

Агрегатные функции

Большинство агрегатных функций (SUM, AVG, COUNT, MAX, MIN) игнорируют NULL-значения. Исключение — COUNT(*), который считает все строки, включая те, где все столбцы равны NULL. COUNT(column_name) считает только не-NULL значения в указанном столбце.

Ограничения

В SQL NULL может быть запрещён для конкретного столбца с помощью ограничения NOT NULL при создании или изменении таблицы. Первичный ключ (PRIMARY KEY) не может содержать NULL-значения. Уникальное ограничение (UNIQUE) в большинстве СУБД допускает несколько NULL-значений (поскольку NULL не равен NULL), хотя стандарт SQL этого не требует.

NULL в языках программирования

В разных языках программирования NULL реализован по-разному.

C и C++

В C NULL — это макрос, обычно определяемый как (void*)0 или просто 0. В C++11 появилось ключевое слово nullptr, которое является безопасным литералом нулевого указателя, неявно преобразуемым в любой тип указателя.

Java

В Java null — это литерал, который может быть присвоен любой ссылочной переменной. Попытка вызова метода или доступа к полю через null приводит к исключению NullPointerException (NPE). В Java 8 был введён класс Optional<T> для безопасной работы с потенциально отсутствующими значениями.

C#

В C# null — это значение по умолчанию для ссылочных типов. Начиная с C# 8.0, введены nullable-ссылочные типы, позволяющие компилятору предупреждать о потенциальных разыменованиях null. Для значимых типов (value types) используется Nullable<T> (или T?).

Python

В Python отсутствует ключевое слово null. Вместо него используется None — единственный экземпляр класса NoneType. None часто используется как значение по умолчанию для аргументов функций.

JavaScript

В JavaScript существуют два значения, обозначающих отсутствие: null и undefined. null — это явное указание на отсутствие значения, а undefined — значение, которое присваивается переменной, если ей не было присвоено никакого значения.

Проблемы и критика

Концепция NULL-значений подвергается критике по нескольким причинам.

Ошибка на миллиард долларов

Тони Хоар, создатель null-ссылок в языке ALGOL W, в 2009 году назвал это изобретение своей «ошибкой на миллиард долларов», имея в виду огромное количество ошибок, сбоев и уязвимостей, вызванных разыменованием нулевых указателей.

Неоднозначность трактовки

NULL может означать разные вещи:

  • Значение неизвестно (например, дата рождения неизвестна).
  • Значение не применимо (например, дата смерти для живого человека).
  • Значение ещё не присвоено (например, поле в форме, которое пользователь не заполнил).
  • Значение отсутствует (например, удалённые данные).

В SQL невозможно различить эти случаи без дополнительных соглашений.

Сложность трёхзначной логики

Трёхзначная логика (TRUE, FALSE, UNKNOWN) усложняет написание корректных запросов. Например, условие WHERE column = NULL никогда не вернёт строки, что является частой ошибкой начинающих. Также сложности возникают при использовании NOT IN с подзапросами, возвращающими NULL.

Производительность

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

Альтернативы и решения

Для снижения проблем, связанных с NULL, применяются различные подходы.

Ограничение NOT NULL

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

Типы-обёртки (Optional/Maybe)

В современных языках программирования используются типы, явно указывающие на возможность отсутствия значения:

  • Optional<T> в Java 8+
  • Maybe<T> в Haskell и Rust
  • Option<T> в Scala и Rust
  • Nullable<T> в C#

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

Специальные значения

В некоторых системах вместо NULL используются специальные «магические» значения, например, -1 для возраста или 9999-12-31 для даты. Однако этот подход чреват ошибками, если такое значение встретится в реальных данных.

Денормализация

В реляционных базах данных NULL можно избежать путём разделения таблицы на несколько. Например, вместо столбца date_of_death с NULL для живых людей можно создать отдельную таблицу deaths, содержащую только записи об умерших.

Интересные факты

  • В стандарте SQL NULL не является значением в обычном понимании, а скорее состоянием. Поэтому в некоторых СУБД (например, в PostgreSQL) NULL хранится отдельно от данных в специальной битовой маске.
  • В языке COBOL, который старше SQL, для обозначения отсутствия данных используется ключевое слово NULL в сочетании с указателями.
  • В некоторых базах данных, например, в Oracle, пустая строка ('') приравнивается к NULL, что может приводить к неожиданным результатам.
  • В языке Rust нет NULL в явном виде. Вместо него используется перечисление Option<T>, которое может быть Some(T) или None. Это позволяет компилятору проверять обработку всех возможных случаев на этапе компиляции.

Источники

  • Codd, E. F. «A Relational Model of Data for Large Shared Data Banks». Communications of the ACM, 1970.
  • ISO/IEC 9075:2016 — стандарт языка SQL.
  • Hoare, C. A. R. «Null References: The Billion Dollar Mistake». Presentation at QCon London, 2009.
  • Date, C. J. «An Introduction to Database Systems». 8th Edition, Addison-Wesley, 2004.
  • Celko, J. «Joe Celko's SQL for Smarties: Advanced SQL Programming». 5th Edition, Morgan Kaufmann, 2014.

BFOmetr — база данных и аналитика по компаниям России.

На главную BFOmetr →