Qual a importância da ordem das colunas nos índices?

173

Ouvi dizer que você deve colocar colunas que serão as mais seletivas no início da declaração do índice. Exemplo:

CREATE NONCLUSTERED INDEX MyINDX on Table1
(
   MostSelective,
   SecondMost,
   Least
)

Primeiro, é o que estou dizendo correto? Nesse caso, é provável que eu observe grandes diferenças no desempenho reorganizando a ordem das colunas no meu índice ou é mais uma prática "agradável de fazer"?

A razão pela qual estou perguntando é porque, depois de fazer uma consulta no DTA, é recomendável criar um índice que contenha quase todas as mesmas colunas que um índice existente, apenas em uma ordem diferente. Eu estava pensando em adicionar as colunas ausentes ao índice existente e chamá-lo de bom. Pensamentos?

Abe Miessler
fonte

Respostas:

193

Veja um índice como este:

Cols
  1   2   3
-------------
|   | 1 |   |
| A |---|   |
|   | 2 |   |
|---|---|   |
|   |   |   |
|   | 1 | 9 |
| B |   |   |
|   |---|   |
|   | 2 |   |
|   |---|   |
|   | 3 |   |
|---|---|   |

Veja como restringir A primeiro, pois sua primeira coluna elimina mais resultados do que restringir sua segunda coluna primeiro? É mais fácil se você imaginar como o índice deve ser atravessado, coluna 1, coluna 2, etc ... você verá que cortar a maioria dos resultados no primeiro passe torna o segundo passo muito mais rápido.

Outro caso, se você consultasse a coluna 3, o otimizador nem usaria o índice, porque não ajuda em nada a restringir os conjuntos de resultados. Sempre que você estiver em uma consulta, restringir o número de resultados a serem tratados antes da próxima etapa significa melhor desempenho.

Como o índice também é armazenado dessa maneira, não há retorno no índice para encontrar a primeira coluna quando você está consultando.

Em resumo: não, não é para mostrar, existem benefícios reais de desempenho.

Nick Craver
fonte
13
Na figura acima, lembre-se de que esse índice só seria benéfico se a Coluna 1 fosse especificada na consulta. Se sua consulta especificar apenas a Coluna 2 no predicado de ingresso ou pesquisa, isso não será benéfico. Portanto, a ordem também é importante. Talvez isso seja óbvio, mas queria mencionar.
CodeCowboyOrg
3
Lembre-se também, suponha que seu Índice seja como a imagem acima e sua consulta filtre na coluna1 e na coluna2, mas a coluna2 é mais exclusiva e o que você realmente deseja filtrar é realmente a coluna2, então é mais benéfico ter apenas um índice em que a coluna 2 é a primeira. Isso pode parecer contra-intuitivo, mas lembre-se de que um índice é armazenado em várias páginas e é uma árvore com um intervalo de valores, enquanto a Coluna 1 acima nega metade das possibilidades, o índice já sabe para qual página de índice ir direto para o Valor da coluna2, não é necessário que a coluna 1 afine o conjunto.
CodeCowboyOrg
4
Esta imagem não é uma representação precisa de como os índices são estruturados ou navegados. Enviamos uma resposta retificando este stackoverflow.com/a/39080819/73226 #
Martin Smith
6
@ MartinSmith Não concordo que seja impreciso. É muito reconhecidamente extremamente simplificado, que era minha intenção. Porém, sua resposta para obter muito mais detalhes sobre os níveis é bem-vinda, para aqueles que desejam se aprofundar mais. Se você olhar para a imagem da sua árvore, verá o que estou ilustrando de uma maneira muito simples. Isso não é muito exclusivo ou mesmo específico do SQL; A indexação de árvores B é bastante comum em muitas coisas.
Nick Craver
@MartinSmith Eu também discordo que é impreciso, o que você está descrevendo é o comportamento padrão de como chegar a cobrir o índice - a seletividade é muito mais importante quando você realiza consultas de intervalo, pois isso minimiza o número de páginas de índice que o otimizador deve digitalizar; isso pode ser significativo em grandes mesas com milhões de linhas
Paul Hatcher
127

A ordem das colunas é crítica. Agora, qual ordem está correta, depende de como você a consultará. Um índice pode ser usado para fazer uma busca exata ou uma varredura de intervalo. Uma busca exata é quando valores para todas as colunas no índice são especificados e a consulta fica exatamente na linha em que está interessado. Para pesquisas, a ordem das colunas é irrelevante. Uma varredura de intervalo é quando apenas algumas colunas são especificadas e, nesse caso, quando o pedido se torna importante. O SQL Server pode usar um índice para uma verificação de intervalo somente se a coluna mais à esquerda estiver especificada e, somente, se a próxima coluna à esquerda estiver especificada, e assim por diante. Se você tiver um índice em (A, B, C), ele poderá ser usado para varrer a varredura para A=@a, para, A=@a AND B=@bmas não para B=@b, para C=@cnem B=@b AND C=@c. O caso A=@a AND C=@cé misto, como noA=@aparte usará o índice, mas o C=@cnão (a consulta varrerá todos os valores B A=@a, não 'pulará' para C=@c). Outros sistemas de banco de dados têm o operador 'skip scan', que pode tirar vantagem de colunas internas de um índice quando as colunas externas não são especificadas.

Com esse conhecimento em mãos, você pode examinar as definições de índice novamente. Um índice ativado (MostSelective, SecondMost, Least)será efetivo somente quando a MostSelectivecoluna for especificada. Mas, sendo a mais seletiva, a relevância das colunas internas se degradará rapidamente. Muitas vezes, você descobrirá que um índice melhor está ativado (MostSelective) include (SecondMost, Least)ou desativado (MostSelective, SecondMost) include (Least). Como as colunas internas são menos relevantes, a colocação de colunas de baixa seletividade nessas posições corretas no índice não faz nada além de ruído para uma busca; portanto, faz sentido movê-las para fora das páginas intermediárias e mantê-las apenas nas páginas de folha, por fins de cobertura de consultas. Em outras palavras, mova-os para INCLUIR. Isso se torna mais importante à medida que o tamanho da Leastcoluna aumenta. A ideia é que esse índice possa beneficiar apenas consultas que especificamMostSelective como um valor exato ou um intervalo, e essa coluna, sendo a mais seletiva, já restringe as linhas candidatas em grande medida.

Por outro lado, um índice (Least, SecondMost, MostSelective)pode parecer um erro, mas na verdade é um índice bastante poderoso. Por ter a Leastcoluna como sua consulta mais externa, ela pode ser usada para consultas que precisam agregar resultados em colunas de baixa seletividade. Essas consultas são predominantes nos armazéns de dados OLAP e de análise, e é exatamente nesse ponto que esses índices têm um bom argumento. Na verdade, esses índices são excelentes índices agrupados , exatamente porque organizam o layout físico em grandes blocos de linhas relacionadas (mesmo Leastvalor, que geralmente indicam algum tipo de categoria ou tipo) e facilitam as consultas de análise.

Infelizmente, não há uma ordem 'correta'. Você não deve seguir nenhuma receita do cortador de biscoitos, mas sim analisar o padrão de consulta que você usará nessas tabelas e decidir qual ordem da coluna de índice está correta.

Remus Rusanu
fonte
3
Resposta impressionante como sempre Remus. Vou ler o seu terceiro parágrafo mais algumas vezes e acompanhar. Eu suspeito que pode ser exatamente o que eu preciso fazer.
Abe Miessler
"O SQL Server pode usar um índice para uma verificação de intervalo apenas se a coluna mais à esquerda for especificada e somente se a próxima coluna à esquerda for especificada, e assim por diante." Isso exatamente o que estava faltando no meu entendimento, obrigado! Eu não sabia que as varreduras de intervalo só podem ser feitas na coluna de índice usada mais à direita, mas agora que faço isso faz muito sentido.
Allon Guralnek
Esta explicação é aplicável ao Oracle DB?
outro
1
@ Roizpi Sim, basicamente qualquer banco de dados de relações com índices funciona da mesma maneira ou de maneira muito semelhante.
Tatranskymedved
45

Como Remus diz, depende da sua carga de trabalho.

Quero abordar um aspecto enganador da resposta aceita.

Para consultas que estão executando uma pesquisa de igualdade em todas as colunas no índice, não há diferença significativa.

O abaixo cria duas tabelas e as preenche com dados idênticos. A única diferença é que um tem as chaves ordenadas do mais para o menos seletivo e o outro, o inverso.

CREATE TABLE Table1(MostSelective char(800), SecondMost TINYINT, Least  CHAR(1), Filler CHAR(4000) null);
CREATE TABLE Table2(MostSelective char(800), SecondMost TINYINT, Least  CHAR(1), Filler CHAR(4000) null);

CREATE NONCLUSTERED INDEX MyINDX on Table1(MostSelective,SecondMost,Least);
CREATE NONCLUSTERED INDEX MyINDX2 on Table2(Least,SecondMost,MostSelective);

INSERT INTO Table1 (MostSelective, SecondMost, Least)
output inserted.* into Table2
SELECT TOP 26 REPLICATE(CHAR(number + 65),800), number/5, '~'
FROM master..spt_values
WHERE type = 'P' AND number >= 0
ORDER BY number;

Agora, fazendo uma consulta nas duas tabelas ...

SELECT *
FROM   Table1
WHERE  MostSelective = REPLICATE('P', 800)
       AND SecondMost = 3
       AND Least = '~';

SELECT *
FROM   Table2
WHERE  MostSelective = REPLICATE('P', 800)
       AND SecondMost = 3
       AND Least = '~'; 

... Ambos usam uma multa de índice e recebem exatamente o mesmo custo.

insira a descrição da imagem aqui

A arte ASCII na resposta aceita não é de fato como os índices são estruturados. As páginas de índice da Tabela1 estão representadas abaixo (clique na imagem para abrir em tamanho real).

insira a descrição da imagem aqui

As páginas de índice contêm linhas que contêm a chave inteira (nesse caso, na verdade, há uma coluna de chave adicional anexada ao identificador de linha, pois o índice não foi declarado como único, mas que pode ser desconsiderado, informações adicionais sobre isso podem ser encontradas aqui ).

Para a consulta acima, o SQL Server não se importa com a seletividade das colunas. Ele faz uma pesquisa binária da página raiz e descobre que a chave (PPP...,3,~ ) é >=(JJJ...,1,~ )e < (SSS...,3,~ )deve ler a página 1:118. Em seguida, ele faz uma pesquisa binária das entradas principais nessa página e localiza a página da folha para a qual viajar.

Alterar o índice em ordem de seletividade não afeta o número esperado de comparações de chaves da pesquisa binária ou o número de páginas que precisam ser navegadas para fazer uma pesquisa no índice. Na melhor das hipóteses, pode acelerar marginalmente a própria comparação de chaves.

Às vezes, solicitar o índice mais seletivo primeiro fará sentido para outras consultas em sua carga de trabalho.

Por exemplo, se a carga de trabalho contiver consultas dos dois formulários a seguir.

SELECT * ... WHERE  MostSelective = 'P'

SELECT * ...WHERE Least = '~'

Os índices acima não estão cobrindo para nenhum deles. MostSelectiveé seletivo o suficiente para fazer um plano com uma busca e pesquisas que valham a pena, mas a consulta contraLeast não é.

No entanto, esse cenário (busca de índice não abrangente no subconjunto de colunas principais de um índice composto) é apenas uma classe de consulta possível que pode ser ajudada por um índice. Se você nunca pesquisar MostSelectivesozinho ou uma combinação deMostSelective, SecondMost e sempre procurar por uma combinação das três colunas, essa vantagem teórica será inútil para você.

Por outro lado, consultas como

SELECT MostSelective,
       SecondMost,
       Least
FROM   Table2
WHERE  Least = '~'
ORDER  BY SecondMost,
          MostSelective 

Seria ajudado por ter a ordem inversa da normalmente prescrita - já que abrange a consulta, pode suportar uma busca e retorna linhas na ordem desejada para inicializar.

Portanto, este é um conselho frequentemente repetido, mas, no máximo, é uma heurística sobre o benefício potencial para outras consultas - e não é um substituto para realmente analisar sua carga de trabalho.

Martin Smith
fonte
31

você deve colocar as colunas que serão as mais seletivas no início da declaração do índice.

Corrigir. Os índices podem ser compostos - compostos por várias colunas - e a ordem é importante por causa do princípio mais à esquerda. O motivo é que o banco de dados verifica a lista da esquerda para a direita e precisa encontrar uma referência de coluna correspondente que corresponda à ordem definida. Por exemplo, ter um índice em uma tabela de endereços com colunas:

  • Endereço
  • Cidade
  • Estado

Qualquer consulta usando a addresscoluna pode utilizar o índice, mas se a consulta tiver apenas referências citye / ou state- o índice não poderá ser usado. Isso ocorre porque a coluna mais à esquerda não é referenciada. O desempenho da consulta deve informar qual é o ideal - índices individuais ou vários compostos com ordens diferentes. Boa leitura: The Tipping Point , de Kimberley Tripp

Pôneis OMG
fonte
E se fosse apenas a coluna mais à direita que não estivesse sendo usada? Portanto, uma consulta usou Endereço e cidade, mas NÃO estado. O índice seria usado então?
Abe Miessler
@ Abe: O mais à direita não seria usado - você deve satisfazer a ordem do índice a partir da esquerda. Senhorita um, não posso usá-lo.
OMG Ponies
4
@ Abe: Se você consultou o endereço e a cidade, mas NÃO o estado - então sim, o índice seria usado. Em outras palavras, o banco de dados pode usar índices parciais para atender a uma solicitação, desde que seja capaz de iniciar a partir da esquerda de um índice e mover para a direita usando os campos que estão sendo consultados. Se, no entanto, você consultou usando Endereço e Estado, mas NÃO cidade, ele ainda pode usar o índice, mas não será tão eficiente - porque agora ele só pode usar a parte Endereço do índice (b / c a seguir é cidade e não está sendo usado na consulta).
JaredC
6

Todas as outras respostas estão erradas.

A seletividade das colunas individuais em um índice composto não importa ao escolher o pedido.

Aqui está o processo simples de pensamento: efetivamente, um índice é a concatenação das colunas envolvidas.

Dando essa justificativa, a única diferença é comparar duas 'strings' que diferem mais cedo ou mais tarde na string. Esta é uma pequena parte do custo total. Não há "primeira passagem / segunda passagem", como mencionado em uma resposta.

Então, qual ordem deve ser usada?

  1. Comece com a (s) coluna (s) testada (s) com =, em qualquer ordem.
  2. Em seguida, incline em uma coluna de intervalo.

Por exemplo, a coluna de seletividade muito baixa deve vir em primeiro lugar:

WHERE deleted = 0  AND  the_datetime > NOW() - INTERVAL 7 DAY
INDEX(deleted, the_datetime)

Trocar a ordem no índice faria com que ela fosse totalmente ignorada deleted.

(Existem muito mais regras para ordenar as colunas.)

Rick James
fonte
O voto negativo é porque estou errado? Ou porque tenho uma opinião forte? Ou alguma outra coisa?
Rick James
não foi meu voto negativo, mas excluído = 0 para mim parece que não é baixa seletividade? Eu imagino que seria a maioria das linhas na tabela.
7742 Greg Greg
@ Greg - Eu acho que isso significa "baixa seletividade" - Ou seja, o uso deletednão ajuda muito na filtragem de linhas indesejadas. Você tem um exemplo melhor? (Isso é o que me veio à mente quando eu escrevi a resposta.)
Rick James
Incompreensão da minha parte.
7742 Greg Greg
1
@ClickOk - Obrigado. Meu livro de receitas fornece algumas informações básicas: mysql.rjweb.org/doc.php/index_cookbook_mysql
Rick James