Os índices funcionam com a cláusula “IN”
Pergunta
Se eu tiver uma consulta como:
Select EmployeeId
From Employee
Where EmployeeTypeId IN (1,2,3)
e eu tenho um índice no EmployeeTypeId
campo, o SQL Server ainda usa esse índice?
Solução
Sim, está certo.Se sua tabela de funcionários tiver 10.000 registros e apenas 5 registros tiverem EmployeetypeID em (1,2,3), provavelmente usará o índice para buscar os registros.No entanto, se descobrir que 9.000 registros têm o EmployeeIDType em (1,2,3), então provavelmente faria apenas uma varredura de tabela para obter os EmployeeIDs correspondentes, pois é mais rápido percorrer toda a tabela do que ir para cada ramo da árvore de índice e observe os registros individualmente.
O SQL Server faz muitas coisas para tentar otimizar a forma como as consultas são executadas.No entanto, às vezes não obtém a resposta certa.Se você sabe que o SQL Server não está usando o índice, observando o plano de execução no analisador de consultas, você pode informar ao mecanismo de consulta para usar um índice específico com a seguinte alteração em sua consulta.
Select EmployeeId From Employee WITH (Index(Index_EmployeeTypeId )) Where EmployeeTypeId IN (1,2,3)
Supondo que o índice que você possui no campo EmployeeTypeId seja denominado Index_EmployeeTypeId.
Outras dicas
Normalmente seria, a menos que a cláusula IN cubra muito da tabela, e então fará uma varredura na tabela.A melhor maneira de descobrir no seu caso específico seria executá-lo no analisador de consultas e verificar o plano de execução.
A menos que a tecnologia tenha melhorado de uma forma que não consigo imaginar ultimamente, a consulta "IN" mostrada produzirá um resultado que é efetivamente o OR de três conjuntos de resultados, um para cada um dos valores na lista "IN".A cláusula IN torna-se uma condição de igualdade para cada lista e usará um índice, se apropriado.No caso de IDs exclusivos e uma tabela grande o suficiente, esperaria que o otimizador usasse um índice.
No entanto, se os itens da lista não forem exclusivos, e eu acho que no exemplo um "TypeId" é uma chave estrangeira, então estou mais interessado na distribuição.Gostaria de saber se o otimizador verificará as estatísticas de cada valor da lista.Digamos que ele verifique o primeiro valor e descubra que está em 20% das linhas (de uma tabela grande o suficiente para ser importante).Provavelmente fará uma varredura na tabela.Mas será que o mesmo plano de consulta será usado para os outros dois, mesmo que sejam únicos?
Provavelmente é discutível - algo como uma tabela Employee provavelmente será pequeno o suficiente para permanecer armazenado em cache na memória e você provavelmente não notaria diferença entre isso e a recuperação indexada de qualquer maneira.
E por último, enquanto estou pregando, tome cuidado com a consulta na cláusula IN:geralmente é uma maneira rápida de fazer algo funcionar e (pelo menos para mim) pode ser uma boa maneira de expressar o requisito, mas quase sempre é melhor reformulado como uma junção.Seu otimizador pode ser inteligente o suficiente para detectar isso, mas talvez não.Se você atualmente não verifica o desempenho em relação aos volumes de dados de produção, faça-o - nestes dias de otimização baseada em custos, você não pode ter certeza do plano de consulta até ter uma carga completa e estatísticas representativas.Se não puder, então esteja preparado para surpresas na produção...
Portanto, existe o potencial de uma cláusula "in" para executar uma varredura de tabela, mas o otimizador tentará descobrir a melhor maneira de lidar com isso?
O uso de um índice não varia tanto no tipo de consulta, mas sim no tipo e na distribuição dos dados na(s) tabela(s), na atualização das estatísticas da tabela e no tipo de dados real da coluna .
Os outros postadores estão corretos ao dizer que um índice será usado em uma varredura de tabela se:
- A consulta não acessará mais do que uma certa porcentagem das linhas indexadas (digamos aproximadamente 10%, mas deve variar entre os SGBDs).
- Como alternativa, se houver muitas linhas, mas relativamente poucos valores exclusivos na coluna, também poderá ser mais rápido fazer uma varredura na tabela.
A outra variável que pode não ser tão óbvia é garantir que os tipos de dados dos valores comparados sejam os mesmos.No PostgreSQL, não acho que índices serão usados se você estiver filtrando em um float, mas sua coluna for composta de inteiros.Existem também alguns operadores que não suportam o uso de índice (novamente, no PostgreSQL, o operador ILIKE é assim).
Porém, conforme observado, sempre verifique o analisador de consultas em caso de dúvida e a documentação do seu SGBD é sua amiga.
@Mike:Obrigado pela análise detalhada.Definitivamente, há alguns pontos interessantes que você destaca aí.O exemplo que postei é um tanto trivial, mas a base da questão veio do uso do NHibernate.
Com o NHibernate, você pode escrever uma cláusula como esta:
int[] employeeIds = new int[]{1, 5, 23463, 32523};
NHibernateSession.CreateCriteria(typeof(Employee))
.Add(Restrictions.InG("EmployeeId",employeeIds))
O NHibernate então gera uma consulta que se parece com
select * from employee where employeeid in (1, 5, 23463, 32523)
Então, como você e outros apontaram, parece que haverá momentos em que um índice será usado ou uma varredura de tabela acontecerá, mas você não pode realmente determinar isso até o tempo de execução.
Select EmployeeId From Employee USE(INDEX(EmployeeTypeId))
Esta consulta pesquisará usando o índice que você criou.Funciona para mim.Por favor, tente..