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 Insert
operaçã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 Insert
comparaçã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 Unique
chave ao final de um índice está em uma de nossas tabelas de fatos. Existe um Primary Key
que é uma Identity
coluna. 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 Key
com uma pesquisa de chave, em vez de alavancar o Clustered Index
. Outras tabelas que seguem um design semelhante, mas Primary Key
anexadas 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
Case
e osIf
limites 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 Index
ou se a chave de cluster éUnique
então não há um problema paraNonclustered
índices?