严格来说,一个唯一的可空列(或一组列)只能为 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 中的行没有关联 uniqnull 值(即,对于那些我们将在 the_entity_incorrect 中放入 NULL 的行),我们只是不在 the_relation 中添加一行。
请注意,uniqnull 的值对于所有 the_relation 都是唯一的,并且还请注意,对于 the_entity 中的每个值,the_relation 中最多可以有一个值,因为其上的主键和外键强制执行此操作。
然后,如果 uniqnull 的值 5 与 3 的 the_entity id 相关联,我们需要:
start transaction;
insert into the_entity (id) values (3);
insert into the_relation (the_entity_id, uniqnull) values (3, 5);
commit;
而且,如果 the_entity 的 id 值为 10 没有 uniqnull 对应项,我们只做:
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
;
“左外连接”运算符确保 the_entity 中的所有行都出现在结果中,当 the_relation 中不存在匹配列时,将 NULL 放在 uniqnull 列中。
请记住,花几天(或几周或几个月)设计一个良好规范化的数据库(以及相应的非规范化视图和过程)的任何努力都将为您节省数年(或数十年)的痛苦和浪费的资源。