Dois-je marquer un index composite comme unique s'il contient la clé primaire?

9

Étant donné une table avec une clé primaire, par exemple:

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

nous avons une clé primaire unique CustomerID.

Traditionnellement, je pourrais alors avoir besoin d'indices de couverture supplémentaires; par exemple pour trouver rapidement un utilisateur soit CustomerIDou Email:

CREATE INDEX IX_Customers_CustomerIDEmail ON Customers
(
   CustomerID,
   Email
)

Et ce sont les types d'index que j'ai créés pendant des décennies.

Il n'est pas nécessaire d'être unique, mais c'est en fait

L'index lui-même existe pour éviter une analyse de table; il s'agit d'un indice de couverture afin de favoriser les performances (l'indice n'est pas là comme une contrainte pour imposer l'unicité).

Aujourd'hui, je me suis souvenu d'une petite quantité d'informations - SQL Server peut utiliser le fait que:

  • une colonne a une contrainte de clé étrangère
  • une colonne a un index unique
  • une contrainte est fiable

afin de l'aider à optimiser l'exécution de ses requêtes. En fait, à partir du Guide de conception d'index SQL Server :

Si les données sont uniques et que vous souhaitez que l'unicité soit appliquée, la création d'un index unique au lieu d'un index non unique sur la même combinaison de colonnes fournit des informations supplémentaires pour l'optimiseur de requête qui peut produire des plans d'exécution plus efficaces . La création d'un index unique (de préférence en créant une contrainte UNIQUE) est recommandée dans ce cas.

Étant donné que mon index multi-colonnes contient la clé primaire, cet index composite sera de facto unique. Ce n'est pas une contrainte que j'ai particulièrement besoin que SQL Server applique à chaque insertion ou mise à jour; mais le fait est que cet index non cluster est unique.

Y a-t-il un avantage à marquer cet index unique de facto comme réellement unique?

CRÉER UN INDEX UNIQUE IX_Customers_CustomerIDEmail ON Clients
(
   N ° de client,
   Email
)

Il me semble que SQL Server pourrait être assez intelligent pour réaliser que mon index est déjà unique du fait qu'il contient la clé primaire.

  • Mais peut-être qu'il ne le sait pas, et il y a un avantage pour l'optimiseur si je déclare l'index comme unique de toute façon.
  • Sauf peut-être que cela pourrait maintenant entraîner des ralentissements lors des insertions et des mises à jour, où il doit effectuer des vérifications d'unicité - là où auparavant il n'avait jamais dû le faire auparavant.
  • À moins qu'il ne sache, l'index est garanti d'être déjà unique, car il contient la clé primaire.

Je ne trouve aucune indication de Microsoft sur la procédure à suivre lorsqu'un index composite contient la clé primaire.

Les avantages des index uniques sont les suivants:

  • L'intégrité des données des colonnes définies est assurée.
  • Des informations supplémentaires utiles à l'optimiseur de requêtes sont fournies.

Dois-je marquer un index composite comme unique s'il contient déjà la clé primaire? Ou SQL Server peut-il comprendre cela par lui-même?

Ian Boyd
la source
4
Pour trouver rapidement un client par CustomerID, le PK est probablement suffisant. Pour trouver un client par e-mail, je préfère avoir le deuxième index uniquement par e-mail (il couvre déjà la clé de clustering). Quoi qu'il en soit, je sais que c'est fictif, mais oui, je le marquerais comme unique si vous savez qu'il doit être unique, cette contrainte ne peut probablement pas blesser suffisamment les insertions pour compenser les cas où cela peut aider l'optimiseur. Mais, tout dépend de votre charge de travail, sur laquelle nous devrons spéculer ...
Aaron Bertrand
1
Nous en avons longuement discuté ici. Et bien que nous ne puissions penser à aucun moyen de fournir des données pour le prouver d'une manière ou d'une autre, nous supposons que les gars de SQL Server sont probablement très intelligents, et l'optimiseur est probablement déjà en mesure de faire cette méta-optimisation. Nous pensons également qu'il est probablement préférable que l'index communique l' intention - qui est simplement un index, et ne pas imposer unicité en tant que règle métier alors qu'il ne serait autrement fait que pour essayer de deviner les capacités de SQL Server.
Ian Boyd du

Réponses:

11

Dois-je marquer un index composite comme unique s'il contient déjà la clé primaire?

Probablement pas. L'optimiseur peut généralement utiliser des informations sur l'unicité de la colonne de clé contenue, il n'y a donc aucun avantage réel.

Il y a aussi une conséquence importante du marquage d'un index unique sur les plans de mise à jour qui modifient les clés de cet index à prendre en compte:

Installer

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 mise à jour par index (index non unique)

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

Plan d'exécution:

Fractionner et filtrer

L'optimiseur prend souvent une décision basée sur les coûts entre la mise à jour des index non clusterisés par ligne (un plan «étroit») ou par index (un plan «large»). La stratégie par défaut (à l'exception des tables OLTP en mémoire) est un plan étendu.

Les plans étroits (où les index non cluster sont maintenus en même temps que l'index de segment de mémoire / cluster) sont une optimisation des performances pour les petites mises à jour. Cette optimisation n'est pas implémentée dans tous les cas - l'utilisation de certaines fonctionnalités (comme les vues indexées) signifie que les index associés seront maintenus dans un plan étendu.

Pour plus d'informations: Optimisation des requêtes T-SQL qui modifient les données

Dans ce cas, j'ai utilisé l'indicateur de trace non documenté 8790 pour forcer un plan de mise à jour étendu: le plan montre donc les index cluster et non cluster maintenus séparément.

Le partage transforme chaque mise à jour en une paire distincte de suppression et d'insertion; le filtre filtre toutes les lignes qui n'entraîneraient pas de modification de l'index.

Plus d'informations: ( mises à jour non mises à jour ) par l'équipe SQL QO.

Plan de mise à jour par index (index unique)

-- 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 d'exécution:

Split-Sort-Collapse

Notez les opérateurs de tri et de réduction supplémentaires lorsque l'index est marqué comme unique.

Ce modèle Split-Sort-Collapse est requis lors de la mise à jour des clés d'un index unique, pour éviter les violations de clés uniques intermédiaires.

Pour plus d'informations: Maintenir des index uniques par Craig Freedman

Le tri en particulier peut être un problème. Non seulement il s'agit d'un coût supplémentaire inutile, mais il peut se répandre sur le disque si les estimations sont inexactes.

À propos des clés non clusterisées

Un autre facteur à considérer est que les structures d'index non cluster sont toujours uniques, à tous les niveaux de l'index, même si elles UNIQUEne sont pas spécifiées. La ou les clés de clustering - et éventuellement un uniquificateur si l'index clusterisé n'est pas marqué comme unique - sont ajoutées à un index non cluster non unique à tous les niveaux.

En conséquence, la définition de l'indice suivante:

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

... contient en fait les clés (Email, CustomerID) à tous les niveaux. Il est donc «recherchable» sur les deux colonnes:

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

Cherche

Plus d'informations: Plus d'informations sur les clés d'index non cluster par Kalen Delaney

Paul White 9
la source
8

SQL sait qu'il est déjà unique (s'il inclut le PK, il ne peut plus l'être), que vous le disiez explicitement ou non.

La grande différence entre un index non unique et un index unique est que les index non uniques nécessitent la clé d'index cluster (avec une valeur uniquifier si le CIX n'est pas déclaré comme unique) aux niveaux supérieurs de l'index, pas seulement à la feuille niveau.

Dans votre cas, vous avez déjà le CIX dans la clé, ce qui signifie qu'il sera déjà à tous les niveaux de l'index.

Mais vous pouvez créer une table qui a un PK distinct (unique) et CIX (peu importe). Créez ensuite un index non unique qui inclut le PK dans sa clé. Mettez quelques lignes dans le tableau, y compris des valeurs varchar facilement trouvables pour votre colonne CIX. Mettez suffisamment de lignes pour provoquer plusieurs niveaux de vos index. Ensuite, vous pouvez utiliser DBCC IND pour trouver les pages dans votre NCIX et DBCC PAGE pour en ouvrir pour consulter les données, pour voir si les valeurs de clé CIX sont aux niveaux supérieurs.

Rob Farley
la source