[SQL] зачем NULL трактуется так извращенно?

aleks058

Субж.
Почему я не могу написать
select * from table where column = :value

передать :value = NULL и получить те значения, которые ожидает любой нормальный человек?
Кто это придумал я нашел, но не могу понять, почему такой непрозрачный неадекват кочует из версии в версию ведущих СУБД и люди продолжают ходить по граблям, плеваться и говорить "так написано в СТАНДАРТЕ"?
Может есть какой-то реальный бенефит, о котором я не знаю?

klyv

NULL - "значение неизвестно". сравнения с ним дают такие же неизвесные значения, но не true.
"бенефит" - простота и интуитивность определения поведения СУБД при работе с неопределёнными значениями. ну и стандартность подхода, разумеется :)

pitrik2

добавлю что это еще способствует думанию про отсутствие данных
когда видишь что в запросе есть обработка NULL сразу понимаешь зачем это надо
когда видишь что ее там нет, сразу начинаешь думать а что будет с запросом если нет данных
эдакая наглядность дополнительная
вощем наллы точняк нужны
другой вопрос почему СУБД внутри себя автоматически не преобразуют
where column = :value
в
where :value IS NOT NULL AND column = :value) OR (:value IS NULL AND column IS NULL
хз почему не преобразуют
может их просто ломает разные обработчики для =,>,< писать...
еще добавлю чтоб не забывали, что в оракле пустая строка и NULL одно и то же
P.S.
чет я совсем туго излагаю свои мысли, надеюсь мя поймут

aleks058

Окей.
Сравнения с NULL дают NULL.
Люди, работающие по несколько лет исключительно с базами данных, офигевают, когда узнают, что

NULL or TRUE = TRUE
NULL and TRUE = NULL

Логика есть, я знаю, но она непрозрачна для мозга.
Еще на поверку оказывается, что в SQL нету типа Boolean, более того, БД не понимают, когда хочешь написать
select (1 = 1) from dual

или его аналог - приходится жутко шаманить.
Короче, перестал я понимать философию БД.

pitrik2

select (1 = 1) from dual
я вроде не СУБД, но это тоже не понимаю :(
что ты хочешь в результе получить? результат присвоения единицы к единице?
указатель в памяти что ли?

aleks058

NULL - отсутствие данных. Они нужны, не спорю.
Но почему же тогда

(отсутствие данных) != (отсутствие данных)

?
Зачем трактовать NULL как какое-то суперзначение, логика работы с которым отличается от логики других значений?
Не хочется думать, во что придется развернуть условие в

select * from table where column1 = :value1 and column2 = :value2 or column3 = :value3

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

aleks058

Я хочу получить TRUE.

pitrik2

в SQL нету типа Boolean
эгегей, зачем врешь?
стандарт SQL99 определяет тип BOOLEAN
если чего нет в оракле, это не значит что его нет в SQL
с каких эпор оракл стал стандартом SQL ?

pitrik2

Но почему же тогда

(отсутствие данных) != (отсутствие данных)

?
гыгы
допустим что отсутствие = отсутсвие
что делать в случае джойна таблиц?
тебе не кажется что такой джойн получается бредом?

pitrik2

select (1 = 1)
Я хочу получить TRUE.
кстати, а ты пробовал это на СУБД в которых есть BOOLEAN?
может это как раз и есть TRUE?
например в постгресе

aleks058

Нет.
В случае джойна таблиц NULL трактовать как обычное значение.
NULL джойнятся с NULL - вполне логично.

aleks058

К сожалению, Посгреса не имею.
Я к тому, что тестить логику БД выраженями

select (1 = 1) or NULL

не удается. Приходится изгаляться с where.
PS. И я бы даже стерпел такое тупое поведение NULL, но
1. Базовики говорят, что постоянно юзают NVL, чтобы избавляться от NULL-ов.
Спрашивается, зачем реализовано такое неочевидное поведение NULL, если в большинстве случаев людям, использующим БД, приходится использовать костыль в виде NVL, чтобы его обойти?
2. Все можно понять. Я надеялся, что напишу функцию Equal(value1, value2 которая внутри будет правильно сравнивать два NULL-значения, НО

select * from table where Equal(column, :value)

Ораклу не нравится. Подозреваю, что SQL сервер тоже не схавает.
Надо будет, например, приписать сравнение с 1

select * from table where Equal(column, :value) = 1

Вашу мать! И это в 2008 году!

katrin2201

Короче, перестал я понимать философию БД.
Может быть, это как-то поможет =)

pitrik2

Подозреваю, что SQL сервер тоже не схавает
если не ошибаюсь, там вообще нельзя создать функцию возвращающую boolean

aleks058

Спасибо за ссылки, немного просветлел.
Интересно, какой процент разработчиков БД осознают это все и обходятся без вуду-программинга.

zya369

добавлю что это еще способствует думанию про отсутствие данных
когда видишь что в запросе есть обработка NULL сразу понимаешь зачем это надо
когда видишь что ее там нет, сразу начинаешь думать а что будет с запросом если нет данных
эдакая наглядность дополнительная
вощем наллы точняк нужны
другой вопрос почему СУБД внутри себя автоматически не преобразуют
В ответ на:
where column = :value
в
В ответ на:
where :value IS NOT NULL AND column = :value) OR (:value IS NULL AND column IS NULL
хз почему не преобразуют
может их просто ломает разные обработчики для =,>,< писать...

имхо ты сам себе противоречишь :confused:
если б база так все разворачивала, то никто б и не думал об "отсутствии данных" - было б тоже самое, чего хочет автор

0000

Не увидел ничего из разряда вуду-программирования. Это достаточно базовые вещи для Оракла, к сожалению :( (в том смысле, что есть куда более сложных и неочевидных вещей).

zya369

NULL джойнятся с NULL - вполне логично.

и что тут логичного? имхо как раз получится фигня (лень пример придумывать)

zya369

1. Базовики говорят, что постоянно юзают NVL, чтобы избавляться от NULL-ов.
Спрашивается, зачем реализовано такое неочевидное поведение NULL, если в большинстве случаев людям, использующим БД, приходится использовать костыль в виде NVL, чтобы его обойти?

на счет "в большинстве случаев" ты имхо не прав ;)
ЗЫ касаемо примера:
допустим ты получил имя клиента и хочешь получить по нему что-то нам (пусть будет именно имя, а не ID).
далее допустим, что тебе не повезло и ты 1) получил пустую строку 2) ты на оракле (i.e. ты получил NULL)
по-твоему нормально, что, в твоем варианте, в этом случае ты получишь непустой результат?
ЗЗЫ вообще NULL полезная штука и то что она трактуется именно так имхо правильно... меня вот скажем раздражает ужасно, что в некоторых базах по умолчанию включено такое сравнение, как ты хочешь

aleks058

Я к тому, что даже эти "базовые", как ты говоришь, вещи сходу не очевидны даже для базовиков с многолетним стажем.

aleks058

Если в базе есть клиенты с именем NULL, они и должны получиться.
Этого будет ожидать любой нормальный человек (не мыслящий в троичной логике).

Marinavo_0507

> Я к тому, что даже эти "базовые", как ты говоришь, вещи сходу не очевидны даже для базовиков с многолетним стажем.
Ты в форуме больше трёх лет. Ты всё знаешь про HTTP?

aleks058

Сможешь развернуть условие, которое я выше дал
select * from table where column1 = :value1 and column2 = :value2 or column3 = :value3

чтобы оно адекватно NULL-ы обработало?
Представь, что кто-то уже развернул.
Ты сможешь понять, что он имел в виду at a glance?

Dasar

NULL or TRUE = TRUE
NULL and TRUE = NULL
...
select (1 = 1) from dual
и первое и второе - это чисто oracle, а не sql
в sql в первом случае должно быть: null or true - null
по второму должно быть
select 1=1 (без всяких dual-ов) и должно возвращаться true

aleks058

Начинаешь прям как Гусман :)
HTML меня мало интересует.
Общие вещи знаю.
А что, у тебя есть вопросы?

Marinavo_0507

Ну вот ты за 3 года не научился различать HTML и HTTP. Чего ты хочешь от тех "базовиков"?

pitrik2

имхо ты сам себе противоречишь :confused:
вроде не противоречю
в первой яасти я говорю что троичная логика нужна, что спец. обработка для null нужна
во второй части я говорю что возможно есть один случай когда возможно она не нужна: это равенство
но потом я сообразил про джойны и этот случай тоже отпал, ибо джойнить по наллам это бред
если бы токо можно было отличать джойн от чиста сравнения
как-то так:

where column ~= :value

ну тойсть есть спец. операция ~= которая может наллы сравнивать
но это же получается ненужный сахар, который еще и к путанице может привести
придумал еще вариант:

where NVL(column, SPECIAL_VALUE_THAT_NEVER_CAN_OCCURS_IN_THIS_COLUMN)
= NVL(:value, SPECIAL_VALUE_THAT_NEVER_CAN_OCCURS_IN_THIS_COLUMN)

pitrik2

и первое и второе - это чисто oracle, а не sql
в sql в первом случае должно быть: null or true - null
от тебя не ожидал :(
даже по обычной домашней логике: правда или всечтоугоднохотьчтотохотьничто = правда

aleks058

Ошибся.
Говорю же, с вебом дело мало имел, да и речь не о нем.
Я знаю их настолько, насколько мне это сейчас нужно.
Понадобится лучше - будет лучше.

Dasar

херотень оказывается в том, что есть null, а есть unknown
от туда же
Boolean datatype inconsistency
The ISO SQL:1999 standard introduced the Boolean datatype to SQL.[20] The Boolean datatype, as defined by the standard, can hold the truth values TRUE, FALSE, and UNKNOWN. Null is defined in this one instance as equivalent to the truth value UNKNOWN.
This "null equals UNKNOWN truth value" proposition introduces an inconsistency into SQL 3VL. One major problem is that it contradicts a basic property of nulls, the property of propagation. Nulls, by definition, propagate through all SQL expressions. The Boolean truth values do not have this property. Consider the following scenarios in SQL:1999, in which two Boolean truth values are combined into a compound predicate. According to the rules of SQL 3VL, and as shown in the 3VL truth table shown earlier in this article, the following statements hold:
( TRUE OR UNKNOWN ) → TRUE
( FALSE AND UNKNOWN ) → FALSE
However, because nulls propagate, treating null as UNKNOWN results in the following logical inconsistencies in SQL 3VL:
( TRUE OR NULL ) → NULL ( = UNKNOWN )
( FALSE AND NULL ) → NULL ( = UNKNOWN )
The SQL:1999 standard does not define how to deal with this inconsistency, and results could vary between implementations. Because of these inconsistencies and lack of support from vendors the SQL Boolean datatype did not gain widespread acceptance. Most SQL DBMS platforms now offer their own platform-specific recommendations for storing Boolean-type data.

zya369

Если в базе есть клиенты с именем NULL, они и должны получиться.

таких нет конечно же - просто кто-то откуда-то прислал тебе пустую строчку вместо имени

aleks058

Тогда ты никого и не получишь.
Что тебе не нравится?

6yrop

Люди, работающие по несколько лет исключительно с базами данных, офигевают, когда узнают, что
code:
NULL or TRUE = TRUE
NULL and TRUE = NULL
весь тред не читал, но про нетривиальность NULL-а и троичную логику Кузнецов на ВМК в базовом курсе лекций по базам данных рассказывал :smirk:

Marinavo_0507

Я знаю их настолько, насколько мне это сейчас нужно.
Понадобится лучше - будет лучше.
Теперь понятно, почему твои базовики мало знают про NULL?

zya369

Тогда ты никого и не получишь

ты недопонял :)
выбираешь ты не из таблицы с клиентами, а из таблицы где есть на них ссылка, которая может быть NULL

sbs-66

NULL сделали для того, чтобы можно было указывать отсутсвующий внешний индекс и корректно его обрабатывать при JOINах. И пользоваться им надо только в тех колонках таблицы, которые хранят ссылки на внешний индекс. Если ты хранишь NULL в качестве отсутсвующего имени, а потом удивляешься, что всё не интуитивно, то сам себе злобный буратин. Ибо так делать не надо. Если имени нет, то храни там пустую строку. Или FALSE или UNKNOWN, смотря что БД позволяет использовать.

pitrik2

Если имени нет, то храни там пустую строку. Или FALSE или UNKNOWN, смотря что БД позволяет использовать.
блин
прочти тред сначала
я ж писал: в оракле пустая строка и NULL это одно и то же
то есть если имени нет, то у тебя токо один вариант: хранить там NULL

sbs-66

Ну это как бы проблема оракла, а не SQL. И вопрос надо ставить не "почему NULL в SQL так нелогично себя ведёт?", а "почему пустая строка так нелогично интерпретируется ораклом?".

Dasar

Если ты хранишь NULL в качестве отсутсвующего имени
отсутствие имени - это и есть null
Ибо так делать не надо. Если имени нет, то храни там пустую строку. Или FALSE или UNKNOWN, смотря что БД позволяет использовать.
бред.
непонятно из каких соображений вот такое

table Car (id int, modelId int nullable)
table CarModel (int id, string name)

select car.id as carId, CarModel.Name as modelName from car
join carmodel on car.modelId=carmodel.id

должно отличаться от

table Car (id int, model string nullable)

select car.id as carId, car.model as modelName from car

т.е. совсем не понятно, почему мы в первом случае должны работать с null-ем, а во втором с каким-то мифическим unknown

kruzer25

если бы токо можно было отличать джойн от чиста сравнения
Можно принудительно заставить всех при селекте по нескольким таблицам сначала сджойнить все эти таблицы (по синтаксису JOIN а только потом писать WHERE :D

kruzer25

просто кто-то откуда-то прислал тебе пустую строчку вместо имени
Пустая строчка и налл - это разные вещи ;)
Если у кого-то в поле "имя" стоит налл, это значит, что у этого клиента не просто пустое имя - а что у этого клиента нет имени вообще.
Соответственно, джойнить в таком случае их действительно будет бредом.

kruzer25

е. совсем не понятно, почему мы в первом случае должны работать с null-ем, а во втором с каким-то мифическим unknown
Что-то я не понял, у тебя там что, появились модели с Id = null?
Если modelId = null, то ты как modelName получишь тот же null, а не то, что там лежит в какой-то мифической строке в таблице моделей по айди null.

aleks058

Никак не могу тебя понять.
Рассмотрим пару таблиц (точный синтаксис SQL не знаю, пишу псевдокод)

create table Role (int id, nullable string name)

insert into Role (1, "Admin")
insert into Role (2, "User")
insert into Role (3, null)

create table User (int id, nullable string name, nullable int roleId)

insert into User (1, "Pentaur2", 1);
insert into User (4, "", 2);
insert into User (5, "", 3);
insert into User (6, "", null);

Можешь на данном примере объяснить, что ты хочешь выбрать и какие проблемы возникли бы, если бы NULL трактовался так, как предлагаю я?

kruzer25

Можешь на данном примере объяснить, что ты хочешь выбрать и какие проблемы возникли бы, если бы NULL трактовался так, как предлагаю я?
Это к тебе вопрос, что ты с этим хочешь сделать, и где там у тебя будет сравнение двух null-ов.
В твоём примере я никак не могу увидеть, что с чем ты пытаешься сравнить, и где у тебя возникает проблема.

sinet

Если modelId = null, то ты как modelName получишь тот же null
В том то и дело, в одном случае ты получишь null в результате, а в другом советуют использовать мифический unknown, хотя по смыслу запросы возвращают одно и тоже. Unknown не спасет от обработки null значений, а только путанницы добавит. Не плодите сущностей. :grin:

mbolik1

Выведем пользователей у которых роли называются одинаково.
Вариант 1. Получим пары Пользователь, Роль и сравним их:

select
t1.name
from
(select u.id, u.name, r.name role from User u join Role r on (u.roleId = r.id) t1,
(select u.id, u.name, r.name role from User u join Role r on (u.roleId = r.id) t2
where t1.role = t2.role
and t1.id > t2.id;

Name
--------------
NULL

Пока всё хорошо: в наших таблицах таких пользователей нет, что мы и получили.
Вариант 2. Оптимизируем производительность. Сначала получим список ролей с одинаковым названием, а затем для них получим список пользователей:

select name from User u where u.roleId in
(Select r1.id from Role r1, Role r2 where r1.name = r2.name and r1.id >r2.id);

Name
--------------
""


Опа.
К слову при традиционной трактовке NULL оба способа дают одинаковый ответ.

kruzer25

Выведем пользователей у которых роли называются одинаково.
Ты извращенец вдвойне.
Во-первых, потому что выбираешь пользователей, у которых роли называются одинаково, а не просто одинаковые.
И во-вторых, потому что считаешь, что у двух пользователей - у первого роль безымянна, а у второго и роли-то нет - одинаковые названия ролей.

aleks058

Я хочу выбрать всех юзеров с заданной ролью:

select * from User where roleId = (select id from Role where name=:roleName)

если передать в качестве имени роли NULL, я ожидаю получить пользователя , а не получу никого.

zya369

Пустая строчка и налл - это разные вещи ;)

пенартур - ты долб ;)
я же написал, что это в случае оракла
ЗЫ ты уже научился правильно произносить SQL?

zya369

непрозрачный неадекват кочует из версии в версию ведущих СУБД и люди продолжают ходить по граблям, плеваться и говорить "так написано в СТАНДАРТЕ"?

точный синтаксис SQL не знаю

ну так построй уже свой луна-парк SQL со своим блек-джеком и шлюхами

mbolik1

Я хочу выбрать всех юзеров с заданной ролью:
code:
select * from User where roleId = (select id from Role where name=:roleName)
если передать в качестве имени роли NULL, я ожидаю получить пользователя , а не получу никого.

А если в качестве имени роли передать, по ошибке, "Админ". То выберется ""

kruzer25

если передать в качестве имени роли NULL, я ожидаю получить пользователя , а не получу никого.
И правильно, потому что ты не должен этого хотеть.
Если ты хочешь получить всех пользователей без роли - то ты говоришь "RoleId IS NULL". Если ты хочешь получить всех пользователей с ролью с именем-пустой строкой, и у тебя есть такая роль (но этого ты тоже не должен хотеть то ты в таблицу ролей кладёшь роль не с именем NULL, а с именем '', и потом передаёшь в выражение не null, а пустую строку.

aleks058

Реально фигня получается.
Тебе +1.
Тему можно закрывать.

pitrik2

блин
иногда ты так сильно надоедаешь ...
Оставить комментарий
Имя или ник:
Комментарий: