Почему Postgres обрабатывает NULL непоследовательно, когда задействованы уникальные ограничения? - PullRequest
19 голосов
/ 29 апреля 2011

Недавно я заметил несоответствие в том, как Postgres обрабатывает NULL в столбцах с уникальным ограничением.

Рассмотрим таблицу людей:

create table People (
   pid  int  not null,
   name text not null,
   SSN  text unique,
 primary key (pid)
);

Столбец SSN должен быть уникальным. Мы можем проверить, что:

-- Add a row.
insert into People(pid, name, SSN)
values(0, 'Bob', '123');

-- Test the unique constraint.
insert into People(pid, name, SSN)
values(1, 'Carol', '123');

Вторая вставка завершается неудачно, поскольку она нарушает ограничение уникальности для SSN. Все идет нормально. Но давайте попробуем NULL:

insert into People(pid, name, SSN)
values(1, 'Carol', null);

Это работает.

select *    
from People;

0;"Bob";"123"
1;"Carol";"<NULL>"

Уникальный столбец примет нулевое значение. Интересно. Как Postgres может утверждать, что null в любом случае уникален или не уникален в этом отношении?

Интересно, могу ли я добавить две строки с нулем в уникальный столбец.

insert into People(pid, name, SSN)
values(2, 'Ted', null);

select *    
from People;

0;"Bob";"123"
1;"Carol";"<NULL>"
2;"Ted";"<NULL>"

Да, я могу. Теперь в столбце SSN есть две строки с NULL, хотя номер SSN должен быть уникальным.

В документации Postgres говорится, В целях уникального ограничения нулевые значения не считаются равными.

Хорошо. Я вижу смысл этого. Это хорошая тонкость в обработке нулей: рассматривая все NULL в столбце с уникальными ограничениями как непересекающиеся, мы откладываем принудительное применение уникального ограничения до тех пор, пока не будет фактического ненулевого значения, на котором можно основать это принудительное применение.

Это довольно круто. Но здесь Postgres теряет меня. Если все NULL в столбце с уникальными ограничениями не равны, как сказано в документации, то мы должны увидеть все нули в select различном запросе.

select distinct SSN
from People;

"<NULL>"
"123"

Неа. Там только один ноль. Похоже, у Postgres это неправильно. Но мне интересно: есть ли другое объяснение?


Редактировать:

Документы Postgres указывают, что «нулевые значения считаются равными в этом сравнении». в разделе на SELECT DISTINCT . Хотя я не понимаю этого понятия, я рад, что оно изложено в документации.

Ответы [ 4 ]

22 голосов
/ 29 апреля 2011

При работе с null почти всегда ошибочно говорить:

"нули ведут себя как то-то и так-то *, поэтому они должны вести себя как такой-то здесь "

Здесь - превосходное эссе на эту тему с точки зрения postgres. Вкратце подытожим, сказав, что к нулям относятся по-разному, в зависимости от контекста, и не допускайте ошибок в предположении о них.

11 голосов
/ 30 апреля 2011

Суть в том, что PostgreSQL делает то же, что и с пустыми значениями, потому что стандарт SQL так говорит.и поэтому, когда стандарт SQL был изначально написан, авторам приходилось совершать звонки в определенных местах.Я бы сказал, что время доказало их более или менее правильно, но это не значит, что не может быть другого языка баз данных, который обрабатывает неизвестные и отсутствующие значения немного (или дико) по-разному.Но PostgreSQL реализует SQL, так что вот так.

Как уже упоминалось в другом ответе, Джефф Дэвис написал несколько хороших статей и презентаций по работе с нулями.

6 голосов
/ 29 апреля 2011

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

Представьте, что у вас есть две коробки, помеченные A и B. Если вы не открываете коробки и не видите внутри, вы никогда не знаете, что это за содержимое. Если вас спросят: «Содержимое этих двух коробок одинаковое?» Вы можете только ответить «Я не знаю».

В этом случае PostgreSQL сделает то же самое. Когда его просят сравнить два NULL с, он говорит: «Я не знаю». Это во многом связано с сумасшедшей семантикой вокруг NULL в базах данных SQL. Ссылка на статью @JackPDouglas является отличной отправной точкой для понимания поведения NULL s. Просто будьте осторожны: это зависит от поставщика.

4 голосов
/ 29 апреля 2011

Допустимы множественные значения NULL в уникальном индексе, поскольку x = NULL является ложным для всех x и, в частности, когда x само по себе равно NULL.Вы также столкнетесь с таким поведением в предложениях WHERE, где вам нужно сказать WHERE x IS NULL и WHERE x IS NOT NULL вместо WHERE x = NULL и WHERE x <> NULL.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...