Devo marcar um índice composto como exclusivo se ele contiver a chave primária?

9

Dada uma tabela com uma chave primária, por exemplo:

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

temos uma chave primária exclusiva ativada CustomerID.

Tradicionalmente, talvez eu precise de alguns índices de cobertura adicionais; por exemplo, para encontrar rapidamente um usuário com um CustomerIDou Email:

CREATE INDEX IX_Customers_CustomerIDEmail ON Customers
(
   CustomerID,
   Email
)

E esses são os tipos de índices que eu criei há décadas.

Não é necessário ser único, mas na verdade é

O próprio índice existe para evitar uma varredura de tabela; é um índice de cobertura para ajudar no desempenho (o índice não existe como uma restrição para impor exclusividade).

Hoje, lembrei-me de um pouco de informações - o SQL Server pode usar o fato de que:

  • uma coluna tem uma restrição de chave estrangeira
  • uma coluna possui um índice exclusivo
  • uma restrição é confiável

para ajudá-lo a otimizar a execução da consulta. De fato, no Guia de Design de Índice do SQL Server :

Se os dados forem exclusivos e você desejar aplicar a exclusividade, a criação de um índice exclusivo em vez de um índice não exclusivo na mesma combinação de colunas fornece informações adicionais para o otimizador de consulta que pode produzir planos de execução mais eficientes . É recomendável criar um índice exclusivo (de preferência criando uma restrição UNIQUE) neste caso.

Dado que meu índice de várias colunas contém a chave primária, esse índice composto será de fato único. Não é uma restrição que eu particularmente precise do SQL Server para impor durante cada inserção ou atualização; mas o fato é que esse índice não clusterizado é único.

Existe alguma vantagem em marcar esse índice exclusivo de fato como realmente único?

CRIAR ÍNDICE UNIQUE IX_Customers_CustomerIDEmail ON Customers
(
   Identificação do Cliente,
   O email
)

Parece-me que o SQL Server pode ser inteligente o suficiente para perceber que meu índice já é exclusivo em virtude do fato de conter a chave primária.

  • Mas talvez ele não saiba disso, e há uma vantagem para o otimizador se eu declarar o índice como único de qualquer maneira.
  • Exceto, talvez, que agora possa levar a lentidão durante inserções e atualizações, onde é necessário executar verificações de exclusividade - onde antes nunca era necessário.
  • A menos que saiba que o índice já é garantido , ele contém a chave primária.

Não consigo encontrar nenhuma orientação da Microsoft sobre o que fazer quando um índice composto contém a chave primária.

Os benefícios de índices exclusivos incluem o seguinte:

  • A integridade dos dados das colunas definidas é garantida.
  • Informações adicionais úteis para o otimizador de consulta são fornecidas.

Devo marcar um índice composto como exclusivo se ele já contiver a chave primária? Ou o SQL Server pode descobrir isso por si mesmo?

Ian Boyd
fonte
4
Para encontrar rapidamente um cliente pelo CustomerID, o PK provavelmente é suficiente. Para encontrar um cliente por email, prefiro o segundo índice apenas por email (ele já cobre a chave de cluster). De qualquer forma, eu sei que isso é fictício, mas sim, eu o marcaria como único se você souber que deve ser único; essa restrição provavelmente não pode prejudicar as inserções o suficiente para compensar os casos em que isso pode ajudar o otimizador. Mas, isso tudo depende de sua carga de trabalho, que teríamos de especular sobre ...
Aaron Bertrand
11
Discutimos amplamente aqui. E, embora não possamos pensar em nenhuma maneira de apresentar dados para provar isso de uma maneira ou de outra, assumimos que o pessoal do SQL Server provavelmente é muito inteligente e o otimizador provavelmente já é capaz de fazer essa meta otimização. Também achamos que é provavelmente melhor que o índice comunique a intenção - que é simplesmente um índice, e não imponha uma exclusividade como regra de negócios, caso contrário, isso seria feito apenas para tentar adivinhar os recursos do SQL Server.
Ian Boyd

Respostas:

11

Devo marcar um índice composto como exclusivo se ele já contiver a chave primária?

Provavelmente não. O otimizador geralmente pode usar informações sobre a exclusividade da coluna de chave contida, de modo que não há vantagem real.

Há também uma consequência importante de marcar um índice exclusivo nos planos de atualização que modificam as chaves desse índice a serem consideradas:

Configuração

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;

Plano de atualização por índice (índice não exclusivo)

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

Plano de execução:

Dividir e filtrar

O otimizador geralmente toma uma decisão baseada em custo entre atualizar índices não clusterizados por linha (um plano 'restrito') ou por índice (um plano 'amplo'). A estratégia padrão (exceto tabelas OLTP na memória) é um plano amplo.

Planos restritos (em que índices não clusterizados são mantidos ao mesmo tempo que o índice de heap / cluster) são uma otimização de desempenho para pequenas atualizações. Essa otimização não é implementada para todos os casos - o uso de certos recursos (como visualizações indexadas) significa que os índices associados serão mantidos em um plano amplo.

Mais informações: Otimizando consultas T-SQL que alteram dados

Nesse caso, usei o sinalizador de rastreamento não documentado 8790 para forçar um amplo plano de atualização: O plano, portanto, mostra que os índices clusterizados e não clusterizados são mantidos separadamente.

O Split transforma cada atualização em um par separado de excluir e inserir; o filtro filtra todas as linhas que não resultariam em uma alteração no índice.

Mais informações: ( atualizações sem atualização ) pela equipe de QO do SQL Server.

Plano de atualização por índice (índice exclusivo)

-- 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

Plano de execução:

Dividir-Classificar-Recolher

Observe os operadores extras de classificação e recolhimento quando o índice estiver marcado como exclusivo.

Esse padrão de divisão, classificação e recolhimento é necessário ao atualizar as chaves de um índice exclusivo, para evitar violações intermediárias de chaves exclusivas.

Mais informações: Manutenção de índices exclusivos por Craig Freedman

A classificação em particular pode ser um problema. Além de ser um custo extra desnecessário, ele pode se espalhar para o disco se as estimativas forem imprecisas.

Sobre chaves não clusterizadas

Outro fator a considerar é que as estruturas de índice não clusterizadas são sempre únicas, em todos os níveis do índice, mesmo que UNIQUEnão sejam especificadas. A (s) chave (s) de cluster - e possivelmente um uniquificador se o índice em cluster não estiver marcado como exclusivo - são adicionadas a um índice não clusterizado não exclusivo em todos os níveis.

Como conseqüência, o seguinte índice foi definido:

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

... na verdade contém as chaves (Email, CustomerID) em todos os níveis. Portanto, é 'procurável' nas duas colunas:

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

Procura

Mais informações: Mais sobre chaves de índice não clusterizadas por Kalen Delaney

Paul White 9
fonte
8

O SQL já sabe que é exclusivo (se incluir o PK, ele não poderá ser mais exclusivo), independentemente de você explicitamente informar.

A grande diferença entre um índice não exclusivo e um índice exclusivo é que os índices não exclusivos requerem a chave de índice em cluster (com valor uniquificador se o CIX não for declarado como exclusivo) nos níveis mais altos do índice, não apenas na folha nível.

No seu caso, você já tem o CIX na chave, o que significa que já estará em todos os níveis do índice.

Mas você pode criar uma tabela que possui um PK (exclusivo) e um CIX (não importa). Em seguida, crie um índice não exclusivo que inclua a PK em sua chave. Coloque algumas linhas na tabela, incluindo alguns valores varchar facilmente localizáveis ​​para sua coluna CIX. Coloque linhas suficientes para causar vários níveis de seus índices. Em seguida, você pode usar o DBCC IND para localizar as páginas em seu NCIX e o DBCC PAGE para abrir alguns para examinar os dados e verificar se os valores da chave CIX estão nos níveis mais altos.

Rob Farley
fonte