Ho avuto un dibattito in corso con vari sviluppatori nel mio ufficio sul costo di un indice e se l'unicità è vantaggiosa o costosa (probabilmente entrambi). Il nocciolo del problema sono le nostre risorse concorrenti.
sfondo
Ho già letto una discussione in cui si afferma che un Unique
indice non è un costo aggiuntivo da mantenere, poiché Insert
un'operazione controlla implicitamente dove si adatta all'albero B e, se si trova un duplicato in un indice non univoco, aggiunge un unificatore a la fine della chiave, ma altrimenti inserisce direttamente. In questa sequenza di eventi, un Unique
indice non ha costi aggiuntivi.
Il mio collega combatte questa affermazione dicendo che Unique
viene applicato come una seconda operazione dopo la ricerca della nuova posizione nell'albero B, e quindi è più costoso da mantenere rispetto a un indice non univoco.
Nel peggiore dei casi, ho visto tabelle con una colonna identità (intrinsecamente univoca) che è la chiave di clustering della tabella, ma esplicitamente dichiarata come non univoca. Dall'altro lato del peggio c'è la mia ossessione per l'unicità, e tutti gli indici sono creati come unici, e quando non è possibile definire una relazione esplicitamente unica con un indice, aggiungo il PK della tabella alla fine dell'indice per garantire che l'unicità è garantita.
Sono spesso coinvolto nelle revisioni del codice per il team di sviluppo e devo essere in grado di fornire linee guida generali che dovranno seguire. Sì, ogni indice dovrebbe essere valutato, ma quando si hanno cinque server con migliaia di tabelle ciascuno e fino a venti indici su una tabella, è necessario essere in grado di applicare alcune semplici regole per garantire un certo livello di qualità.
Domanda
L'unicità ha un costo aggiuntivo sul back-end di un Insert
confronto rispetto al costo di mantenimento di un indice non univoco? In secondo luogo, cosa c'è di sbagliato nell'aggiungere la chiave primaria di una tabella alla fine di un indice per garantire l'univocità?
Definizione della tabella di esempio
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);
Esempio
Un esempio del perché aggiungerei la Unique
chiave alla fine di un indice è in una delle nostre tabelle dei fatti. C'è un Primary Key
che è una Identity
colonna. Tuttavia, Clustered Index
è invece la colonna dello schema di partizionamento, seguita da tre dimensioni di chiave esterna senza unicità. Selezionare le prestazioni su questa tabella è spaventoso e spesso ottengo tempi di ricerca migliori usando il Primary Key
tasto con una ricerca chiave invece di sfruttare il Clustered Index
. Altre tabelle che seguono un design simile, ma che sono state Primary Key
aggiunte alla fine, hanno prestazioni notevolmente migliori.
-- 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
e leIf
strutture sono limitate a 10 livelli, ha senso che esiste anche un limite alla risoluzione di entità non uniche. Secondo la tua affermazione, sembra che si applichi solo ai casi in cui la chiave di clustering non è univoca. È un problema per unNonclustered Index
o se la chiave di clustering èUnique
allora non c'è un problema per gliNonclustered
indici?