Строго говоря, уникальный обнуляемый столбец (или набор столбцов) может быть NULL (или записью NULL) только один раз, поскольку наличие одного и того же значения (включая NULL) более одного раза явно нарушает ограничение уникальности.
Однако это не означает, что понятие «уникальные обнуляемые столбцы» действительно; чтобы фактически реализовать его в любой реляционной базе данных, мы просто должны помнить, что этот тип баз данных предназначен для нормальной работы, и нормализация обычно включает добавление нескольких (не связанных с сущностью) дополнительных таблиц для установления отношений между объектами. .
Давайте поработаем над базовым примером, рассматривая только один «уникальный столбец, допускающий значение NULL», его легко расширить до большего числа таких столбцов.
Предположим, что мы представляем информацию в виде таблицы:
create table the_entity_incorrect
(
id integer,
uniqnull integer null, /* we want this to be "unique and nullable" */
primary key (id)
);
Мы можем сделать это, разложив uniqnull и добавив вторую таблицу, чтобы установить связь между значениями uniqnull и the_entity (вместо того, чтобы иметь uniqnull «внутри» the_entity):
create table the_entity
(
id integer,
primary key(id)
);
create table the_relation
(
the_entity_id integer not null,
uniqnull integer not null,
unique(the_entity_id),
unique(uniqnull),
/* primary key can be both or either of the_entity_id or uniqnull */
primary key (the_entity_id, uniqnull),
foreign key (the_entity_id) references the_entity(id)
);
Чтобы связать значение uniqnull со строкой в the_entity, нам также необходимо добавить строку в the_relation.
Для строк в the_entity не было связано ни одного уникального значения (то есть для тех, которые мы поместили бы NULL в the_entity_incorrect), мы просто не добавляем строку в the_relation.
Обратите внимание, что значения для uniqnull будут уникальными для всех the_relation, а также обратите внимание, что для каждого значения в the_entity может быть не более одного значения в the_relation, поскольку первичные и внешние ключи в нем обеспечивают это.
Тогда, если значение 5 для uniqnull должно быть связано с идентификатором the_entity, равным 3, нам необходимо:
start transaction;
insert into the_entity (id) values (3);
insert into the_relation (the_entity_id, uniqnull) values (3, 5);
commit;
И, если значение идентификатора 10 для the_entity не имеет единственного аналога, мы только делаем:
start transaction;
insert into the_entity (id) values (10);
commit;
Чтобы денормализовать эту информацию и получить данные, которые будет содержать таблица, такая как the_entity_incorrect, нам нужно:
select
id, uniqnull
from
the_entity left outer join the_relation
on
the_entity.id = the_relation.the_entity_id
;
Оператор "left external join" гарантирует, что все строки из the_entity появятся в результате, поместив NULL в столбец uniqnull, когда в the_relation нет соответствующих столбцов.
Помните, что любые усилия, потраченные в течение нескольких дней (или недель, или месяцев) на разработку хорошо нормализованной базы данных (и соответствующих денормализующих представлений и процедур), сэкономят вам годы (или десятилетия) боли и потраченных ресурсов.