Eu tenho um debate em andamento com vários desenvolvedores em meu escritório sobre o custo de um índice e se a exclusividade é ou não benéfica ou cara (provavelmente as duas). O cerne da questão são nossos recursos concorrentes.
fundo
Eu li anteriormente uma discussão que afirmava que um Uniqueíndice não tem custo adicional de manutenção, uma vez que uma Insertoperação verifica implicitamente onde ele se encaixa na árvore B e, se uma duplicata for encontrada em um índice não exclusivo, anexa um unificador final da chave, mas, caso contrário, é inserido diretamente. Nesta sequência de eventos, um Uniqueíndice não tem custo adicional.
Meu colega de trabalho combate essa afirmação dizendo que Uniqueé imposta como uma segunda operação após a busca pela nova posição na árvore B e, portanto, é mais cara de manter do que um índice não exclusivo.
Na pior das hipóteses, vi tabelas com uma coluna de identidade (inerentemente exclusiva) que é a chave de cluster da tabela, mas declarada explicitamente como não exclusiva. Do outro lado do pior, está minha obsessão pela exclusividade, e todos os índices são criados como únicos e, quando não é possível definir uma relação explicitamente exclusiva com um índice, anexo a PK da tabela ao final do índice para garantir que o a exclusividade é garantida.
Estou freqüentemente envolvido em revisões de código para a equipe de desenvolvimento e preciso fornecer diretrizes gerais para que eles sigam. Sim, todos os índices devem ser avaliados, mas quando você tem cinco servidores com milhares de tabelas cada e até vinte índices em uma tabela, precisa aplicar algumas regras simples para garantir um certo nível de qualidade.
Questão
A exclusividade tem um custo adicional no final de uma Insertcomparação com o custo de manutenção de um índice não exclusivo? Em segundo lugar, o que há de errado em acrescentar a Chave Primária de uma tabela ao final de um índice para garantir a exclusividade?
Exemplo de definição de tabela
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);
Exemplo
Um exemplo de por que eu adicionaria a Uniquechave ao final de um índice está em uma de nossas tabelas de fatos. Existe um Primary Keyque é uma Identitycoluna. No entanto, Clustered Indexé a coluna do esquema de particionamento, seguida por três dimensões de chave estrangeira sem exclusividade. O desempenho selecionado nesta tabela é péssimo e, frequentemente, os tempos de busca são melhores usando o Primary Keycom uma pesquisa de chave, em vez de alavancar o Clustered Index. Outras tabelas que seguem um design semelhante, mas Primary Keyanexadas ao final, têm desempenho consideravelmente melhor.
-- 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

Casee osIflimites são limitados a 10 níveis, faz sentido que também haja um limite para a resolução de entidades não exclusivas. Pela sua declaração, isso parece aplicar-se apenas a casos em que a chave de cluster não é exclusiva. Isso é um problema para umNonclustered Indexou se a chave de cluster éUniqueentão não há um problema paraNonclusteredíndices?