J'ai eu un débat en cours avec divers développeurs dans mon bureau sur le coût d'un indice, et si oui ou non l'unicité est bénéfique ou coûteuse (probablement les deux). Le nœud du problème réside dans nos ressources concurrentes.
Contexte
J'ai déjà lu une discussion qui a déclaré qu'un Unique
index n'est pas un coût supplémentaire à maintenir, car une Insert
opération vérifie implicitement où il s'inscrit dans l'arbre B et, si un doublon est trouvé dans un index non unique, ajoute un uniquificateur à la fin de la clé, mais sinon insère directement. Dans cette séquence d'événements, un Unique
index n'a aucun coût supplémentaire.
Mon collègue combat cette affirmation en disant qu'elle Unique
est appliquée en tant que deuxième opération après la recherche de la nouvelle position dans l'arbre B, et est donc plus coûteuse à maintenir qu'un index non unique.
Au pire, j'ai vu des tables avec une colonne d'identité (intrinsèquement unique) qui est la clé de clustering de la table, mais explicitement déclarée comme non unique. De l'autre côté du pire est mon obsession de l'unicité, et tous les index sont créés comme uniques, et lorsqu'il n'est pas possible de définir une relation explicitement unique à un index, j'ajoute le PK de la table à la fin de l'index pour garantir la l'unicité est garantie.
Je suis souvent impliqué dans les revues de code pour l'équipe de développement, et je dois être en mesure de donner des directives générales à suivre. Oui, chaque index doit être évalué, mais lorsque vous avez cinq serveurs avec des milliers de tables chacun et jusqu'à vingt index sur une table, vous devez être en mesure d'appliquer des règles simples pour garantir un certain niveau de qualité.
Question
L'unicité a-t-elle un coût supplémentaire sur le back-end d'un Insert
par rapport au coût de maintien d'un indice non unique? Deuxièmement, qu'y a-t-il de mal à ajouter la clé primaire d'une table à la fin d'un index pour garantir l'unicité?
Exemple de définition de table
create table #test_index
(
id int not null identity(1, 1),
dt datetime not null default(current_timestamp),
val varchar(100) not null,
is_deleted bit not null default(0),
primary key nonclustered(id desc),
unique clustered(dt desc, id desc)
);
create index
[nonunique_nonclustered_example]
on #test_index
(is_deleted)
include
(val);
create unique index
[unique_nonclustered_example]
on #test_index
(is_deleted, dt desc, id desc)
include
(val);
Exemple
Un exemple de la raison pour laquelle j'ajouterais la Unique
clé à la fin d'un index se trouve dans l'une de nos tables de faits. Il y a un Primary Key
qui est une Identity
colonne. Cependant, Clustered Index
c'est à la place la colonne du schéma de partitionnement, suivie de trois dimensions de clé étrangère sans unicité. Les performances de sélection sur ce tableau sont épouvantables, et j'obtiens souvent de meilleurs temps de recherche en utilisant le Primary Key
avec une recherche de clé plutôt que de tirer parti du Clustered Index
. Les autres tableaux qui suivent une conception similaire, mais qui sont Primary Key
annexés à la fin, ont des performances considérablement meilleures.
-- date_int is equivalent to convert(int, convert(varchar, current_timestamp, 112))
if not exists(select * from sys.partition_functions where [name] = N'pf_date_int')
create partition function
pf_date_int (int)
as range right for values
(19000101, 20180101, 20180401, 20180701, 20181001, 20190101, 20190401, 20190701);
go
if not exists(select * from sys.partition_schemes where [name] = N'ps_date_int')
create partition scheme
ps_date_int
as partition
pf_date_int all
to
([PRIMARY]);
go
if not exists(select * from sys.objects where [object_id] = OBJECT_ID(N'dbo.bad_fact_table'))
create table dbo.bad_fact_table
(
id int not null, -- Identity implemented elsewhere, and CDC populates
date_int int not null,
dt date not null,
group_id int not null,
group_entity_id int not null, -- member of group
fk_id int not null,
-- tons of other columns
primary key nonclustered(id, date_int),
index [ci_bad_fact_table] clustered (date_int, group_id, group_entity_id, fk_id)
)
on ps_date_int(date_int);
go
if not exists(select * from sys.objects where [object_id] = OBJECT_ID(N'dbo.better_fact_table'))
create table dbo.better_fact_table
(
id int not null, -- Identity implemented elsewhere, and CDC populates
date_int int not null,
dt date not null,
group_id int not null,
group_entity_id int not null, -- member of group
-- tons of other columns
primary key nonclustered(id, date_int),
index [ci_better_fact_table] clustered(date_int, group_id, group_entity_id, id)
)
on ps_date_int(date_int);
go
Case
et lesIf
structures sont limitées à 10 niveaux, il est logique qu'il y ait également une limite à la résolution d'entités non uniques. D'après votre déclaration, cela semble ne s'appliquer qu'aux cas où la clé de clustering n'est pas unique. Est-ce un problème pour unNonclustered Index
ou si la clé de clustering estUnique
alors il n'y a pas de problème pour lesNonclustered
index?