2008-10-10 25 views
88

Estoy usando SQL Server 2005. Quiero restringir los valores en una columna para que sean únicos, mientras que permite NULLS.¿Cómo crear un índice único en una columna NULL?

Mi solución actual consiste en un índice único en una vista de este modo:

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) 

mejor idea?

+14

¿No hay posibilidad de utilizar sql 2008? puede crear un índice filtrado usando 'where' –

+3

No quiso decir _unique, permitiendo NULLs_, parece que se ha referido a _unique, pero incluye varios NULLs_. De lo contrario, NULL se indexa como cualquier otro valor y la restricción de exclusividad funciona como se esperaba, simplemente no según los estándares de SQL, como @pst se menciona en un comentario a continuación. – Suncat2000

Respuesta

26

Bastante seguro de que no puede hacer eso, ya que viola el propósito de los únicos.

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

+2

Parece que el contenido del enlace que proporcionó fue copiado (parcialmente) sin atribución desde aquí: http://decipherinfosys.wordpress.com/2007/11/30/multiple-null-values-in-a-unique-index -in-sql-serverdb2-luw/ –

+71

No estoy de acuerdo con que "viole el propósito de los únicos": NULL tiene un valor especial en SQL (similar en muchos aspectos a NaN) y debe tratarse en consecuencia. En realidad, es una falla en SQL Server para cumplir con varias especificaciones SQL: aquí hay un enlace para una solicitud de la "implementación correcta" de lo que vale: http://connect.microsoft.com/SQLServer/feedback/details/299229/change-unique-constraint-to-allow-multiple-null-values. –

+2

como referencia en 2008 puede hacer CREATE UNIQUE INDEX foo EN dbo.bar (clave) DONDE la clave NO ES NULA; – niico

68

El truco columna calculada es ampliamente conocido como un "nullbuster"; mi crédito notas 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) 
) 
+0

Esto parece un buen truco. Extrañamente buscar nullbuster no trae demasiadas cosas. Me pregunto si esto también será útil para acelerar las búsquedas, en lugar de una columna calculada de solo 1 y 0 para null o no, si usa PK le da al índice algo más con lo que trabajar. Voy a probar este fin de semana en una gran mesa y lo veo. –

+0

@DavidStorfer, no puedes hacer eso porque podrías tener una colisión entre los ID de las dos tablas diferentes. – user393274

+0

Mejora: ISNULL (X, CONVERTIR (VARCHAR (10), pk)) – Faiz

97

Uso de 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 es fácil de perder)

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

+78

'crear índice exclusivo UIX en MyTable (Columna1) donde Column1 no es nulo' –

+1

Nota: actualmente SQL Server Management Studio no parece saber cómo crear dichos índices, por lo que si modifica la tabla posteriormente se confundirá e intentará para soltarlo, así que recuerda recrearlo –

+1

Parece que Microsoft ha actualizado SSMS para admitir esto. Tengo SSMS 10.50.1617 y en el cuadro de diálogo Propiedades del índice puede seleccionar la página Filtro para editar el filtro. p.ej. "([Column1] IS NOT NULL)" –

0

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

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

Vamos a trabajar con un ejemplo básico teniendo en cuenta solo una "columna con nulos única", es fácil expandirla a más de tales columnas.

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

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

Podemos hacerlo poniendo uniqnull aparte y la adición de una segunda tabla para establecer una relación entre los valores 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 de the_entity tenemos que añadir también una fila en the_relation.

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

Tenga en cuenta que los valores para uniqnull serán únicos para toda la relación y también para cada valor en la entidad no puede haber como máximo un valor en la relación, ya que las claves primaria y externa lo obligan.

Entonces, si un valor de 5 para uniqnull ha de asociarse con un id the_entity de 3, tenemos que:

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 de ID de 10 para the_entity no tiene contrapartida uniqnull, sólo hacemos:

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

para desnormalizar esta información y obtener los datos de una tabla como the_entity_incorrect llevaría a cabo, tenemos que:

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 haya columnas coincidentes en the_relation.

Recuerde, cualquier esfuerzo realizado 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 de desnormalización) le ahorrará años (o décadas) de dolor y recursos desperdiciados.

+2

Como ya se indicó en el comentario de la respuesta aceptada con cincuenta votaciones ascendentes, MS Sql Server debe admitir que tenga nulo múltiple en una columna indexada como única. Es una falla implementar estándares de SQL para no permitirlo. Null no es un valor, null no es igual a null, es una regla SQL básica desde hace años. Entonces, su primera oración es incorrecta y la mayoría de los lectores no se molestarán en seguir leyendo. –

Cuestiones relacionadas