что означает значение null
NULL (SQL)
NULL в Системах управления базами данных (СУБД) — специальное значение (псевдозначение), которое может быть записано в поле таблицы базы данных (БД). NULL соответствует понятию «пустое поле», то есть «поле, не содержащее никакого значения». Введено для того, чтобы различать в полях БД пустые (визуально не отображаемые) значения (например, строку нулевой длины) и отсутствующие значения (когда в поле не записано вообще никакого значения, даже пустого).
NULL означает отсутствие, неизвестность информации. Значение NULL не является значением в полном смысле слова: по определению оно означает отсутствие значения и не принадлежит ни одному типу данных. Поэтому NULL не равно ни логическому значению FALSE, ни пустой строке, ни нулю. При сравнении NULL с любым значением будет получен результат NULL, а не FALSE и не 0. Более того, NULL не равно NULL!
Содержание
Необходимость NULL в реляционных БД
Использование NULL в БД
В БД, поддерживающих понятие NULL, для поля таблицы при описании определяется, может ли оно быть пустым. Если да, то в это поле можно не записывать никакого значения, и это поле будет иметь значение NULL. Также можно и явно записать в такое поле значение NULL.
Как правило, СУБД не разрешает значение NULL для полей, являющихся частью первичного ключа таблицы. В полях внешних ключей, напротив, NULL допускается. Наличие NULL в поле внешнего ключа может трактоваться как признак отсутствия связанной записи, и для такого внешнего ключа не требуется исполнение правил ссылочной целостности, обязательных для любого другого значения внешнего ключа.
Операции с NULL
Поскольку NULL не является, в общем смысле, значением, использование его в арифметических, строковых, логических и других операциях, строго говоря, некорректно. Тем не менее, большинство СУБД поддерживают такие операции, но вводят для них специальные правила:
Кроме того, могут существовать специальные системные функции для удобного преобразования NULL к определённым значениям, например, в Oracle имеется системная функция NVL, которая возвращает значение своего параметра, если он не NULL, или значение по умолчанию, если операнд — NULL. В стандарте SQL-92 определены две функции: NULLIF и COALESCE, поэтому их использование является более предпочтительным (если конкретная СУБД их реализует).
Оригинал статьи этой статьи впервые был опубликован в блоге автора @KevinMarquette. Группа разработчиков PowerShell благодарит Кевина за то, что он поделился с нами этими материалами. Читайте его блог — PowerShellExplained.com.
Что означает NULL
Значение NULL можно считать неизвестным или пустым значением. Переменная имеет значение NULL, пока ей не присвоено значение или объект. Это важный момент, так как некоторые команды требуют значения и возвращают ошибку, если значением является NULL.
$null в PowerShell
$null — это автоматическая переменная в PowerShell, используемая для представления значения NULL. Ее можно назначать переменным и использовать в сравнениях, а также в качестве заполнителя для значения NULL в коллекции.
В строках
Это одна из причин, по которой я предпочитаю заключать переменные в скобки при их использовании в сообщениях журнала. Если значение находится в конце строки, это также помогает обнаружить границы значений переменных.
В числовом уравнении
Вместо коллекции
Вместо объекта
Метод в выражении со значением NULL
Взгляните на следующий пример и постарайтесь предсказать результаты:
PSScriptAnalyzer и VSCode
Поскольку VS Code также использует правила PSScriptAnalyser, в редакторе такие фрагменты кода выделяются и считаются проблемой.
Простая проверка if
Но это еще не все. На самом деле строка значит следующее:
Ниже приведен более полный пример этой инструкции.
Несколько дней назад я столкнулся с этой проблемой при рефакторинге кода. В нем присутствовала базовая проверка такого типа.
$null.Count
[PSCustomObject] Count
Выполнение этого примера в Windows PowerShell 5.1 и PowerShell 6.0 дает разные результаты.
Пустое значение NULL
Pipeline
foreach
Оператор foreach начал так работать с версии PowerShell 3.0. Если у вас предыдущая версия, такой режим работы в ней не поддерживается. Это одно из важных отличий, которое необходимо учитывать при обратном портировании кода для совместимости с версией 2.0.
Типы значений
Параметры функции
Общепринято использовать строго типизированные значения в параметрах функций. Обычно мы стараемся определять типы параметров, даже если не собираемся определять типы других переменных в скриптах. Возможно, вы сами строго типизировали переменные в функциях, даже не осознавая этого.
IsNotNullOrEmpty
Я часто так делаю, когда знаю, что тип значения должен быть строкой.
Сценарий без результатов
У меня выработалась привычка инициализировать все переменные перед их использованием. В других языках это обязательно. В начале функции или цикла foreach я определяю все используемые значения.
Вот сценарий, который я советую вам внимательно изучить. Это пример ошибки, которую я однажды выявлял.
Проблемы с областью действия
Если взять тот же пример Do-something и удалить из него цикл, то код будет таким:
Out-Null
Сводка
Учимся избегать null-значений в современном Java. Часть 1
Oct 12, 2020 · 11 min read
Null. Правила использования
В своем выступлении “Null References: The billion dollar mistake” (“Нулевые ссылки: ошибка на миллиа р д долларов”), Тони Хоар описывает реализацию нулевых ссылок в языках программирования ALGOL, что также по его словам стало ошибкой стоимостью в миллиард долларов. Такие авторитетные книги, как Clean Code: A Handbook of Agile Software Craftsmanship (“Чистый код: настольное руководство по гибкой разработке ПО”) рекомендуют использовать нуль как можно реже. В то же время в книге Bug Patterns in Java (“Шаблоны ошибок в Java”) проблемам, связанным с нулевыми значениями, посвящается аж целых три главы. Тема “What is a null pointer exception and how do I fix it” (“Что такое исключение нулевого значения и как его исправить”), обсуждаемая на Stack Overflow, набрала уже более 3 млн просмотров. Работа с нулевыми значениями и впрямь может вызвать немало сложностей.
Я не отношусь к тем, кто можем говорить лауреатам премии Тьюринга вроде Хоар, как проектировать языки программирования, но при этом все же не считаю null заведомо плохим. В этой и последующих статьях мы рассмотрим, что это вообще такое, когда стоит или не стоит эти значения использовать, а также, как их изящно обрабатывать.
В конкретно данной статье я поделюсь своими размышлениями об их использовании, а в следующей мы перейдем к рассмотрению практического применения некоторых методов работы с null, включая последние возможности Java 8.
Несмотря на то, что в центре нашего внимания будет именно Java, основные принципы и обсуждение должны охватывать объектно-ориентированные языки в целом. Текущая статья в первую очередь предназначена для менее опытных программистов и всех тех, кто испытывает сложности, сталкиваясь с null. Но, думаю, что даже бывалые разработчики смогут найти здесь для себя полезные приемы.
Чем опасен null?
Null — это особое значение, поскольку оно не ассоциируется ни с каким типом (можете смело проверить это инструкцией instanceof в отношении любого другого класса в JRE) и с радостью занимает место любого другого объекта в присвоениях переменных и вызовах методов. Именно в этом и кроются две основных его опасности:
В результате любое возвращаемое значение или объект параметра — это потенциальное исключение нулевого указателя (NPE), возникающее в случае неправильной обработки.
Будет ли в таком случае решением проверять каждое возвращаемое значение и параметр на null? Очевидно, что идея не очень. Во-первых, код будет загроможден проверками на null. Во-вторых, разработчики будут вынуждены тратить драгоценное время на поиск правильного способа обработки нулевых значений, которые никогда не возникнут, а проверки на null будут сбивать с толку других разработчиков.
Может тогда вообще никогда не присваивать значениям null? Тоже неудачное предположение. Если учесть тот факт, что в каждом языке программирования есть пустое значение ( nil, undefined, None, void и т.д.), то наличие общего значения, обозначающего отсутствие чего-либо, чрезвычайно полезно.
Ошибка в условии цикла while может породить бесконечный цикл в любой программе, но это не делает такие циклы плохими по природе. Аналогично будет неверным считать, что null всегда неуместен только из-за того, что его неправильное использование может привести к ошибкам. Null — это наиболее естественное значение для выражения конкретных вещей, но при этом очень неподходящее для выражения других. Компетентные разработчики должны уметь различать эти случаи. В следующем разделе я как раз перейду к объяснению этого.
Когда Null уместен, а когда нет
В этой части я рассмотрю сценарии, в которых null возвращается из методов и передается им, поясню несколько традиционных альтернатив (т.е. предшествующих Java 8) и приведу доводы в пользу уместности встроенного типа вроде null в некоторых случаях.
Возвращение null
Одной из основных идей ООП является моделирование принципов области бизнеса, в которой работает наше ПО. Для этого мы определяем классы, соответствующие данным принципам и их атрибутам. Если речь заходит об использовании нулевых значений, я считаю важным рассмотреть эти типы классов отдельно от остальных.
Сейчас я работаю над проектом электронной записи пациентов Columna, в котором у нас есть классы, представляющие элементы рабочего процесса больницы вроде пациентов, медикаментов, врачей, больничных отделений, госпитализаций и пр. При моделировании любой области возникают случаи, когда нам нужно допустить для определенного элемента отсутствие значения. Предположим, что у нас есть класс, представляющий госпитализацию с атрибутами, которые ее описывают: больничное отделение, куда помещается пациент, причина госпитализации, ее время и т.д. Аналогичным образом у нас может быть класс, который представляет пациента с набором атрибутов вроде имени и номера социального страхования. В любой момент времени пациент может быть госпитализирован или нет. Говоря более формально, у нас есть связь типа “имеет” с мощностью 0..1.
Представим метод, извлекающий из базы данных информацию о госпитализации данного пациента:
Что должен возвращать этот метод для не госпитализированного пациента, если не null? Есть ли для выражения этого более точное значение? Спорю, что нет.
Существует и много других сценариев, в которых объект области напрямую ассоциирован с необязательными значениями в качестве атрибутов. Объект, представляющий медикаменты, содержит такие значения, как название препарата, его форму, активность, действующее вещество и т.д. Однако разнообразие медикаментов чрезвычайно обширно, начиная от антибактериальных кремов и заканчивая чаями из каннабиса (оставим или на ромашку заменим?:)), следовательно не все атрибуты будут актуальны для каждого. И снова возвращение null для отсутствующего атрибута выглядит очевидным способом сообщить, что допускается отсутствие значения. Есть ли для этого лучшая альтернатива?
Некоторые выступают за использование так называемого шаблона проектирования Null Object вместо null. Главная его идея в реализации пустого класса с минимумом или вообще без функциональности, т.е. нулевого объекта, который можно использовать вместо класса, содержащего действительную функциональность. Лучше всего данный шаблон показывает себя в сценариях, где нужно рекурсивно обойти структуру бинарного дерева в поиске суммы значений всех узлов. Для этого вы выполняете поиск в глубину и рекурсивно суммируете в каждом узле значения его левого и правого поддерева.
Дерево поиска обычно реализуется так, что каждый узел в нем имеет левого и правого потомка, которые являются либо также узлами, либо концевыми вершинами. Если такое представление дерева использует для концевых узлов null, то вам придется явно выполнять проверки на нулевых потомков, чтобы останавливать рекурсию в концевом узле, предотвращая попытку получения его значения. Вместо этого вам следует определить интерфейс Node с простым методом getValue() и реализовать его в представляющем узел классе, который вычисляет значение, складывая значения getValues() потомков, как показано на рисунке ниже. Реализуйте такой же интерфейс в классе, представляющем узел, и пусть класс концевого узла возвращает при вызове 0. Теперь нам больше не нужно различать код между концевым узлом и обычным. Необходимость явно проверять наличие null отпала вместе с риском получения исключения (NPE).
Тем не менее лично мне сложно найти применение данному шаблону в типичных базах кода производственной среды, где логика зачастую превосходит сложностью простое накопление чисел. Шаблоны существуют для упрощения решений, но при несоответствующем использовании добавляют сложность, одновременно лишаясь всех преимуществ.
Во многих случаях вам придется писать код, который в определенный момент должен будет проверять что-то для обработки ложных значений. Так почему бы просто не использовать проверки на null изначально, избегая определения дополнительных усложняющих код классов? Бывают случаи, когда рассмотренный шаблон работает прекрасно, но на мой взгляд таких случаев в реальных условиях мало, поскольку его можно использовать только для объектов с методами, содержащими пустые значения, или когда вы можете вернуть что-то гармонично вписывающееся в поток окружающего кода.
Думаю, что нет. В случае класса, представляющего явления из реальной жизни, мы можем сделать обоснованное предположение. Но вышеприведенные типы классов не дают нам для этого никакой возможности, и разобраться в таких случаях можно только, читая код. По этой причине лучше избегать возвращения null вместо других типов, так как у разработчиков редко возникают причины ожидать возвращения null. Если вы этого не ждете, то зачем затрачивать усилия на защиту кода от этих значений?
Передача нулевых параметров
Из предыдущего раздела следует, что допустимо использовать нулевые аргументы при вызове методов с моделирующими область типами параметров, имеющими необязательные значения. При этом методы должны обеспечивать безопасное их использование. На практике же нулевые параметры применяются для гораздо большего спектра задач. Когда нам нужно предоставить такой параметр методу, мы должны обеспечить, чтобы все последующие обработки этого параметра были защищены от null, а это может оказаться нелегко. И даже несмотря на это, ваша программа может находиться в состоянии, скрывающем небезопасное поведение, что приведет к раскрытию проблемы только при других условиях. Предоставление нулевых параметров также добавляет риск вызвать ошибки при изменении кода в его следующих за их добавлением частях.
Как же полностью избежать нулевых параметров?
Нередко нам нужно использовать какую-либо функциональность в существующем методе, но текущий контекст вызова несколько иной, и мы либо не можем обеспечить все вызываемые методом значения, либо требуется больше информации, чем допускает его структура. Само собой мы не хотим повторять практически идентичный метод. Переиспользование является одним из столпов легко обслуживаемого кода, и одинаковая функциональность не должна реализовываться в нескольких местах, поскольку это не только усложнит поддержание синхронности кода, но и внесет риск появления ошибок. Поэтому мы изменяем существующий код под наши задачи и используем null для параметров, которые предоставляются не всегда. Некоторые методы по своей структуре могут принимать по меньшей мере несколько нулевых параметров, другие же не могут совсем. Тем не менее может оказаться затруднительным определить, какие параметры могут иметь значение null, и подходит ли оно для представления отсутствующего значения.
В таких языках, как Python, сигнатуры методов могут содержать предустановленные значения параметров, используемые при отсутствии значения аргумента в вызове метода. Тем не менее в Java такое невозможно. Ближайшим аналогом этого будет использовать перегрузку метода, когда в классе одна и та же сигнатура метода определяется несколько раз с разными параметрами. Один метод будет содержать всю функциональность и принимать весь набор параметров, а другие будут просто “декораторами” для вызова этого метода, каждый из которых будет получать свой поднабор параметров. Методы-декораторы определяют, какие значения должны использовать вместо отсутствующих параметров, чтобы вызывающему компоненту не пришлось их предоставлять. Жестко прописывая, какие значения должны предоставляться, когда у вызывающего их не хватает, мы уменьшаем риск появления ошибок и делаем принимаемые значения параметров явными.
В рассмотренном выше решении нулевые значения по-прежнему передаются в методы внутри объекта, но вызывающий и вызываемые методы спроектированы с учетом этого. Любой метод, вызываемый с одним из этих значений, по умолчанию должен корректно обрабатывать null. При этом нужно запретить сторонним вызывающим объектам передавать нулевые значения для параметров, которые не учтены в структуре, поскольку эти значения могут не поддерживаться, и правильная их обработка не гарантируется.
Воспринимайте null правильно
Все больше языков программирования начинают реализовывать определенные возможности с учетом безопасности. Например, в таких языках, как Clojure, F# и Rust переменные по умолчанию неизменяемы. Компилятор допускает изменение значений только для тех из них, которые объявлены со специальным модификатором. Такой способ использования опасных функций вынуждает программистов переопределять поведение по умолчанию, указывая тем самым, что они осознают степень риска и делают это не без весомых оснований. И к null нам стоит относиться аналогичным образом. Нужно придерживать это значение для особых случаев, где оно будет вполне уместно, ограничив при этом его использование в целом, опять же не ценой усложнения кода креативными обходными решениями. При каждом намерении использовать null вместо перемещающегося между методами значения следует учесть оправданность этого. В таком случае вы должны гарантировать, что в итоге оно не окажется в том месте, где может вызвать проблемы, и другие разработчики будут знать, что значение может быть null. Если же этого обеспечить нельзя, то лучше рассмотреть другие варианты.
Значение NULL: руководство для начинающих
Если вы далеки от работы с базами данных, для вас может быть открытием, что ноль – это не значение NULL, хотя, признаем, они созвучны. Кроме того, NULL не является значением пустой строки, хотя можно найти поле, содержащее данные любого типа.
NULL можно представить как значение для представления неизвестного фрагмента данных (обратите внимание: не нулевого, хотя поле при этом выглядит пустым). А еще он не равен ничему, даже другому NULL. И сегодня мы поговорим об этом загадочном (на первый взгляд) значении NULL более подробно.
Пример значения NULL
Итак, что вы должны знать о значении NULL? Давайте разбираться.
Представьте себе письменный стол, на котором лежат канцелярские принадлежности: 6 шариковых ручек и 2 простых карандаша. Также известно, что в ящике стола должны быть фломастеры. Но вот сколько их и есть ли они вообще — данных нет. Если нам нужно составить таблицу инвентаризации с вводом значения NULL, то выглядеть она будет так:
InventoryID | Item | Количество |
1 | ручки | 6 |
2 | карандаши | 2 |
3 | фломастеры | NULL |
Как вы понимаете, принимать за «0» количество фломастеров в данном случае было бы неверным, так как подобная запись показывала бы, что фломастеров нет вообще. Но точные данные об их количестве отсутствуют, поэтому может оказаться, что несколько штук все же есть.
Значение NULL и НЕ NULL
IS NULL и IS NOT NULL – специально созданные операторы, которые осуществляют сравнение имеющихся NULLов. IS NULL возвращает истину, если операнда является NULLом. Соответственно, если операнд не является NULLом, то значение будет ложным.
select case when null is null then ‘YES’ else ‘NO’ end from dual; — YES
select case when ‘a’ is null then ‘YES’ else ‘NO’ end from dual; — NO
IS NOT NULL имеет обратный принцип: значение будет истинным, если операнд не является NULLом, и ложным, если он таковым является.
select case when ‘a’ is NOT null then ‘YES’ else ‘NO’ end from dual; — YES
select case when null is NOT null then ‘YES’ else ‘NO’ end from dual; — NO
Учтите, что когда речь идет об отсутствующих значениях, есть особые случаи их сравнения:
Вот так проявляет себя DECODE:
, null, ‘EMPTY’ — это условие будет истинным
Значение NULL в MySQL
Результат при сравнении NULLов, в зависимости от операции SQL, часто будет иметь значение NULL. Предположим, что А НЕДЕЙСТВИТЕЛЕН:
Ваш Путь в IT начинается здесь
Логические операции и NULL
Для логических операторов AND и OR есть свои особенности при работе со значением NULL. Краткое руководство рассмотрим на примере.
Чаще всего с неизвестным результатом работают как с ЛОЖЬЮ:
select 1 from dual where dummy = null; — запрос не вернёт записей
При отрицании неизвестности результатом будет НЕИЗВЕСТНО:
exec test_bool( not(null = null)); — UNKNOWN
exec test_bool( not(null = ‘a’) ); — UNKNOWN
exec test_bool(null or true); — TRUE Главный карьерный консультант
Мы в GeekBrains каждый день обучаем людей новым профессиям и точно знаем, с какими трудностями они сталкиваются. Вместе с экспертами по построению карьеры поможем определиться с новой профессией, узнать, с чего начать, и преодолеть страх изменений.
Карьерная мастерская это:
Уже 50 000 человек прошли мастерскую и сделали шаг к новой профессии!
Запишитесь на бесплатный курс и станьте ближе к новой карьере:
Зарегистрироваться и получить подарки
Операторы IN и NOT IN для значения NULL
Чтобы понять взаимодействие этих операторов с NULLом, рассмотрим пример.
Создадим таблицу Т, состоящую из одного числового столбца А и строками: 1, 2, 3 и NULL.
create table t as select column_value a from table(sys.odcinumberlist(1,2,3,null));
Затем выполним трассировку запроса (учтите, что для этого нужно обладать ролью PLUSTRACE).
От трассировки в листингах оставлена часть filter, чтобы показать преобразование указанных в запросе условий.
Теперь, после подготовительных действий, попробуем выбрать те записи, которые будут соответствовать набору (1, 2, NULL).
select * from t where a in(1,2,null); — вернёт [1,2]
— filter(«A»=1 OR «A»=2 OR «A»=TO_NUMBER(NULL))
По какой-то причине строка с NULLом не выбрана. Возможно, это случилось потому, что вычисление предиката «А»=TO_NUMBER(NULL) вернуло состояние НЕИЗВЕСТНО. Попробуем явно указать условие включения NULLов в результаты запросов:
select * from t where a in(1,2) or a is null; — вернёт [1,2,NULL]
— filter(«A» IS NULL OR «A»=1 OR «A»=2)
Попробуем с NOT IN:
select * from t where a not in(1,2,null); — no rows selected
— filter(«A»<>1 AND «A»<>2 AND «A»<>TO_NUMBER(NULL))
Ни одной записи так и не появилось.
Это объясняется тем, что трехзначная логика NOT IN не взаимодействует с NULLами: при попадании NULL в условия отбора данных можно не ждать.
Значение NULL и пустая строка в СУБД
Oracle отличается от стандартов ANSI SQL в определении NULLов: он проводит знак равенства между NULL и пустой строкой. Эта особенность программы рождает много споров, хотя Oracle и заявляет, что, возможно, в будущих релизах будет изменен подход в обработке пустой строки, как NULL. Но в реальности проведение таких изменений сомнительно, так как под эту СУБД написано неимоверное количество кода.
exec test_bool( » is null ); — TRUE
Если попытаться найти причину, почему вообще пустую строку стали считать эквивалентной NULL, то ответ можно найти в формате хранения varchar`ов и NULLов внутри блоков данных. Табличные строки Oracle хранит в структуре, представляющей собой заголовок и следующими за ним столбцы с данными.
Каждый столбец, в свою очередь, состоит из 2-х полей: длина данных в столбце (1 или 3 байта) и сами данные. При нулевой длине varchar2 в поле с данными нечего вносить, так как оно не занимает ни байта. В поле же, где указывается длина, вносится специальное значение 0xFF, что и означает отсутствие данных.
NULL Oracle представляет аналогично, то есть отсутствует поле с данными, а в поле длины данных вносится 0xFF. Так как изначально разработчики Oracle не разделяли эти два состояния, то и сейчас принцип внесения данных не изменился.
Значение NULL
Понятие «пустая строка» допустимо толковать как абсолютное отсутствие значения, так как ее длина равна нулю. NULL же, в свою очередь, имеет длину неопределенного значения. Поэтому выражение length (») возвращает NULL, а не ожидаемый ноль.
Еще одна причина, по которой нельзя сравнивать NULL с пустой строкой: выражение val = » вернёт состояние НЕИЗВЕСТНО, так как, по сути, идентично val=NULL.
Неопределенная длина пустой строки:
select length(») from dual; — NULL
Сравнение с пустой строкой невозможно:
Критика такого подхода Oracle к значениям NULL и пустой строки, основывается на том, что не всегда пустая строка может означать неизвестность. Например, когда менеджер-продавец вносит данные в карточку клиента, то в поле «Контактный номер» он может указать конкретный номер; также он может указать, что номер неизвестен (NULL); но еще он может указать, что номера как такового нет (пустая строка).
Отличия между null и undefined
Можно сказать, что NULL – это такое значение, которое является определенным для отсутствующего объекта. UNDEFINED же означает именно неопределенность. Например:
// значение переменной element до её инициализации не определённо: undefined
// здесь при попытке получения несуществующего элемента, метод getElementById возвращает null
// переменная element теперь инициализирована значением null, её значение определено
Осуществляя проверку на NULL или UNDEFINED, нужно помнить о разнице в операторах равенства (==) и идентичности (===): с первым оператором производится преобразование типов.
typeof null // object (не «null» из соображений обратной совместимости)
typeof undefined // undefined
null === undefined // false
null == undefined // true
Это все то, что вы должны знать о значении NULL. Обрастая опытом и применяя некоторые уловки для избежания NullPointerException, вы научитесь делать безопасный код. Главным образом неразбериха возникает из-за того, что NULL может трактоваться как пустое значение или как неидентифицированное.
Поэтому важно документально фиксировать поведение метода, когда есть входящее значение NULL. Держите в памяти, что NULL – это значение по умолчанию ссылочных переменных. И вызывать методы экземпляра или получать доступ к переменным экземпляра, применяя NULL-ссылку, вы не можете.