Ao executar uma consulta, o otimizador do SQL Server tenta encontrar o melhor plano de consulta com base nos índices existentes e nas estatísticas mais recentes disponíveis por um tempo razoável, é claro, se esse plano ainda não estiver armazenado no cache do servidor. Se não, a consulta é executada de acordo com este plano e o plano é armazenado no cache do servidor. Se o plano já foi construído para esta consulta, a consulta é executada de acordo com o plano existente.
Estamos interessados na seguinte questão:
Durante a compilação de um plano de consulta, ao classificar possíveis índices, se o servidor não encontrar o melhor índice, o índice ausente é marcado no plano de consulta e o servidor mantém estatísticas sobre tais índices:quantas vezes o servidor usaria esse índice e quanto custaria essa consulta.
Neste artigo, vamos analisar esses índices ausentes – como lidar com eles.
Vamos considerar isso em um exemplo particular. Crie algumas tabelas em nosso banco de dados em um servidor local e de teste:
[expandir título =”Código”]
if object_id ('orders_detail') is not null drop table orders_detail; if object_id('orders') is not null drop table orders; go create table orders ( id int identity primary key, dt datetime, seller nvarchar(50) ) create table orders_detail ( id int identity primary key, order_id int foreign key references orders(id), product nvarchar(30), qty int, price money, cost as qty * price ) go with cte as ( select 1 id union all select id+1 from cte where id < 20000 ) insert orders select dt, seller from ( select dateadd(day,abs(convert(int,convert(binary(4),newid()))%365),'2016-01-01') dt, abs(convert(int,convert(binary(4),newid()))%5)+1 seller_id from cte ) c left join ( values (1,'John'), (2,'Mike'), (3,'Ann'), (4,'Alice'), (5,'George') ) t (id,seller) on t.id = c.seller_id option(maxrecursion 0) insert orders_detail select order_id, product, qty, price from ( select o.id as order_id, abs(convert(int,convert(binary(4),newid()))%5)+1 product_id, abs(convert(int,convert(binary(4),newid()))%20)+1 qty from orders o cross join ( select top(abs(convert(int,convert(binary(4),newid()))%5)+1) * from ( values (1),(2),(3),(4),(5),(6),(7),(8) ) n(num) ) n ) c left join ( values (1,'Sugar', 50), (2,'Milk', 80), (3,'Bread', 20), (4,'Pasta', 40), (5,'Beer', 100) ) t (id,product, price) on t.id = c.product_id go
[/expandir]
A estrutura é simples e consiste em duas tabelas. A primeira tabela é chamada de pedidos com campos como identificador, data de venda e vendedor. A segunda são os detalhes do pedido, onde algumas mercadorias são especificadas com preço e quantidade.
Veja uma consulta simples e seu plano:
select count(*) from orders o join orders_detail d on o.id = d.order_id where d.cost > 1800 go
Podemos ver uma dica verde sobre o índice ausente na exibição gráfica do plano de consulta. Se você clicar com o botão direito do mouse e selecionar “Missing Index Details ..”, haverá o texto do índice sugerido. A única coisa a fazer é remover os comentários no texto e dar um nome ao índice. O script está pronto para ser executado.
Não construiremos o índice que recebemos da dica fornecida pelo SSMS. Em vez disso, veremos se esse índice será recomendado por visualizações dinâmicas vinculadas a índices ausentes. As vistas são as seguintes:
select * from sys.dm_db_missing_index_group_stats select * from sys.dm_db_missing_index_details select * from sys.dm_db_missing_index_groups
Como podemos ver, existem algumas estatísticas sobre índices ausentes na primeira visualização:
- Quantas vezes uma pesquisa seria realizada se o índice sugerido existisse?
- Quantas vezes uma verificação seria executada se o índice sugerido existisse?
- Última data e hora em que usamos o índice
- O custo real atual do plano de consulta sem o índice sugerido.
A segunda visão é o corpo do índice:
- Banco de dados
- Objeto/tabela
- Colunas classificadas
- Colunas adicionadas para aumentar a cobertura do índice
A terceira vista é a combinação da primeira e da segunda vista.
Assim, não é difícil obter um script que gere um script para criar índices ausentes dessas visualizações dinâmicas. O roteiro é o seguinte:
[expandir título=”Código”]
with igs as ( select * from sys.dm_db_missing_index_group_stats ) , igd as ( select *, isnull(equality_columns,'')+','+isnull(inequality_columns,'') as ix_col from sys.dm_db_missing_index_details ) select --top(10) 'use ['+db_name(igd.database_id)+']; create index ['+'ix_'+replace(convert(varchar(10),getdate(),120),'-','')+'_'+convert(varchar,igs.group_handle)+'] on '+ igd.[statement]+'('+ case when left(ix_col,1)=',' then stuff(ix_col,1,1,'') when right(ix_col,1)=',' then reverse(stuff(reverse(ix_col),1,1,'')) else ix_col end +') '+isnull('include('+igd.included_columns+')','')+' with(online=on, maxdop=0) go ' command ,igs.user_seeks ,igs.user_scans ,igs.avg_total_user_cost from igs join sys.dm_db_missing_index_groups link on link.index_group_handle = igs.group_handle join igd on link.index_handle = igd.index_handle where igd.database_id = db_id() order by igs.avg_total_user_cost * igs.user_seeks desc
[/expandir]
Para eficiência do índice, os índices ausentes são gerados. A solução perfeita é quando esse conjunto de resultados não retorna nada. Em nosso exemplo, o conjunto de resultados retornará pelo menos um índice:
Quando não há tempo e você não tem vontade de lidar com os bugs do cliente, executei a consulta, copiei a primeira coluna e executei no servidor. Depois disso, tudo funcionou bem.
Eu recomendo tratar as informações sobre esses índices de forma consciente. Por exemplo, se o sistema recomendar os seguintes índices:
create index ix_01 on tbl1 (a,b) include (c) create index ix_02 on tbl1 (a,b) include (d) create index ix_03 on tbl1 (a)
E esses índices são usados para a pesquisa, é bastante óbvio que é mais lógico substituir esses índices por um que cubra todos os três sugeridos:
create index ix_1 on tbl1 (a,b) include (c,d)
Assim, fazemos uma revisão dos índices ausentes antes de implantá-los no servidor de produção. Embora…. Novamente, por exemplo, implantei os índices perdidos no servidor TFS, aumentando assim o desempenho geral. Levou um tempo mínimo para realizar essa otimização. No entanto, ao mudar do TFS 2015 para o TFS 2017, enfrentei o problema de não haver atualização devido a esses novos índices. No entanto, eles podem ser facilmente encontrados pela máscara
select * from sys.indexes where name like 'ix[_]2017%'
Ferramenta útil:
dbForge Index Manager – suplemento SSMS útil para analisar o status de índices SQL e corrigir problemas com fragmentação de índice.