¿Cómo crear un índice único en una columna NULA?


Estoy usando SQL Server 2005. Quiero restringir los valores en una columna para que sean únicos, mientras que permite VALORES NULOS.

Mi solución actual implica un índice único en una vista como esta:

CREATE VIEW vw_unq WITH SCHEMABINDING AS
    SELECT Column1
      FROM MyTable
     WHERE Column1 IS NOT NULL

CREATE UNIQUE CLUSTERED INDEX unq_idx ON vw_unq (Column1)

¿Alguna idea mejor?

Author: p.campbell, 2008-10-10

4 answers

Estoy bastante seguro de que no puedes hacer eso, ya que viola el propósito de uniques.

Sin embargo, esta persona parece tener un trabajo decente alrededor: http://sqlservercodebook.blogspot.com/2008/04/multiple-null-values-in-unique-index-in.html

 25
Author: willasaywhat,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2008-10-10 14:17:17

Usando SQL Server 2008, puede crear un índice filtrado: http://msdn.microsoft.com/en-us/library/cc280372.aspx. (Veo que Simon agregó esto como un comentario, pero pensó que merecía su propia respuesta ya que el comentario se pierde fácilmente)

Otra opción es un disparador para comprobar la unicidad, pero esto podría afectar el rendimiento.

 97
Author: Phil Haselden,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-07-07 02:50:14

El truco de la columna calculada es ampliamente conocido como un "nullbuster"; mis notas acreditan a Steve Kass:

CREATE TABLE dupNulls (
pk int identity(1,1) primary key,
X  int NULL,
nullbuster as (case when X is null then pk else 0 end),
CONSTRAINT dupNulls_uqX UNIQUE (X,nullbuster)
)
 68
Author: onedaywhen,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2008-10-10 14:56:44

Estrictamente hablando, una columna única nullable (o conjunto de columnas) puede ser NULL (o un registro de NULLs) solo una vez, ya que tener el mismo valor (y esto incluye NULL) más de una vez obviamente viola la restricción única.

Sin embargo, eso no significa que el concepto de "columnas nullables únicas" sea válido; para implementarlo en cualquier base de datos relacional solo tenemos que tener en cuenta que este tipo de bases de datos están destinadas a ser normalizadas para funcionar correctamente, y la normalización generalmente implica la adición de varias tablas adicionales (no de entidad) para establecer relaciones entre las entidades.

Vamos a trabajar un ejemplo básico considerando solo una "columna nullable única", es fácil expandirla a más columnas de este tipo.

Supongamos que la información representada por una tabla como esta:

create table the_entity_incorrect
(
  id integer,
  uniqnull integer null, /* we want this to be "unique and nullable" */
  primary key (id)
);

Podemos hacerlo separando uniqnull y agregando una segunda tabla para establecer una relación entre los valores de uniqnull y the_entity (en lugar de tener uniqnull " dentro" 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)
);

Para asociar un valor de uniqnull a una fila en the_entity necesitamos también agregar una fila en the_relation.

Para las filas en the_entity donde no se asocian valores uniqnull (es decir, para las que pondríamos NULL en the_entity_incorrect) simplemente no agregamos una fila en the_relation.

Tenga en cuenta que los valores de uniqnull serán únicos para toda la relación_, y también tenga en cuenta que para cada valor en la_entidad puede haber como máximo un valor en la_relación, ya que la las claves primarias y foráneas en él hacen cumplir esto.

Entonces, si un valor de 5 para uniqnull se asocia con un id de the_entity de 3, necesitamos:

start transaction;
insert into the_entity (id) values (3); 
insert into the_relation (the_entity_id, uniqnull) values (3, 5);
commit;

Y, si un valor id de 10 para the_entity no tiene contraparte uniqnull, solo hacemos:

start transaction;
insert into the_entity (id) values (10); 
commit;

Para desnormalizar esta información y obtener los datos que tendría una tabla como the_entity_incorrect, necesitamos:

select
  id, uniqnull
from
  the_entity left outer join the_relation
on
  the_entity.id = the_relation.the_entity_id
;

El operador "left outer join" asegura que todas las filas de the_entity aparecerán en el resultado, poniendo NULL en la columna uniqnull cuando no hay columnas coincidentes presentes en la_relación.

Recuerde, cualquier esfuerzo invertido durante algunos días (o semanas o meses) en el diseño de una base de datos bien normalizada (y las correspondientes vistas y procedimientos desnormalizantes) le ahorrará años (o décadas) de dolor y recursos desperdiciados.

 -2
Author: roy,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-02-24 05:56:46