Devo indexar um campo de bits no SQL Server?

99

Lembro-me de ter lido em um ponto que não vale a pena fazer a indexação de um campo com baixa cardinalidade (um baixo número de valores distintos). Admito que não sei o suficiente sobre como funcionam os índices para entender por que isso acontece.

E daí se eu tiver uma tabela com 100 milhões de linhas e estiver selecionando registros em que um campo de bits é 1? E digamos que, em qualquer ponto no tempo, haja apenas um punhado de registros em que o campo de bits seja 1 (em oposição a 0). Vale a pena indexar esse campo de bits ou não? Por quê?

Claro que posso apenas testá-lo e verificar o plano de execução, e farei isso, mas também estou curioso sobre a teoria por trás disso. Quando é que a cardinalidade é importante e quando não é?

jeremcc
fonte
Esta é uma consulta comum? Pode valer a pena ao procurar um "punhado" de registros, mas não ajudará muito nas outras linhas. Existem outras maneiras de identificar os dados?
jason saldo,
4
Embora eu não ache que indexaria APENAS uma coluna de bits por si só, é muito comum incluir colunas de bits como parte de um índice composto. Um exemplo simples seria um índice em ACTIVE, LASTNAME em vez de apenas lastname, quando seu aplicativo está quase sempre procurando por clientes ativos.
BradC
"Lembro-me de ter lido em um ponto que indexar um campo com baixa cardinalidade (um baixo número de valores distintos) realmente não vale a pena fazer" Isso porque o SQL Server quase sempre achará mais eficiente apenas fazer uma verificação de tabela do que ler o índice. Então, basicamente, seu índice nunca será usado e é um desperdício mantê-lo. Como outros disseram, pode ser bom em um índice composto.
DJ.
5
Eu discordo. Se sua distribuição for 50/50, você nunca usaria o índice, pois seria mais rápido fazer uma varredura na tabela. No entanto, se você tiver apenas 5, 1 valores e 1 milhão de valores 0, é muito provável que use o índice ao pesquisar 1.
Kibbee,
1
No exemplo que você deu, eu estaria mais inclinado a colocar o Sobrenome primeiro. Depende da carga de trabalho de consulta específica, mas em geral ter a coluna mais seletiva primeiro significa que o índice é mais provável de ser usado.
Mitch Wheat,

Respostas:

72

Considere o que é um índice em SQL - e o índice é realmente um pedaço de memória apontando para outros pedaços de memória (ou seja, ponteiros para linhas). O índice é dividido em páginas para que partes do índice possam ser carregadas e descarregadas da memória, dependendo do uso.

Quando você pede um conjunto de linhas, o SQL usa o índice para encontrar as linhas mais rapidamente do que a verificação da tabela (olhando para cada linha).

SQL tem índices clusterizados e não clusterizados. Meu entendimento sobre índices clusterizados é que eles agrupam valores de índice semelhantes na mesma página. Dessa forma, quando você solicitar todas as linhas que correspondem a um valor de índice, o SQL pode retornar essas linhas de uma página de memória agrupada. É por isso que tentar agrupar o índice de uma coluna GUID é uma má ideia - você não tenta agrupar valores aleatórios.

Quando você indexa uma coluna inteira, o índice SQL contém um conjunto de linhas para cada valor de índice. Se você tiver um intervalo de 1 a 10, terá 10 indicadores de índice. Dependendo de quantas linhas existem, isso pode ser paginado de forma diferente. Se sua consulta procurar o índice correspondente a "1" e onde Nome contém "Fred" (presumindo que a coluna Nome não esteja indexada), o SQL obtém o conjunto de linhas correspondentes a "1" muito rapidamente e, em seguida, verifica a tabela para encontrar o resto.

Portanto, o que o SQL está realmente fazendo é tentar reduzir o conjunto de trabalho (número de linhas) sobre o qual ele deve iterar.

Ao indexar um campo de bits (ou algum intervalo estreito), você apenas reduz o conjunto de trabalho pelo número de linhas que correspondem a esse valor. Se você tiver um pequeno número de linhas correspondentes, isso reduzirá muito seu conjunto de trabalho. Para um grande número de linhas com distribuição 50/50, isso pode significar muito pouco ganho de desempenho em comparação com manter o índice atualizado.

O motivo pelo qual todo mundo diz para testar é porque o SQL contém um otimizador muito inteligente e complexo que pode ignorar um índice se decidir que a varredura da tabela é mais rápida, ou pode usar uma classificação, ou pode organizar as páginas de memória da maneira que bem entender.

Geoff Cox
fonte
Portanto, parece que se eu só tivesse um punhado de linhas onde o campo de bits é 1 (por exemplo, manter o controle de "IsProcessed"), um índice seria bom porque ele os ordenará por valor e, então, será capaz de selecionar o pequeno conjunto de trabalho muito rapidamente. Se você concorda, acrescente e eu aceitarei.
jeremcc
2
O que quero dizer em meu comentário anterior é que esta afirmação: "Quando você indexa um campo de bits (ou algum intervalo estreito), você apenas reduz o conjunto de trabalho pela metade" não é verdadeira se a distribuição é fortemente ponderada para um valor. Mas gostei do resto da sua resposta, então, se você corrigir isso, vou aceitá-la.
jeremcc
1
Feito. Eu estava pensando que para um milhão de linhas, um campo de bits teria 50% de distribuição, mas você está certo ao dizer que, para um determinado espaço de problema, ele poderia reduzir muito o conjunto de trabalho.
Geoff Cox
Vale a pena examinar os planos de execução com e sem o índice e ver se o índice está sendo usado e se ele realmente reduz o custo de suas consultas. Fácil e científico!
onupdatecascade
Que tal indexar um campo de bits + outro campo? Por exemplo. em um log de atividades da web, seria indexado o carimbo de data / hora, mas outro índice útil pode estar em um campo de bits "IsHTTPS" + carimbo de data / hora, para visualizar rapidamente todas as ações https. Isso também seria ineficiente?
ingrediente_15939
19

Acabei de encontrar essa pergunta por meio de outra. Supondo que sua declaração de que apenas um punhado de registros assuma o valor 1 (e que esses são aqueles em que você está interessado), um índice filtrado pode ser uma boa escolha. Algo como:

create index [IX_foobar] on dbo.Foobar (FooID) where yourBitColumn = 1

Isso criará um índice substancialmente menor que o otimizador é inteligente o suficiente para usar quando for um predicado em sua consulta.

Ben Thul
fonte
1
É importante observar que o predicado na consulta deve ser codificado para o valor no índice filtrado. Se você passar o valor em um parâmetro yourBitColumn = @value, o otimizador não poderá determinar se o índice filtrado pode ser usado.
geofftnz
2
Existem maneiras de contornar isso, mas você está certo; o otimizador precisa de uma garantia no momento da compilação de que os valores de quaisquer predicados que correspondam ao predicado do índice filtrado sejam estáticos / invariáveis, pois é trabalho do otimizador criar um plano geral que funcione para qualquer conjunto de parâmetros.
Ben Thul
9

100 milhões de registros, com apenas alguns tendo o campo de bits definido como 1? Sim, eu acho que a indexação do campo de bits definitivamente acelera a consulta dos registros bit = 1. Você deve obter o tempo de pesquisa logarítmica do índice e, em seguida, tocar apenas nas poucas páginas com registros de bit = 1. Do contrário, você teria que tocar em todas as páginas da tabela de 100 milhões de registros.

Então, novamente, eu definitivamente não sou um especialista em banco de dados e pode estar faltando algo importante.

C. Dragon 76
fonte
8

Se a sua distribuição for bastante conhecida e desequilibrada, como 99% das linhas são bit = 1 e 1% são bit = 0, quando você faz uma cláusula WHERE com bit = 1, uma verificação completa da tabela ocorrerá ao mesmo tempo que a varredura de índice. Se você quiser ter uma consulta rápida onde bit = 0, a melhor maneira que conheço é criar um índice filtrado, adicionando uma cláusula WHERE bit = 0. Dessa forma, esse índice armazenará apenas a linha de 1%. Então, fazer um bit WHERE = 0 simplesmente deixará o otimizador de consulta escolher aquele índice, e todas as linhas dele serão bit = 0. Você também tem a vantagem de ter uma quantidade muito pequena de espaço em disco necessária para comparar um índice completo no bit .

Philippe Boucher
fonte
2
Se 99% das linhas forem bit = 1, o otimizador deve ignorar o índice e fazer uma varredura na tabela. Usar o índice será pior do que uma varredura de tabela, pelo menos em uma unidade rotacional, mais E / S e leituras não consecutivas do disco. O índice filtrado (equivalente ao Postgres: índice parcial) é o caminho a percorrer. Acho que porque já se passaram anos depois da pergunta, essa resposta não obteve os votos que merecia.
Andrew Lazarus
7

Embora eu não ache que indexaria APENAS uma coluna de bits por si só, é muito comum incluir colunas de bits como parte de um índice composto.

Um exemplo simples seria um índice em ACTIVE, LASTNAME em vez de apenas lastname, quando seu aplicativo está quase sempre procurando por clientes ativos.

BradC
fonte
7
No exemplo que você deu, eu estaria mais inclinado a colocar o Sobrenome primeiro. Depende da carga de trabalho de consulta específica, mas em geral ter a coluna mais seletiva primeiro significa que o índice é mais provável de ser usado.
Mitch Wheat,
7

Caso você não tenha lido, Jason Massie escreveu um artigo recentemente que discutiu esse mesmo tópico.

http://statisticsio.com/Home/tabid/36/articleType/ArticleView/articleId/302/Never-Index-a-BIT.aspx

Editar: Novo local do artigo - http://sqlserverpedia.com/blog/sql-server-bloggers/never-index-a-bit

Máquina de retorno para a localização do artigo anteriormente "Novo": http://web.archive.org/web/20120201122503/http://sqlserverpedia.com/blog/sql-server-bloggers/never-index-a-bit/

O novo local do SQL Server Pedia é Toadworld, que tem um novo artigo de Kenneth Fisher discutindo este tópico:

http://www.toadworld.com/platforms/sql-server/b/weblog/archive/2014/02/17/dba-myths-an-index-on-a-bit-column-will-never-be- used.aspx

máquina de retorno: http://web.archive.org/web/20150508115802/http://www.toadworld.com/platforms/sql-server/b/weblog/archive/2014/02/17/dba-myths-an -index-on-a-bit-column-will-never-be-used.aspx

Jeff
fonte
este artigo não está mais visível
Homero6 de
@ Homer6 Eu adicionei um link para o que parece ser a nova casa para este artigo.
Jeff de
Novo link vai para a página inicial do Toad World.
N West
Encontrei o artigo usando a máquina Wayback e encontrei um novo artigo relacionado. Espero que isto ajude.
Jeff
2

Claro que vale a pena, especialmente se você precisar recuperar os dados por esse valor. Seria semelhante a usar uma matriz esparsa em vez de usar uma matriz normal.

Agora, com o SQL 2008, você pode usar funções de particionamento e filtrar os dados que vão para um índice. A desvantagem para versões anteriores seria que o índice seria feito para todos os dados, mas isso pode ser otimizado armazenando os valores interessantes em um grupo de arquivos separado.

Bogdan Maxim
fonte
2

Como outros já disseram, você vai querer medir isso. Não me lembro onde li isso, mas uma coluna precisa ter uma cardinalidade muito alta (cerca de 95%) para que um índice seja eficaz. Seu melhor teste para isso seria construir o índice e examinar os planos de execução para os valores 0 e 1 do campo BIT. Se você vir uma operação de busca de índice no plano de execução, você sabe que seu índice será usado.

Seu melhor curso de ação seria testar o com uma tabela SELECT * FROM básica WHERE BitField = 1; consulte e construa lentamente a funcionalidade a partir daí, passo a passo, até que você tenha uma consulta realista para seu aplicativo, examinando o plano de execução em cada etapa para ter certeza de que a busca de índice ainda está sendo utilizada. É certo que não há garantia de que esse plano de execução será utilizado na produção, mas há boas chances de que seja.

Algumas informações podem ser encontradas nos fóruns sql-server-performance.com e no artigo referenciado

Jeremiah Peschka
fonte
Não é tanto a cardinalidade da coluna como um todo que importa. É a seletividade da cláusula WHERE. Portanto, se houver poucas colunas com valor 1, ainda pode ser bom indexar. Se for 50/50 (por exemplo, masculino / feminino), então não vale a pena.
WW.
2

"Lembro-me de ter lido em um ponto que indexar um campo com baixa cardinalidade (um baixo número de valores distintos) realmente não vale a pena fazer"

Isso porque o SQL Server quase sempre achará mais eficiente apenas fazer uma varredura de tabela do que ler o índice. Então, basicamente, seu índice nunca será usado e é um desperdício mantê-lo. Como outros já disseram, pode ser bom em um índice composto.

DJ.
fonte
2

Se o seu objetivo é tornar a consulta de registros onde o valor do campo de bits é igual a '1' mais rápido, você pode tentar uma visão indexada de sua tabela base que contenha apenas registros onde seu campo de bits é igual a '1'. Na edição corporativa, se uma consulta puder usar uma visualização indexada em vez de uma tabela especificada para melhorar o desempenho da consulta, ela usará a visualização. Em teoria, isso aumentaria a velocidade das consultas selecionadas que procuram apenas registros com um valor de campo de bit de '1'.

http://www.microsoft.com/technet/prodtechnol/sql/2005/impprfiv.mspx

Tudo isso pressupõe que você seja o Microsoft SQL Server 2005 Enterprise. O mesmo pode se aplicar a 2008, não estou familiarizado com essa versão.


fonte
2

Se você deseja saber se um índice tem os efeitos desejados: teste e teste novamente.

Em geral, você não quer um índice que não restrinja sua tabela o suficiente, devido ao custo de manutenção de um índice. (custo> lucro). Mas se o índice no seu caso reduzir a mesa pela metade, você pode ganhar algo, mas colocando-o na mesa. Tudo depende do tamanho / estrutura exata de sua tabela e como você a está usando (número de leituras / gravações).

thijs
fonte
1

Por si só, não, pois resulta em muito pouca seletividade. Como parte de um índice composto. muito possivelmente, mas somente após outras colunas de igualdade.

Craig Nicholson
fonte
1

Não é possível indexar um campo de bits no SQL Server 2000, conforme indicado nos Manuais Online da época:

mordeu

Tipo de dados inteiro 1, 0 ou NULL.

Observações

As colunas do tipo bit não podem ter índices.

Sim, se você tiver apenas um punhado de linhas, entre milhões, um índice ajudará. Mas se você quiser fazer isso neste caso, você precisa tornar a coluna a tinyint.

Observação : o Enterprise Manager não permitirá que você crie um índice em uma coluna de bits. Se desejar, você ainda pode criar manualmente um índice em uma coluna de bits:

CREATE INDEX IX_Users_IsActiveUsername ON Users
(
   IsActive,
   Username
)

Mas o SQL Server 2000 não usará realmente esse índice - executando uma consulta em que o índice seria um candidato perfeito, por exemplo:

SELECT TOP 1 Username 
FROM Users
WHERE IsActive = 0

O SQL Server 2000 fará uma varredura na tabela, agindo como se o índice nem existisse. Se você alterar a coluna para um tinyint, o SQL Server 2000 fará uma busca de índice. Além disso, a seguinte consulta não coberta:

SELECT TOP 1 * 
FROM Users
WHERE IsActive = 0

Ele executará uma busca de índice, seguida por uma pesquisa de marcador.


O SQL Server 2005 tem suporte limitado para índices em colunas de bits. Por exemplo:

SELECT TOP 1 Username 
FROM Users
WHERE IsActive = 0

irá causar uma busca de índice através do índice de cobertura. Mas o caso não coberto:

SELECT TOP 1 * 
FROM Users
WHERE IsActive = 0

não causará uma busca de índice seguida por uma pesquisa de marcador, ele executará uma varredura de tabela (ou varredura de índice clusterizado), em vez de realizar a busca de índice seguida por uma pesquisa de marcador.

Verificado por experimentação e observação direta.

Ian Boyd
fonte
Para sua informação - o SQL Server 2005 Management Studio permite que você faça isso.
jeremcc
Minha cópia do SQL Server 2000 me permitiu definir um índice em uma coluna de bits.
Kibbee
Minha cópia do SQL Server 2000 não me permite definir um índice em uma coluna de bits.
Ian Boyd,
1

muito tarde resposta ...

Sim, pode ser útil de acordo com a equipe SQL CAT (atualizado, foi consolidado)

gbn
fonte
1
O link parece estar morto agora. No entanto, essa postagem parece ter sido consolidada junto com várias outras em um e-book . A seção referenciada começa na página 86. O e-book pode ser baixado de SQLCAT.com eBooks no link "SQLCAT's Guide to Relational Engine".
mwolfe02
0

Esta é uma consulta comum? Pode valer a pena ao procurar um "punhado" de registros, mas não ajudará muito nas outras linhas. Existem outras maneiras de identificar os dados?

jason saldo
fonte
0

A cardinalidade é um fator, o outro é quão bem o índice divide seus dados. Se você tiver cerca de meio 1s e meio 0s, isso ajudará. (Supondo que esse índice seja um caminho melhor para escolher do que algum outro índice). No entanto, com que frequência você insere e atualiza? Adicionar índices para o desempenho do SELECT também prejudicou o desempenho do INSERT, UPDATE e DELETE, então tenha isso em mente.

Eu diria que se os 1s a 0s (ou vice-versa) não forem melhores do que 75% a 25%, não se preocupe.

Anthony Potts
fonte
1
Eu discordo. Se sua distribuição for 50/50, você nunca usaria o índice, pois seria mais rápido fazer uma varredura na tabela. No entanto, se você tiver apenas 5, 1 valores e 1 milhão de valores 0, é muito provável que use o índice ao pesquisar 1.
Kibbee,
0

meça o tempo de resposta antes e depois e veja se vale a pena; teoricamente, deve melhorar o desempenho das consultas usando os campos indexados, mas realmente depende da distribuição de valores verdadeiro / falso e dos outros campos envolvidos nas consultas que o preocupam

Steven A. Lowe
fonte
0

Ian Boyd está correto quando diz que você não pode fazer isso por meio do Enterprise Manager para SQL 2000 (veja sua nota sobre como criá-lo através do T-SQL.

John B
fonte
0

Você precisa ser esperto aqui para consultar, você deve saber o valor de carga em sua coluna se a carga de verdadeiro está mais em seu sistema e você deseja verificar todos os valores verdadeiros, escreva sua consulta para verificar se não é falso .. isso ajudará muito , é apenas um truque.

Chetan Verma
fonte