SQL выберите отдельный исключить строки NULL
Желаемый результат: исключить строки с NULL-данными; ПЕРВЫЙ ряд.
Я понимаю, что мы можем использовать предложение where для обоих столбцов для фильтрации пустых строк, но что, если у нас есть большое количество столбцов для выбора, и мы не хотим иметь длинные where пункт?
Мы будем очень признательны за любое решение или обходной путь. Спасибо.
3 ответа
Если вы собираетесь делать это на чистом SQL (без динамического SQL), то, так или иначе, вам нужно будет перечислить имена столбцов.
Основное решение — использовать or условия:
Вы также можете использовать coalesce() — по комментариям HABO:
concat_ws() тоже приходит на ум — это та же логика, что и coalesce() действительно:
Наконец, мы могли бы также использовать cross apply чтобы развернуть, затем агрегировать:
Если вы хотите исключить строки, где все значения NULL , затем WHERE пункт — это то, что вам нужно:
Если вас интересует написание запроса, вы можете облегчить это, используя таблицы метаданных для своей базы данных. Вы можете запросить метаданные «столбцов» для построения where предложение — либо с помощью SQL, либо электронной таблицы, либо другого инструмента.
Глава 18. ОГРАНИЧЕНИЕ ЗНАЧЕНИЙ ВАШИХ ДАННЫХ
В Главе 17 вы узнали, как создаются таблицы. Теперь мы более основательно покажем вам, как вы можете устанавливать ограничения в таблицах. Ограничения это часть определений таблицы, вводящая ограничения на значения, которые вы можете вводить в столбцы. До этого места в данной книге ограничениями на значения, которые вы могли вводить, были тип данных и размер вводимых значений, которые должны быть совместимы с теми столбцами, куда эти значения помещаются (как определено для команды CREATE TABLE или команды ALTER TABLE).
Ограничения дают вам значительно б́ольшие возможности, и скоро вы это увидите. Вы также узнаете, как определять значения по умолчанию. По умолчанию — это значение, которое вставляется автоматически в любой столбец таблицы, когда значение для этого столбца отсутствует в команде INSERT для этой таблицы. NULL это наиболее широко используемое значение по умолчанию, но в этой главе будет показано, как определять и другие значения по умолчанию.
ОГРАНИЧЕНИЕ ТАБЛИЦ
Когда вы создаёте таблицу (или, когда вы её изменяете), вы можете указывать ограничения на значения, которые могут быть введены в поля. Если вы это сделали, SQL будет отклонять любые значения, нарушающие критерии, которые вы определили.
Есть два основных типа ограничений: ограничение столбца и ограничение таблицы. Различие между ними в том, что ограничение столбца применяется только к отдельным столбцам, в то время как ограничение таблицы применяется к группам из одного и более столбцов.
ОБЪЯВЛЕНИЕ ОГРАНИЧЕНИЙ
Вы вставляете ограничение столбца в конец имени столбца после типа данных и перед запятой. Ограничение таблицы помещается в конец имени таблицы после последнего имени столбца, но перед заключительной круглой скобкой. Далее показан синтаксис для команды CREATE TABLE, расширенной для включения в неё ограничения:
(Для краткости мы опустили аргумент размера, который иногда используется с типом данных.) Поля данных в круглых скобках после ограничения таблицы это поля, к которым применено данное ограничение. Ограничение столбца, естественно, применяется к тем столбцам, после чьих имен оно следует. Остальная часть этой глава будет описывать различные типы ограничений и их использование.
ИСПОЛЬЗОВАНИЕ ОГРАНИЧЕНИЙ ДЛЯ ИСКЛЮЧЕНИЯ ПУСТЫХ (NULL) ЗНАЧЕНИЙ
Вы можете использовать команду CREATE TABLE, чтобы предотвратить появление в поле пустых (NULL) указателей с помощью ограничения NOT NULL. Это ограничение накладывается только для разнообразных столбцов. Вы можете вспомнить, что NULL это специальное обозначение, которое отмечает поле как пустое. NULL может быть полезен в тех случаях, когда вы хотите быть от этого гарантированы.
Очевидно, что первичные ключи никогда не должны быть пустыми, поскольку это будет нарушать их функциональные возможности. Кроме того, такие поля как имена требуют в большинстве случаев определённых значений. Например, вы, вероятно, захотите иметь имя для каждого заказчика в таблице Заказчиков. Если вы поместите ключевые слова NOT NULL сразу после типа данных (включая размер) столбца, любая попытка поместить значение NULL в это поле будет отклонена. В противном случае SQL принимает, что NULL разрешен.
Например, давайте улучшим наше определение таблицы Продавцов, не позволяя помещать NULL-значения в столбцы snum или sname:
Важно помнить, что любому столбцу с ограничением NOT NULL должно быть установлено значение в каждом предложении INSERT, воздействующем на таблицу. При отсутствии NULL, SQL может не иметь значений для установки в эти столбцы, если, конечно, значение по умолчанию, описанное ранее в этой главе, уже не было назначено. Если ваша система поддерживает использование ALTER TABLE, чтобы добавлять новые столбцы к уже существующей таблице, вы можете, вероятно, помещать ограничение столбцов типа NOT NULL для этих новых столбцов. Однако, если вы предписываете новому столбцу значение NOT NULL, текущая таблица должна быть пустой.
УБЕДИТЕСЬ, ЧТО ЗНАЧЕНИЯ УНИКАЛЬНЫ
В Главе 17 мы обсудили использование уникальных индексов, чтобы заставить поля иметь различные значения для каждой строки. Эта практика осталась с прежних времен, когда SQL поддерживал ограничение UNIQUE. Уникальность это свойство данных в таблице, и поэтому его более логично назвать ограничением этих данных, а не просто свойством логического отличия, связывающим объект данных (индекс). Несомненно, уникальные индексы — один из самых простых и наиболее эффективных методов предписания уникальности. По этой причине некоторые реализации ограничения UNIQUE используют уникальные индексы; то есть они создают индекс, не сообщая вам об этом. Остается фактом, что вероятность беспорядка в базе данных достаточно мала, если вы предписываете уникальность вместе с ограничением.
УНИКАЛЬНОСТЬ КАК ОГРАНИЧЕНИЕ СТОЛБЦА
Время от времени вам нужно будет убедиться, что все значения, введённые в столбец, отличаются друг от друга. Например, первичные ключи. Если вы помещаете ограничение столбца UNIQUE в поле при создании таблицы, база данных отклонит любую попытку ввода в это поле для одной из строк значения, которое уже представлено в другой строке. Это ограничение может применяться только к полям, которые были объявлены как непустые (NOT NULL), так как не имеет смысла позволить одной строке таблицы иметь значение NULL, а затем исключать другие строки с NULL-значениями как дубликаты.
Вот дальнейшее усовершенствование нашей команды создания таблицы Продавцов:
Когда вы объявляете поле sname уникальным, убедитесь, что две Mary Smith будут введены различными способами, например, Mary Smith и M. Smith. В то же время это не так уж необходимо с функциональной точки зрения, потому что поле snum в качестве первичного ключа всё равно обеспечит отличие этих двух строк, что проще, нежели помнить, что эти Smith не идентичны. Столбцы (не первичные ключи), чьи значения требуют уникальности, называются ключами-кандидатами или уникальными ключами.
УНИКАЛЬНОСТЬ КАК ОГРАНИЧЕНИЕ ТАБЛИЦЫ
Вы можете также определить группу полей как уникальную с помощью команды UNIQUE ограничения таблицы. Объявление группы полей уникальной отличается от объявления уникальными индивидуальных полей, так как это комбинация значений, а не просто индивидуальное значение, которое обязано быть уникальным.
Уникальность группы это представление порядка так, что бы пары строк со значениями столбцов "a", "b" и "b", "a" рассматривались отдельно одна от другой. Наша БД сделана так, чтобы каждый заказчик был назначен одному, и только одному, продавцу. Это означает, что каждая комбинация номера заказчика (cnum) и номера продавца (snum) в таблице Заказчиков должна быть уникальной. Вы можете убедиться в этом, создав таблицу Заказчиков таким способом:
Обратите внимание, что оба поля в ограничении таблицы UNIQUE всё ещё используют ограничение столбца NOT NULL. Если бы мы использовали ограничение столбца UNIQUE для поля cnum, такое ограничение таблицы было бы необязательным. Если значения поля cnum различны для каждой строки, то не может быть двух строк с идентичной комбинацией значений полей cnum и snum. То же самое получится, если мы объявим поле snum уникальным, хотя это и не будет соответствовать нашему примеру, так как продавец будет назначен многочисленным заказчикам. Следовательно, ограничение UNIQUE таблицы наиболее полезно, когда вы не хотите, чтобы отдельные поля были уникальными.
Предположим, например, что мы разработали таблицу для отслеживания всех заказов каждый день для каждого продавца. Каждая строка такой таблицы представляет сумму чисел любых заказов, а не просто индивидуальный заказ. В этом случае мы могли бы устранить некоторые возможные ошибки, убедившись, что на каждый день имеется не более чем одна строка для данного продавца или что каждая комбинация полей snum и odate является уникальной. Вот как, например, мы могли бы создать таблицу с именем Salestotal:
Кроме того, имеется команда, которую вы будете использовать, чтобы помещать текущие данные в эту таблицу:
ОГРАНИЧЕНИЕ ПЕРВИЧНЫХ КЛЮЧЕЙ
До этого мы воспринимали первичные ключи исключительно как логические понятия. Хоть мы и знаем, что такое первичный ключ и как он должен использоваться в любой таблице, мы не в курсе, "знает" ли об этом SQL. Поэтому мы использовали ограничение UNIQUE или уникальные индексы в первичных ключах, чтобы предписывать им уникальность. В более ранних версиях языка SQL это было необходимо и могло выполняться данным способом. Однако теперь SQL поддерживает первичные ключи непосредственно ограничением Первичный Ключ (PRIMARE KEY). Это ограничение может быть доступным или недоступным в вашей системе. PRIMARY KEY может ограничивать таблицы или их столбцы. Это ограничение работает так же, как и ограничение UNIQUE, за исключением случая, когда только один первичный ключ (для любого числа столбцов) может быть определен для данной таблицы. Имеется также различие между первичными ключами и уникальностью столбцов в способе их использования с внешними ключами, о которых будет рассказано в Главе 19. Синтаксис и определение их уникальности — те же, что и для ограничения UNIQUE. Первичные ключи не могут позволить значений NULL. Это означает, что, подобно полям в ограничении UNIQUE, любое поле, используемое в ограничении PRIMARY KEY, должно уже быть объявлено NOT NULL. Имеется улучшенный вариант создания нашей таблицы Продавцов:
Как видите, уникальность (UNIQUE) полей может быть объявлена для той же самой таблицы. Лучше всего помещать ограничение PRIMARY KEY в поле (или в поля), которое будет образовывать ваш уникальный идентификатор строки, и сохранять ограничение UNIQUE для полей, которые должны быть уникальными логически (такие как номера телефона или поле sname), а не для идентификации строк.
ПЕРВИЧНЫЕ КЛЮЧИ БОЛЕЕ ЧЕМ ОДНОГО ПОЛЯ
Ограничение PRIMARY KEY может также быть применено для нескольких полей, составляющих уникальную комбинацию значений. Предположим, что ваш первичный ключ это имя и вы имеете первое имя и последнее имя, сохранёнными в двух различных полях (так, что вы можете организовывать данные с помощью любого из них). Очевидно, что ни первое, ни последнее имя нельзя заставить быть уникальным самостоятельно, но мы можем каждую из этих двух комбинаций сделать уникальной.
Мы можем применить ограничение таблицы PRIMARY KEY для пар:
Одна проблема в этом подходе — мы можем вынудить появление уникальности, например, введя Mary Smith и M. Smith. Это может ввести в заблуждение, потому что ваши служащие могут не знать, кто из них кто. Обычно более надежный способ определения числового поля, которое могло бы отличать одну строку от другой, — иметь первичный ключ и применять ограничение UNIQUE для двух имен полей.
ПРОВЕРКА ЗНАЧЕНИЙ ПОЛЕЙ
Конечно, имеется любое число ограничений, которые можно устанавливать для данных, вводимых в ваши таблицы, чтобы увидеть, например, находятся ли данные в соответствующем диапазоне или правильном формате, о чем SQL, естественно, не может знать заранее. Для этого SQL предоставляет вам ограничение CHECK, позволяющее установить условие, которому должно удовлетворять значение, вводимое в таблицу, прежде чем оно будет принято.
Ограничение CHECK состоит из ключевого слова CHECK, сопровождаемого предложением предиката, который использует указанное поле. Любая попытка модифицировать или вставить значение поля, которое могло бы сделать этот предикат неверным, будет отклонена.
Давайте рассмотрим ещё раз таблицу Продавцов. Столбец комиссионных выражается десятеричным числом и поэтому может быть умножен непосредственно на сумму приобретений, в результате чего будет получена сумма комиссионных (в долларах) продавца с установленным справа значком доллара ($). Кто-то может использовать понятие процента, однако можно ведь об этом и не знать. Если человек введёт по ошибке 14 вместо .14 для указания, в процентах, своих комиссионных, это будет расценено как 14.0 , что является законным десятеричным значением и будет нормально воспринято системой. Чтобы предотвратить эту ошибку, мы можем наложить ограничение CHECK столбца, чтобы убедиться, что вводимое значение меньше 1.
ИСПОЛЬЗОВАНИЕ CHECK ДЛЯ ПРЕДОПРЕДЕЛЕНИЯ ДОПУСТИМОГО ВВОДИМОГО ЗНАЧЕНИЯ
Мы можем также использовать ограничение CHECK, чтобы защитить от ввода в поле определённых значений, и таким образом предотвратить ошибку. Например, предположим, что городами, в которых мы имеем офисы сбыта, являются Лондон, Барселона, Сан-Хосе и Нью-Йорк. Если вам известны все продавцы, работающие в каждом из этих офисов, нет необходимости разрешать ввод других значений. Если же нет, использование ограничения может предотвратить опечатки и другие ошибки.
Конечно, если вы собираетесь сделать это, вы должны быть уверены, что ваша компания не открыла уже других новых офисов сбыта. Большинство программ баз данных поддерживают команду ALTER TABLE (см. Главу 17), которая позволяет изменять определение таблицы, даже когда она находится в использовании. Однако изменение или удаление ограничений не всегда возможно для этих команд, даже там, где это вроде бы поддерживается.
Если вы используете систему, которая не может удалять ограничения, вы должны будете создавать (CREATE) новую таблицу и передавать информацию из старой таблицы в неё всякий раз, когда вы хотите изменить ограничение. Конечно же, вы не захотите делать это часто и со временем вообще перестанете это делать. Создадим таблицу Заказов:
Как мы уже говорили в Главе 2, тип DATЕ (ДАТА) широко поддерживается, но не является частью стандарта ANSI. Что же делать, если мы используем БД, которая, следуя ANSI, не распознаёт тип DATЕ? Если мы объявим поле odate с любым числовым типом, мы не сможем использовать слэш (/) или тире (-) в качестве разделителя. Так как печатаемые номера это символы ASCII, мы можем объявить тип поля date — CHAR. Основная проблема в том, что мы должны будем использовать одинарные кавычки всякий раз, когда ссылаемся на значение поля odate в запросе. Нет более простого решения этой проблемы там, где тип DATЕ стал таким популярным. В качестве иллюстрации, давайте объявим поле odate типом CHAR. Мы можем, как минимум, наложить на него наш формат с ограничением CHECK:
Кроме того, если вы хотите, вы можете наложить ограничения, гарантирующие, что введенные символы — числа и что они в пределах значений нашего диапазона.
ПРОВЕРКА УСЛОВИЙ, БАЗИРУЮЩИХСЯ НА НЕСКОЛЬКИХ ПОЛЯХ
Вы можете также использовать CHECK в качестве табличного ограничения. Это полезно в тех случаях, когда вы хотите включить более одного поля строки в условие. Предположим, что комиссионные .15 и выше будут разрешены только для продавца из Барселоны. Вы можете указать это с помощью следующего табличного ограничения CHECK:
Как видите, два различных поля должны быть проверены, чтобы определить, верен предикат или нет. Имейте в виду, что это — два разных поля одной и той же строки. Хотя вы можете использовать несколько полей, SQL не может проверить более одной строки одновременно. Вы не можете, например, использовать ограничение CHECK, чтобы удостовериться, что все комиссионные в данном городе одинаковы. Чтобы сделать это, SQL должен всякий раз, просматривая другие строки таблицы, когда вы модифицируете или вставляете строку, видеть, что значение комиссионных указано для текущего города. SQL этого делать не умеет.
Фактически вы могли бы использовать сложное ограничение CHECK для вышеупомянутого, если бы знали заранее, каковы должны быть комиссионные в разных городах. Например, вы могли бы установить такое ограничение:
Мы подали вам идею. Чем налагать такой комплекс ограничений, вы могли бы просто использовать представление с предложением WITH CHECK OPTION, которое имеет все эти условия в своем предикате (смотри в Главах 20 и 21 информацию о представлении и о WITH CHECK OPTION).
Пользователи могут обращаться к представлению таблицы вместо самой таблицы. Одним из преимуществ этого будет то, что процедура изменения в ограничении не будет такой болезненной или трудоёмкой. Представление с WITH CHECK OPTION — хороший заменитель ограничению CHECK, что будет показано в Главе 21.
УСТАНОВКА ЗНАЧЕНИЙ ПО УМОЛЧАНИЮ
Когда вы вставляете строку в таблицу без указания в ней значений для каждого поля, SQL должен иметь значение по умолчанию для включения его в определённое поле, или же команда будет отклонена. Наиболее общим значением по умолчанию является NULL. Это — значение по умолчанию для любого столбца, которому не было дано ограничение NOT NULL или который имеет другое значение по умолчанию. Значение DEFAULT (ПО УМОЛЧАНИЮ) указывается в команде CREATE TABLE тем же способом, что и ограничение столбца, хотя, с технической точки зрения, значение DEFAULT — не ограничительного свойства: оно не ограничивает значения, которые вы можете вводить, а просто определяет, что может случиться, если вы не введёте любое из них.
Предположим, что вы работаете в офисе Нью-Йорка и подавляющее большинство ваших продавцов живут в Нью-Йорке. Вы можете указать Нью-Йорк в качестве значения поля city по умолчанию для вашей таблицы Продавцов:
Конечно, вводить значение Нью-Йорк в таблицу каждый раз, когда назначается новый продавец, не так уж необходимо, и можно просто пренебречь им (не вводя его), даже если оно должно иметь некоторое значение. Значение по умолчанию такого типа более предпочтительно, чем, например, длинный конторский номер, указывающий на ваш собственный офис в таблице Заказов. Длинные числовые значения более предрасположены к ошибке, поэтому, если подавляющее большинство (или все) ваших заказов должны иметь ваш собственный конторский номер, желательно устанавливать для них значение по умолчанию.
Другой способ использования значения по умолчанию — использовать его как альтернативу NULL. Так как NULL (фактически) является false при любом сравнении, ином, нежели IS NULL, он может быть исключён с помощью большинства предикатов.
Иногда вам нужно видеть пустые значения ваших полей, не обрабатывая их каким-то определённым образом. Вы можете установить значения по умолчанию, типа нуль или пробел, которые функционально меньше по значению, чем просто не установленное значение — пустое значение (NULL). Различие между ними и обычным NULL в том, что SQL будет обрабатывать их так же, как и любое другое значение.
Предположим, что заказчикам не назначены оценки изначально. Каждые шесть месяцев вы повышаете оценку всем вашим заказчикам, имеющим оценку ниже средней, включая и тех, кто предварительно не имел никакого назначения оценки. Если вы хотите выбрать всех этих заказчиков как группу, следующий запрос исключит всех заказчиков с оценкой = NULL:
Однако, если вы назначили значение по умолчанию = 000, в поле rating, заказчики без оценок будут выбраны наряду с другими. Приоритет каждого метода зависит от ситуации. Если вы будете делать запрос с помощью поля оценки, то захотите ли вы включить строки без значений, или исключите их?
Другая характеристика значений по умолчанию этого типа позволит объявить поле оценки как NOT NULL. Если вы используете его по умолчанию, чтобы избежать значений = NULL, то это, вероятно, хорошая защита от ошибок. Вы можете также использовать ограничения UNIQUE или PRIMARY KEY в этом поле. Если вы сделаете это, то имеете в виду, что только одна строка одновременно может иметь значение по умолчанию. Любую строку, которая содержит значение по умолчанию нужно будет модифицировать, прежде чем другая строка с установкой по умолчанию будет вставлена. Это не так, как при обычном использовании значений по умолчанию, поэтому ограничения UNIQUE и PRIMARY KEY (особенно последнее) обычно не устанавливаются для строк со значениями по умолчанию.
РЕЗЮМЕ
Вы теперь владеете несколькими способами управления значениями, которые могут быть введены в ваши таблицы. Вы можете использовать ограничение NOT NULL, чтобы исключать NULL; ограничение UNIQUE, чтобы вынуждать все значения в группе из одного или более столбцов отличаться друг от друга; ограничение PRIMARY KEY, для того чтобы делать в основном то же самое что и UNIQUE, но с различным окончанием, и наконец ограничение CHECK — для определения ваших собственных специальных условий, чтобы значения, встреченные перед ними, могли бы быть введены.
Кроме того, вы можете использовать предложение DEFAULT, которое будет автоматически вставлять значение по умолчанию в любое поле с именем, не указанным в INSERT, так же, как вставляется значение NULL, когда предложение DEFAULT не установлено и отсутствует ограничение NOT NULL.
Ограничения FOREIGN KEY или REFERENCES, о которых вы узнаете в Главе 19, очень похожи на них, за исключением того, что они связывают группу из одного или более полей с другой группой, и таким образом, сразу воздействуют на значения, которые могут быть введены в любую из этих групп.
Основные положения
Специальное значение NULL означает отсутствие данных, констатацию того факта, что значение неизвестно. По умолчанию это значение могут принимать столбцы и переменные любых типов, если только на них не наложено ограничение NOT NULL . Также, СУБД автоматически добавляет ограничение NOT NULL к столбцам, включенным в первичный ключ таблицы.
Основная особенность NULLа заключается в том, что он не равен ничему, даже другому NULLу. С ним нельзя сравнить какое-либо значение с помощью любых операторов: = , < , > , like … Даже выражение NULL != NULL не будет истинным, ведь нельзя однозначно сравнить одну неизвестность с другой. Кстати, ложным это выражение тоже не будет, потому что при вычислении условий Oracle не ограничивается состояниями ИСТИНА и ЛОЖЬ . Из-за наличия элемента неопределённости в виде NULLа существует ещё одно состояние — НЕИЗВЕСТНО .
Таким образом, Oracle оперирует не двухзначной, а трёхзначной логикой. Эту особенность заложил в свою реляционную теорию дедушка Кодд, а Oracle, являясь реляционной СУБД, полностью следует его заветам. Чтобы не медитировать над “странными” результатами запросов, разработчику необходимо знать таблицу истинности трёхзначной логики. Ознакомиться с ней можно, например, на английской википедии: Three-valued_logic.
Для удобства сделаем процедуру, печатающую состояние булевого параметра:
и включим опцию печати сообщений на консоль:
Привычные операторы сравнения пасуют перед NULLом:
Сравнение с NULLом
Существуют специальные операторы IS NULL и IS NOT NULL , которые позволяют производить сравнения с NULLами. IS NULL вернёт истину, если операнд имеет значение NULL и ложь, если он им не является.
Соответственно, IS NOT NULL действует наоборот: вернёт истину, если значение операнда отлично от NULLа и ложь, если он является NULLом:
Кроме того, есть пара исключений из правил, касающихся сравнений с отсутствующими значениями. Во-первых, — это функция DECODE , которая считает два NULLа эквивалентными друг другу. Во-вторых, — это составные индексы: если два ключа содержат пустые поля, но все их непустые поля равны, то Oracle считает эти два ключа эквивалентными.
DECODE идёт против системы:
Пример с составными индексами находится в параграфе про индексы.
Логические операции и NULL
Обычно, состояние НЕИЗВЕСТНО обрабатывается так же, как ЛОЖЬ . Например, если вы выбираете строки из таблицы и вычисление условия x = NULL в предложении WHERE дало результат НЕИЗВЕСТНО , то вы не получите ни одной строки. Однако, есть и отличие: если выражение НЕ(ЛОЖЬ) вернёт истину, то НЕ(НЕИЗВЕСТНО) вернёт НЕИЗВЕСТНО . Логические операторы AND и OR также имеют свои особенности при обработке неизвестного состояния. Конкретика в примере ниже.
В большинстве случаев неизвестный результат обрабатывается как ЛОЖЬ :
Отрицание неизвестности даёт неизвестность:
Оператор OR :
Оператор AND :
Операторы IN и NOT IN
Для начала сделаем несколько предварительных действий. Для тестов создадим таблицу T с одним числовым столбцом A и четырьмя строками: 1, 2, 3 и NULL
Включим трассировку запроса (для этого надо обладать ролью PLUSTRACE ).
В листингах от трассировки оставлена только часть filter, чтобы показать, во что разворачиваются указанные в запросе условия.
Предварительные действия закончены, давайте теперь поработаем с операторами. Попробуем выбрать все записи, которые входят в набор (1, 2, NULL) :
Как видим, строка с NULLом не выбралась. Произошло это из-за того, что вычисление предиката «A»=TO_NUMBER(NULL) вернуло состояние НЕИЗВЕСТНО . Для того, чтобы включить NULLы в результат запроса, придётся указать это явно:
Попробуем теперь с NOT IN :
Вообще ни одной записи! Давайте разберёмся, почему тройка не попала в результаты запроса. Посчитаем вручную фильтр, который применила СУБД, для случая A=3 :
Из-за особенностей трёхзначной логики NOT IN вообще не дружит с NULLами: как только NULL попал в условия отбора, данных не ждите.
NULL и пустая строка
Здесь Oracle отходит от стандарта ANSI SQL и провозглашает эквивалентность NULLа и пустой строки. Это, пожалуй, одна из наиболее спорных фич, которая время от времени рождает многостраничные обсуждения с переходом на личности, поливанием друг друга фекалиями и прочими непременными атрибутами жёстких споров. Судя по документации, Oracle и сам бы не прочь изменить эту ситуацию (там сказано, что хоть сейчас пустая строка и обрабатывается как NULL, в будущих релизах это может измениться), но на сегодняшний день под эту СУБД написано такое колоссальное количество кода, что взять и поменять поведение системы вряд ли реально. Тем более, говорить об этом они начали как минимум с седьмой версии СУБД (1992-1996 годы), а сейчас уже двенадцатая на подходе.
NULL и пустая строка эквивалентны:
непременный атрибут жёсткого спора:
Если последовать завету классика и посмотреть в корень, то причину эквивалентности пуcтой строки и NULLа можно найти в формате хранения varchar`ов и NULLов внутри блоков данных. Oracle хранит строки таблицы в структуре, состоящей из заголовка, за которым следуют столбцы данных. Каждый столбец представлен двумя полями: длина данных в столбце (1 или 3 байта) и, собственно, сами данные. Если varchar2 имеет нулевую длину, то в поле с данными писать нечего, оно не занимает ни байта, а в поле с длиной записывается специальное значение 0xFF , обозначающее отсутствие данных. NULL представлен точно так же: поле с данными отсутствует, а в поле с длиной записывается 0xFF . Разработчики Оракла могли бы, конечно, разделить эти два состояния, но так уж издревле у них повелось.
Лично мне эквивалентность пустой строки и NULLа кажется вполне естественной и логичной. Само название «пустая строка» подразумавает отсутствие значения, пустоту, дырку от бублика. NULL, в общем-то, обозначает то же самое. Но здесь есть неприятное следствие: если про пустую строку можно с уверенностью сказать, что её длина равна нулю, то длина NULLа никак не определена. Поэтому, выражение length(») вернёт вам NULL, а не ноль, как вы, очевидно, ожидали. Ещё одна проблема: нельзя сравнивать с пустой строкой. Выражение val = » вернёт состояние НЕИЗВЕСТНО , так как, по сути, эквивалентно val = NULL .
Длина пустой строки не определена:
Сравнение с пустой строкой невозможно:
Критики подхода, предлагаемого Ораклом, говорят о том, что пустая строка не обязательно обозначает неизвестность. Например, менеджер по продажам заполняет карточку клиента. Он может указать его контактный телефон (555-123456), может указать, что он неизвестен (NULL), а может и указать, что контактный телефон отсутствует (пустая строка). С оракловым способом хранения пустых строк реализовать последний вариант будет проблемно. С точки зрения семантики довод правильный, но у меня на него всегда возникает вопрос, полного ответа на который я так и не получил: как менеджер введёт в поле «телефон» пустую строку и как он в дальнейшем отличит его от NULLа? Варианты, конечно, есть, но всё-таки…
Вообще-то, если говорить про PL/SQL, то где-то глубоко внутри его движка пустая строка и NULL различаются. Один из способов увидеть это связан с тем, что ассоциативные коллекции позволяют сохранить элемент с индексом » (пустая строка), но не позволяют сохранить элемент с индексом NULL:
Использовать такие финты ушами на практике не стоит. Во избежание проблем лучше усвоить правило из доки: пустая строка и NULL в оракле неразличимы.
Математика NULLа
Этот маленький абзац писался пятничным вечером под пиво, на фоне пятничного РЕН-ТВшного фильма. Переписывать его лень, уж извините.
Задача. У Маши до замужества с Колей было неизвестное количество любовников. Коля знает, что после замужества у Маши был секс с ним, Сашей и Витей. Помогите найти Коле точное количество любовников Маши.
Очевидно, что мы ничем не сможем помочь Коле: неизвестное количество любовников Маши до замужества сводит все расчёты к одному значению — неизвестно. Oracle, хоть и назвался оракулом, в этом вопросе уходит не дальше, чем участники битвы экстрасенсов: он даёт очевидные ответы только на очевидные вопросы. Хотя, надо признать, что Oracle гораздо честнее: в случае с Колей он не будет заниматься психоанализом и сразу скажет: «я не знаю»:
С конкатенацией дела обстоят по другому: вы можете добавить NULL к строке и это её не изменит. Такая вот политика двойных стандартов.
NULL и агрегатные функции
Почти все агрегатные функции, за исключением COUNT (и то не всегда), игнорируют пустые значения при расчётах. Если бы они этого не делали, то первый же залетевший NULL привёл бы результат функции к неизвестному значению. Возьмём для примера функцию SUM , которой необходимо просуммировать ряд (1, 3, null, 2) . Если бы она учитывала пустые значения, то мы бы получили такую последовательность действий:
1 + 3 = 4; 4 + null = null; null + 2 = null .
Вряд ли вас устроит такой расчёт при вычислении агрегатов, ведь вы наверняка не это хотели получить. А какой бы был геморрой с построением хранилищ данных… Бррррр…
Таблица с данными. Используется ниже много раз:
Пустые значения игнорируются агрегатами:
Функция подсчёта количества строк COUNT , если используется в виде COUNT(*) или COUNT(константа) , будет учитывать пустые значения. Однако, если она используется в виде COUNT(выражение) , то пустые значения будут игнорироваться.
с константой:
С выражением:
Также, следует быть осторожным с функциями вроде AVG . Поскольку она проигнорирует пустые значения, результат по полю N будет равен (1+3+2)/3 , а не (1+3+2)/4 . Возможно, такой расчёт среднего вам не нужен. Для решения подобных проблем есть стандартное решение — воспользоваться функцией NVL :
Агрегатные функции возвращают состояние НЕИЗВЕСТНО , если они применяются к пустому набору данных, либо если он состоит только из NULLов. Исключение составляют предназначенные для подсчёта количества строк функции REGR_COUNT и COUNT(выражение) . Они в перечисленных выше случаях вернут ноль.
Набор данных только из NULLов:
Пустой набор данных:
NULL в OLAP
Очень коротко ещё об одной особенности, связанной с агрегатами. В многомерных кубах NULL в результах запроса может означать как отсутствие данных, так и признак группировки по измерению. Самое противное, что на глаз эти две его ипостаси никак не различишь. К счастью, есть специальные функции GROUPING и GROUPING_ID , у которых глаз острее. GROUPING(столбец) вернёт единицу, если NULL в столбце измерения означает признак группировки по этому столбцу и ноль, если там содержится конкретное значение (в частности, NULL). Функция GROUPING_ID — это битовый вектор из GROUPING ов, в этой заметке она точно лишняя.
В общем, такая вот краткая и сумбурная информация про дуализм NULLа в многомерном анализе. Ниже пример использования GROUPING , а за подробностями велкам ту Data Warehousing Guide, глава 21.
Удобная фишка sqlplus: при выводе данных заменяет NULL на указанную строку: