Estritamente falando, uma coluna anulável exclusiva (ou conjunto de colunas) pode ser NULL (ou um registro de NULLs) apenas uma vez, visto que ter o mesmo valor (e isso inclui NULL) mais de uma obviamente viola a restrição exclusiva.
No entanto, isso não significa que o conceito de "colunas anuláveis exclusivas" seja válido; para realmente implementá-lo em qualquer banco de dados relacional, só temos que ter em mente que este tipo de banco de dados deve ser normalizado para funcionar corretamente, e a normalização geralmente envolve a adição de várias tabelas extras (não-entidades) para estabelecer relacionamentos entre as entidades .
Vamos trabalhar um exemplo básico considerando apenas uma "coluna anulável única", é fácil expandi-la para mais colunas.
Suponha que as informações representadas por uma tabela como esta:
create table the_entity_incorrect
(
id integer,
uniqnull integer null, /* we want this to be "unique and nullable" */
primary key (id)
);
Podemos fazer isso separando uniqnull e adicionando uma segunda tabela para estabelecer uma relação entre valores uniqnull e the_entity (em vez de ter uniqnull "dentro" da_entidade):
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)
);
Para associar um valor de uniqnull a uma linha na entidade, precisamos também adicionar uma linha na relação.
Para linhas em the_entity onde nenhum valor uniqnull está associado (ou seja, para aqueles que colocaríamos NULL em the_entity_incorrect), simplesmente não adicionamos uma linha em the_relation.
Observe que os valores para uniqnull serão únicos para toda a relação, e também observe que para cada valor na entidade pode haver no máximo um valor na relação, uma vez que as chaves primária e externa obrigam isso.
Então, se um valor de 5 para uniqnull deve ser associado a um id the_entity de 3, precisamos:
start transaction;
insert into the_entity (id) values (3);
insert into the_relation (the_entity_id, uniqnull) values (3, 5);
commit;
E, se um valor de id de 10 para the_entity não tiver uma contraparte única, fazemos apenas:
start transaction;
insert into the_entity (id) values (10);
commit;
Para desnormalizar essas informações e obter os dados que uma tabela como the_entity_incorrect manteria, precisamos:
select
id, uniqnull
from
the_entity left outer join the_relation
on
the_entity.id = the_relation.the_entity_id
;
O operador "left outer join" garante que todas as linhas de the_entity apareçam no resultado, colocando NULL na coluna uniqnull quando nenhuma coluna correspondente estiver presente na relação.
Lembre-se de que qualquer esforço despendido por alguns dias (ou semanas ou meses) no projeto de um banco de dados bem normalizado (e as visualizações e procedimentos desnormalizantes correspondentes) poupará anos (ou décadas) de dor e desperdício de recursos.