2012-08-01 9 views
13

Tengo una tabla que contiene, por ejemplo, dos campos que quiero que sean únicos dentro de la base de datos. Por ejemplo:Cómo creo una restricción única de columna múltiple en SQL Server

create table Subscriber (
    ID int not null, 
    DataSetId int not null, 
    Email nvarchar(100) not null, 
    ... 
) 

La columna de ID es la clave principal y tanto DataSetId como Email están indexados.

Lo que quiero poder hacer es evitar que aparezca la misma combinación de Email y DataSetId en la tabla o, para decirlo de otra manera, el valor de Email debe ser exclusivo para un DataSetId determinado.

intenté crear un índice único en las columnas

CREATE UNIQUE NONCLUSTERED INDEX IX_Subscriber_Email 
ON Subscriber (DataSetId, Email) 

pero he encontrado que esto tuvo un impacto muy significativo en el tiempo de búsqueda (en la búsqueda de una dirección de correo electrónico, por ejemplo - hay 1,5 millones de filas de la mesa).

¿Existe alguna manera más eficiente de lograr este tipo de restricciones?

+0

¿Está diciendo que la búsqueda SIN el índice es considerablemente más rápida que CON el índice? Esto es nuevo para mí, hasta donde yo sé, los índices siempre se crearon para acelerar las búsquedas, NO para ralentizarlas. –

+0

No, eso es todo, pero no debería tener ** ningún ** impacto significativo en sus tiempos de búsqueda ?! ¿De cuánto impacto estamos hablando? ¿Puedes mostrar el plan de ejecución? ¿Has actualizado tus estadísticas? –

+0

Una búsqueda en una dirección de correo electrónico con índices 'simples' en Email y DataSetId tomó alrededor de 1 segundo. Al agregar el índice compuesto adicional, esto aumentó a alrededor de 9 segundos. – Neilski

Respuesta

25

pero he encontrado que esto tuvo un impacto bastante significativo en los tiempos de búsqueda (en la búsqueda de una dirección de correo electrónico, por ejemplo,

El índice ha definido en (DataSetId, Email) no se puede utilizar para búsquedas basadas en correo electrónico. Si creara un índice con el campo Email en la posición más a la izquierda, podría usarse:

CREATE UNIQUE NONCLUSTERED INDEX IX_Subscriber_Email 
    ON Subscriber (Email, DataSetId); 

Este índice servidor tanto como una aplicación de la restricción única y como un medio para buscar rápidamente un correo electrónico. Sin embargo, este índice no se puede usar para buscar rápidamente un DataSetId específico.

La esencia de esto es que siempre que defina un índice multikey, se puede usar solo para búsquedas en el orden de las teclas. Un índice en (A, B, C) se puede utilizar para buscar valores en la columna A, para buscar valores en ambos A y B o para buscar valores en las tres columnas A, B y C. Sin embargo, no se puede usar para buscar valores en B o en C solo.

+0

Todo es cierto, pero esto no explica cómo agregar un índice afecta negativamente a los tiempos de búsqueda * (significativamente) * –

+0

@Lieven: sospecho que el OP hizo algo más que crear una nueva restricción. P.ej. se cayó un índice existente en 'Correo electrónico' –

+1

Muy probablemente, pero para parafrasear [Elmer Fud] (http://en.wikipedia.org/wiki/Elmer_Fudd), OP es * vewy vewy tranquilo *

-1

Supongo que la única manera de ingresar datos en esa tabla es a través de SP. Si ese es el caso, puede implementar cierta lógica en su inserción y actualización de SP para encontrar si los valores que va a insertar/actualizar ya existen en esa mesa o no

Algo como esto

create proc spInsert 
(
    @DataSetId int, 
    @Email nvarchar(100) 
) 
as 
begin 

if exists (select * from tabaleName where DataSetId = @DataSetId and Email = @Email) 
    select -1 -- Duplicacy flag 
else 
begin 
    -- insert logic here 
    select 1 -- success flag 
end 

end 
GO 


create proc spUpdate 
(
    @ID int, 
    @DataSetId int, 
    @Email nvarchar(100) 
) 
as 
begin 

if exists 
(select * from tabaleName where DataSetId = @DataSetId and Email = @Email and ID <> @ID) 
    select -1 -- Duplicacy flag 
else 
begin 
    -- insert logic here 
    select 1 -- success flag 
end 

end 
GO 
+0

-1 para recomendar una solución de procedimiento en lugar de una aplicación de restricción. Además, el procedimiento no funcionaría en concurrencia. –

+0

@RemusRusanu 'el procedimiento no funcionaría en concurrencia' ¿qué quiere decir con eso? – yogi

+1

transacciones múltiples pueden verificar la condición * concurrentemente * y concluir que no hay duplicados. Todos los hilos proceden a insertar, creando duplicados. Es un problema bien conocido con el patrón de codificación 'check and insert': http://sqlblog.com/blogs/alexander_kuznetsov/archive/2010/01/12/t-sql-tuesday-002-patterns-that-do-not -work-as-expected.aspx –

Cuestiones relacionadas