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 + 5→NULLNULL * 0→NULLNULL / 2→NULL
Сравнения
NULL не равен ничему, включая сам себя. Сравнение с использованием операторов =, <>, <, > всегда возвращает UNKNOWN (не TRUE и не FALSE):
NULL = NULL→UNKNOWNNULL <> NULL→UNKNOWNNULL > 5→UNKNOWN
Для проверки на NULL используются специальные операторы:
IS NULL— возвращаетTRUE, если значение равно NULL.IS NOT NULL— возвращаетTRUE, если значение не равно NULL.ISNULL()(в некоторых СУБД, например, в SQL Server) — функция, заменяющая NULL на указанное значение.COALESCE()— стандартная функция SQL, возвращающая первое не-NULL значение из списка.
Логические операции
В трёхзначной логике SQL:
TRUE AND UNKNOWN→UNKNOWNFALSE AND UNKNOWN→FALSETRUE OR UNKNOWN→TRUEFALSE OR UNKNOWN→UNKNOWNNOT UNKNOWN→UNKNOWN
Агрегатные функции
Большинство агрегатных функций (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 и RustOption<T>в Scala и RustNullable<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 →