Por que usar a cláusula INCLUDE ao criar um índice?

432

Ao estudar para o exame 70-433, notei que você pode criar um índice de cobertura de uma das duas maneiras a seguir.

CREATE INDEX idx1 ON MyTable (Col1, Col2, Col3)

- OU -

CREATE INDEX idx1 ON MyTable (Col1) INCLUDE (Col2, Col3)

A cláusula INCLUDE é nova para mim. Por que você o usaria e quais diretrizes sugeriria para determinar se um índice de cobertura deve ser criado com ou sem a cláusula INCLUDE?

Cory
fonte

Respostas:

364

Se a coluna não estiver na WHERE/JOIN/GROUP BY/ORDER BY, mas apenas na lista de colunas na SELECTcláusula.

A INCLUDEcláusula adiciona os dados no nível mais baixo / folha, em vez de na árvore de índice. Isso diminui o índice porque não faz parte da árvore

INCLUDE columnsnão são colunas-chave no índice, portanto não são ordenadas. Isso significa que não é realmente útil para predicados, classificação etc., como mencionei acima. No entanto, pode ser útil se você tiver uma pesquisa residual em algumas linhas da (s) coluna (s) chave (s)

Outro artigo do MSDN com um exemplo trabalhado

gbn
fonte
7
Então, essa seria uma técnica para criar uma versão mais barata de um índice coberto?
JMarsch
3
@gbn, você se importaria de explicar esta frase com mais detalhes e explique por que isso significa que a cláusula include não é útil para classificação, etc: "A cláusula INCLUDE adiciona os dados no nível mais baixo / folha, e não na árvore de índice . Isso diminui o índice porque não faz parte da árvore "
Tola Odejayi
4
@ JMarsch: desculpe pela resposta tardia, mas sim, é exatamente isso que é.
gbn
10
@Tola Odejayi: INCLUDE as colunas não são colunas-chave no índice, portanto não são ordenadas. Isso os torna tipicamente úteis para JOINs ou classificação. E porque eles são colunas não-chave, eles não se sentar em toda a estrutura B-árvore como colunas de chave
GBN
4
Enquanto esta é a resposta mais aceita, eu acho que é necessário mais explicações, e se para algumas consultas a coluna é parte do SELECTe para alguns não \?
Chisko
215

Você usaria INCLUDE para adicionar uma ou mais colunas ao nível da folha de um índice não clusterizado. Se você fizer isso, poderá "cobrir" suas consultas.

Imagine que você precise consultar o ID, o ID do departamento e o sobrenome de um funcionário.

SELECT EmployeeID, DepartmentID, LastName
FROM Employee
WHERE DepartmentID = 5

Se você tiver um índice não agrupado em (CódigoDoEmpregado, CódigoDoDomínio), depois de encontrar os funcionários de um determinado departamento, você precisará fazer uma "pesquisa de favoritos" para obter o registro completo do funcionário, apenas para obter a coluna do sobrenome . Isso pode ficar bem caro em termos de desempenho, se você encontrar muitos funcionários.

Se você incluiu esse sobrenome no seu índice:

CREATE NONCLUSTERED INDEX NC_EmpDep 
  ON Employee(EmployeeID, DepartmentID)
  INCLUDE (Lastname)

todas as informações necessárias estarão disponíveis no nível folha do índice não clusterizado. Apenas procurando no índice não agrupado e encontrando seus funcionários para um determinado departamento, você tem todas as informações necessárias e a pesquisa de marcadores para cada funcionário encontrado no índice não é mais necessária -> você economiza muito tempo.

Obviamente, você não pode incluir todas as colunas em todos os índices não agrupados - mas, se houver consultas em que faltam apenas uma ou duas colunas para serem "cobertas" (e que são muito usadas), pode ser muito útil INCLUIR aquelas em um índice não clusterizado adequado.

marc_s
fonte
25
Tem certeza de que usaria esse índice? Por que EmployeeID? Você só precisa do DepartmentID nas colunas-chave? Você foi citado aqui como autorativo: stackoverflow.com/q/6187904/27535
gbn
3
Sua explicação é boa, mas na verdade não está alinhada com o caso de uso que você descreve. As colunas de chave devem estar no filtro ou JOINnas chaves da consulta e INCLUDEdevem ser os dados que você está recuperando, mas não classificando.
JNK
15
Em primeiro lugar o empregado índice (CódigoDoEmpregado, DepartmentID) não irá ser usado para filtrar DepartmentID = 5. Devido a sua ordem não está combinando
AnandPhadke
29

Esta discussão está perdendo o ponto importante: A questão não é se as "colunas sem chave" são melhores para incluir como colunas de índice ou como colunas incluídas .

A questão é quão caro é usar o mecanismo de inclusão para incluir colunas que não são realmente necessárias no índice ? (normalmente não faz parte das cláusulas where, mas geralmente inclui as seleções). Portanto, seu dilema é sempre:

  1. Use o índice em id1, id2 ... idN sozinho ou
  2. Use o índice em id1, id2 ... idN e inclua col1, col2 ... colN

Onde: id1, id2 ... idN são colunas frequentemente usadas em restrições e col1, col2 ... colN são colunas frequentemente selecionadas, mas geralmente não são usadas em restrições

(A opção de incluir todas essas colunas como parte da chave de índice é sempre sempre boba (a menos que elas também sejam usadas em restrições) - porque sempre seria mais caro manter uma vez que o índice deve ser atualizado e classificado mesmo quando o "chaves" não foram alteradas).

Então use a opção 1 ou 2?

Resposta: Se sua tabela raramente é atualizada - principalmente inserida / excluída de -, é relativamente barato usar o mecanismo de inclusão para incluir algumas "colunas quentes" (que são frequentemente usadas em seleções - mas não costumam ser usadas em restrições), pois inserções / exclusões exigem que o índice seja atualizado / classificado de qualquer maneira e, portanto, pouca sobrecarga extra é associada ao armazenamento de algumas colunas extras enquanto você já atualiza o índice. A sobrecarga é a memória e a CPU extras usadas para armazenar informações redundantes no índice.

Se as colunas que você considera adicionar como colunas incluídas são frequentemente atualizadas (sem que as colunas- chave do índice sejam atualizadas) - ou - se são tantas delas que o índice se aproxima de uma cópia da sua tabela - use a opção 1 Eu sugiro! Além disso, se adicionar determinadas colunas de inclusão não fizer diferença no desempenho - você pode ignorar a ideia de adicioná-las :) Verifique se elas são úteis!

O número médio de linhas pelos mesmos valores nas chaves (id1, id2 ... idN) também pode ser de alguma importância.

Observe que, se uma coluna - adicionada como uma coluna incluída do índice - for usada na restrição : Enquanto o índice, como tal, puder ser usado (com base na restrição contra colunas da chave do índice ) -, o SQL Server corresponderá a restrição de coluna em relação ao índice (valores do nó da folha) em vez de percorrer o caminho caro da própria tabela.

Fredrik Solhaug
fonte
18

As colunas básicas do índice são classificadas, mas as colunas incluídas não são classificadas. Isso economiza recursos na manutenção do índice, enquanto ainda é possível fornecer os dados nas colunas incluídas para cobrir uma consulta. Portanto, se você deseja cobrir consultas, pode colocar os critérios de pesquisa para localizar linhas nas colunas classificadas do índice, mas depois "incluir" colunas adicionais e não classificadas com dados que não são de pesquisa. Definitivamente, ajuda a reduzir a quantidade de classificação e fragmentação na manutenção do índice.

onupdatecascade
fonte
7

As razões pelas quais (incluindo os dados no nível da folha do índice) foram bem explicadas. O motivo pelo qual você dá duas dicas sobre isso é que, quando você executa sua consulta, se você não tiver as colunas adicionais incluídas (novo recurso no SQL 2005), o SQL Server precisará acessar o índice de cluster para obter as colunas adicionais que leva mais tempo e adiciona mais carga ao serviço do SQL Server, aos discos e à memória (cache do buffer para ser específico) à medida que novas páginas de dados são carregadas na memória, potencialmente expulsando outros dados necessários com mais freqüência do cache do buffer.

Mrdenny
fonte
existe uma maneira de provar que ele está realmente usando menos memória? é o que eu esperaria também, mas estou ficando um pouco estático sobre isso no trabalho #
0000 Asken
Como você precisa carregar a página do heap ou do índice clusterizado na memória, bem como da página de índice, o que significa que você está colocando dados duplicados na memória, a matemática se torna bastante simples. Quanto a uma maneira de medir especificamente, não, não existe.
mrdenny
5

Uma consideração adicional que não vi nas respostas já fornecidas é que as colunas incluídas podem ser de tipos de dados que não são permitidos como colunas de chave de índice, como varchar (max).

Isso permite incluir essas colunas em um índice de cobertura. Recentemente, tive que fazer isso para fornecer uma consulta gerada pelo nHibernate, que tinha muitas colunas no SELECT, com um índice útil.

Robin Hames
fonte
3

Um motivo para preferir INCLUDEas colunas-chave, se você não precisar dessa coluna na chave, é a documentação. Isso torna os índices em evolução muito mais fáceis no futuro.

Considerando o seu exemplo:

CREATE INDEX idx1 ON MyTable (Col1) INCLUDE (Col2, Col3)

Esse índice é melhor se a sua consulta estiver assim:

SELECT col2, col3
  FROM MyTable
 WHERE col1 = ...

É claro que você não deve colocar colunas INCLUDEse puder obter um benefício adicional por tê-las na parte principal. Na verdade, as duas consultas a seguir preferem a col2coluna na chave do índice.

SELECT col2, col3
  FROM MyTable
 WHERE col1 = ...
   AND col2 = ...
SELECT TOP 1 col2, col3
  FROM MyTable
 WHERE col1 = ...
 ORDER BY col2

Vamos assumir que esse não é o caso e temos col2a INCLUDEcláusula porque simplesmente não há benefício em tê-la na parte da árvore do índice.

Avanço rápido em alguns anos.

Você precisa ajustar esta consulta:

SELECT TOP 1 col2
  FROM MyTable
 WHERE col1 = ...
 ORDER BY another_col

Para otimizar essa consulta, o seguinte índice seria ótimo:

CREATE INDEX idx1 ON MyTable (Col1, another_col) INCLUDE (Col2)

Se você verificar quais índices você já possui nessa tabela, o índice anterior ainda pode estar lá:

CREATE INDEX idx1 ON MyTable (Col1) INCLUDE (Col2, Col3)

Agora você sabe disso Col2e Col3não faz parte da árvore de índices e, portanto, não é usado para restringir o intervalo do índice de leitura nem para ordenar as linhas. É bastante seguro adicionar another_columno final da parte-chave do índice (depois col1). Há pouco risco de quebrar qualquer coisa:

DROP INDEX idx1 ON MyTable;
CREATE INDEX idx1 ON MyTable (Col1, another_col) INCLUDE (Col2, Col3);

Esse índice se tornará maior, o que ainda apresenta alguns riscos, mas geralmente é melhor estender os índices existentes em comparação com a introdução de novos.

Se você tivesse um índice sem INCLUDE, não saberia quais consultas interromperia adicionando another_collogo em seguida Col1.

CREATE INDEX idx1 ON MyTable (Col1, Col2, Col3)

O que acontece se você adicionar another_colentre Col1e Col2? Outras consultas sofrerão?

Existem outros "benefícios" de INCLUDEcolunas-chave vs. se você adicionar essas colunas apenas para evitar buscá-las na tabela . No entanto, considero o aspecto da documentação o mais importante.

Para responder sua pergunta:

que diretrizes você sugeriria para determinar se criaria um índice de cobertura com ou sem a cláusula INCLUDE?

Se você adicionar uma coluna ao índice com o único objetivo de disponibilizá-la no índice sem visitar a tabela, coloque-a na INCLUDEcláusula

Se adicionar a coluna à chave de índice traz benefícios adicionais (por exemplo, para order byou porque pode restringir o intervalo de leitura do índice), adicione-o à chave.

Você pode ler uma discussão mais longa sobre isso aqui:

https://use-the-index-luke.com/blog/2019-04/include-columns-in-btree-indexes

Markus Winand
fonte
2

Há um limite para o tamanho total de todas as colunas incluídas na definição do índice. Dito isto, nunca tive que criar um índice tão amplo. Para mim, a maior vantagem é o fato de que você pode cobrir mais consultas com um índice que incluiu colunas, pois elas não precisam ser definidas em nenhuma ordem específica. Pense sobre é como um índice dentro do índice. Um exemplo seria o StoreID (em que StoreID é baixa seletividade, o que significa que cada loja está associada a muitos clientes) e, em seguida, os dados demográficos do cliente (Sobrenome, Nome, DOB): Se você apenas alinhar essas colunas nesta ordem (StoreID, Sobrenome) , FirstName, DOB), você só pode procurar com eficiência por clientes dos quais conhece StoreID e LastName.

Por outro lado, definir o índice no StoreID e incluir as colunas Sobrenome, Nome e DOB permitiria, em essência, fazer dois predicados de índice de busca no StoreID e, em seguida, procurar predicado em qualquer uma das colunas incluídas. Isso permitiria cobrir todas as permutações de pesquisa possíveis, desde que iniciasse com o StoreID.

mEmENT0m0RI
fonte