¿Debo marcar un índice compuesto como único si contiene la clave primaria?

9

Dada alguna tabla con una clave primaria, por ejemplo:

CREATE TABLE Customers (
   CustomerID int NOT NULL PRIMARY KEY,
   FirstName nvarchar(50),
   LastName nvarchar(50),
   Address nvarchar(200),
   Email nvarchar(260)
   --...
)

Tenemos una clave primaria única CustomerID.

Tradicionalmente, podría necesitar algunos índices de cobertura adicionales; por ejemplo para encontrar rápidamente un usuario por CustomerIDo Email:

CREATE INDEX IX_Customers_CustomerIDEmail ON Customers
(
   CustomerID,
   Email
)

Y estos son los tipos de índices que he creado durante décadas.

No es necesario que sea único, pero en realidad lo es

El índice en sí existe para evitar un escaneo de tabla; es un índice de cobertura para ayudar al rendimiento (el índice no existe como una restricción para imponer la unicidad).

Hoy recordé un poco de información: SQL Server puede usar el hecho de que:

  • una columna tiene una restricción de clave externa
  • una columna tiene un índice único
  • se confía en una restricción

para ayudarlo a optimizar la ejecución de su consulta. De hecho, de la Guía de diseño de índices de SQL Server :

Si los datos son únicos y desea que se aplique la unicidad, la creación de un índice único en lugar de un índice no único en la misma combinación de columnas proporciona información adicional para el optimizador de consultas que puede producir planes de ejecución más eficientes . En este caso, se recomienda crear un índice único (preferiblemente creando una restricción ÚNICA).

Dado que mi índice de varias columnas contiene la clave principal, este índice compuesto será de hecho único. No es una restricción que particularmente necesito que SQL Server aplique durante cada inserción o actualización; pero el hecho es que este índice no agrupado es único.

¿Hay alguna ventaja en marcar este índice único de facto como realmente único?

CREAR ÍNDICE ÚNICO IX_Clientes_CustomerIDEmail EN Clientes
(
   Identificación del cliente,
   Correo electrónico
)

Me parece que SQL Server podría ser lo suficientemente inteligente como para darse cuenta de que mi índice ya es único en virtud del hecho de que contiene la clave primaria.

  • Pero tal vez no lo sepa, y hay una ventaja para el optimizador si declaro el índice como único de todos modos.
  • Excepto que tal vez eso podría conducir a una desaceleración durante las inserciones y actualizaciones, donde debe realizar comprobaciones de unicidad, donde antes nunca antes había tenido que hacerlo.
  • A menos que sepa, se garantiza que el índice ya sea único, porque contiene la clave primaria.

No puedo encontrar ninguna guía de Microsoft sobre qué hacer cuando un índice compuesto contiene la clave primaria.

Los beneficios de los índices únicos incluyen los siguientes:

  • Se garantiza la integridad de los datos de las columnas definidas.
  • Se proporciona información adicional útil para el optimizador de consultas.

¿Debo marcar un índice compuesto como único si ya contiene la clave primaria? ¿O puede SQL Server resolver esto por sí mismo?

Ian Boyd
fuente
44
Para encontrar rápidamente un cliente por CustomerID, la PK probablemente sea suficiente. Para encontrar un cliente por correo electrónico, prefiero tener el segundo índice solo en el correo electrónico (ya cubre la clave de agrupación). De todos modos, sé que esto es ficticio, pero sí, lo marcaría como único si sabe que tiene que ser único, esa restricción probablemente no pueda dañar las inserciones lo suficiente como para compensar los casos en los que puede ayudar al optimizador. Pero, todo eso depende de su carga de trabajo, sobre la que tendríamos que especular ...
Aaron Bertrand
1
Lo discutimos extensamente aquí. Y aunque no podemos pensar en ninguna forma de obtener datos para demostrarlo de una forma u otra, asumimos que los chicos de SQL Server son probablemente muy inteligentes, y el optimizador probablemente ya sea capaz de realizar esta meta optimización. También creemos que probablemente sea mejor que el índice comunique la intención , que es simplemente un índice, y no imponga una singularidad como regla de negocio cuando de otro modo solo se haría para tratar de adivinar las capacidades de SQL Server.
Ian Boyd

Respuestas:

11

¿Debo marcar un índice compuesto como único si ya contiene la clave primaria?

Probablemente no. El optimizador generalmente puede usar información sobre la unicidad de la columna de clave contenida de todos modos, por lo que no hay una ventaja real.

También hay una consecuencia importante de marcar un índice único en los planes de actualización que modifican las claves de ese índice para tener en cuenta:

Preparar

CREATE TABLE dbo.Customers 
(
   CustomerID int NOT NULL PRIMARY KEY,
   FirstName nvarchar(50),
   LastName nvarchar(50),
   [Address] nvarchar(200),
   Email nvarchar(260)
);

CREATE NONCLUSTERED INDEX 
    IX_Customers_CustomerIDEmail 
ON dbo.Customers
(
   CustomerID,
   Email
);

-- Pretend we have some rows
UPDATE STATISTICS dbo.Customers 
WITH ROWCOUNT = 100000, PAGECOUNT = 20000;

Plan de actualización por índice (índice no exclusivo)

UPDATE dbo.Customers 
SET Email = N'New', [Address] = 'New Address'
WHERE Email = N'Old' 
OPTION (QUERYTRACEON 8790); -- Per-index update plan

Plan de ejecución:

Split y filtro

El optimizador a menudo toma una decisión basada en el costo entre la actualización de índices no agrupados por fila (un plan "estrecho") o por índice (un plan "amplio"). La estrategia predeterminada (excepto las tablas OLTP en memoria) es un plan amplio.

Los planes estrechos (donde los índices no agrupados se mantienen al mismo tiempo que el índice de montón / agrupado) son una optimización del rendimiento para pequeñas actualizaciones. Esta optimización no se implementa para todos los casos: el uso de ciertas funciones (como vistas indexadas) significa que los índices asociados se mantendrán en un plan amplio.

Más información: Optimización de consultas T-SQL que cambian datos

En este caso, he usado el indicador de seguimiento no documentado 8790 para forzar un amplio plan de actualización: por lo tanto, el plan muestra que los índices agrupados y no agrupados se mantienen por separado.

El Split convierte cada actualización en un par separado para eliminar e insertar; el filtro filtra cualquier fila que no resulte en un cambio en el índice.

Más información: ( Actualizaciones no actualizadas ) por el equipo QO de SQL Server.

Plan de actualización por índice (índice único)

-- Same index, but unique
CREATE UNIQUE INDEX IX_Customers_CustomerIDEmail ON Customers
(
   CustomerID,
   Email
)
WITH (DROP_EXISTING = ON);

UPDATE dbo.Customers 
SET Email = N'New', [Address] = 'New Address'
WHERE Email = N'Old' 
OPTION (QUERYTRACEON 8790); -- Per-index update plan

Plan de ejecución:

Split-Sort-Collapse

Observe los operadores adicionales Ordenar y Contraer cuando el índice se marca como único.

Este patrón Split-Sort-Collapse es necesario al actualizar las claves de un índice único, para evitar violaciones de clave únicas intermedias.

Más información: Mantenimiento de índices únicos por Craig Freedman

La clasificación en particular puede ser un problema. No solo es un costo adicional innecesario, sino que puede derramarse en el disco si las estimaciones son inexactas.

Acerca de las claves no agrupadas

Otro factor a tener en cuenta es que las estructuras de índice no agrupadas son siempre únicas, en todos los niveles del índice, incluso si UNIQUEno se especifica. La (s) clave (s) de agrupamiento, y posiblemente un unicificador si el índice agrupado no está marcado como único, se agregan a un índice no agrupado no único en todos los niveles.

Como consecuencia, el siguiente índice de definición:

CREATE INDEX IX_Customers_CustomerIDEmail ON Customers
(
   Email
)
WITH (DROP_EXISTING = ON);

... en realidad contiene las claves (Email, CustomerID) en todos los niveles. Por lo tanto, es 'buscable' en ambas columnas:

SELECT * 
FROM dbo.Customers AS C WITH (INDEX(IX_Customers_CustomerIDEmail))
WHERE C.Email = N'Email'
AND C.CustomerID = 1;

Busca

Más información: Más información sobre las claves de índice no agrupadas por Kalen Delaney

Paul White 9
fuente
8

SQL ya sabe que es único (si incluye el PK, no puede ser más único), independientemente de si lo dice explícitamente.

La gran diferencia entre un índice no único y un índice único es que los índices no únicos requieren la clave de índice agrupado (con valor de uniquifier si el CIX no se declara como único) en los niveles más altos del índice, no solo en la hoja nivel.

En su caso, ya tiene el CIX en la clave, lo que significa que ya estará en todos los niveles del índice.

Pero podría crear una tabla que tenga un PK separado (único) y CIX (no importa). Luego cree un índice no único que incluya el PK en su clave. Coloque algunas filas en la tabla, incluidos algunos valores varchar fáciles de encontrar para su columna CIX. Coloque suficientes filas para causar múltiples niveles de sus índices. Luego puede usar DBCC IND para encontrar las páginas en su NCIX, y DBCC PAGE para abrir algunas para ver los datos, para ver si los valores clave de CIX están en los niveles más altos.

Rob Farley
fuente