严格来说,一个唯一的可为空的列(或一组列)只能为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值与the_entity id 3关联,则需要:
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列中。
请记住,花几天(或几周或几个月)来设计标准化良好的数据库(以及相应的非标准化视图和过程),将为您节省数年(或数十年)的痛苦和资源浪费。