Estou comparando duas consultas no SQL Server 2012. O objetivo é usar todas as informações pertinentes disponíveis no otimizador de consultas ao escolher a melhor consulta. Ambas as consultas produzem os mesmos resultados; o número máximo de pedidos para todos os clientes.
A limpeza do buffer pool foi feita antes de executar cada consulta com FREEPROCCACHE e DROPCLEANBUFFERS
Usando as informações fornecidas abaixo, qual consulta é a melhor escolha?
-- Query 1 - return the maximum order id for a customer
SELECT orderid, custid
FROM Sales.Orders AS O1
WHERE orderid = (SELECT MAX(O2.orderid)
FROM Sales.Orders AS O2
WHERE O2.custid = O1.custid);
-- Query 2 - return the maximum order id for a customer
SELECT MAX(orderid), custid
FROM Sales.Orders AS O1
group by custid
order by custid
TEMPO ESTATÍSTICO
Consulta 1 TEMPO ESTATÍSTICO: tempo de CPU = 0ms, tempo decorrido = 24 ms
Consulta 2 TEMPO ESTATÍSTICO: tempo de CPU = 0 ms, tempo decorrido = 23 ms
ESTATÍSTICAS IO
Consulta 1 ESTATÍSTICA IO: Tabela 'Pedidos'. Contagem de varreduras 1, leituras lógicas 5, leituras físicas 2, leituras antecipadas 0, leituras lógicas lob 0, leituras lógicas lob 0, leituras físicas lob 0, leituras antecipadas lob.
Consulta 2 ESTATÍSTICAS IO: Tabela 'Pedidos'. Contagem de varreduras 1, leituras lógicas 4, leituras físicas 1, leituras antecipadas 8, leituras lógicas 0, lob leituras físicas 0, leituras físicas lob 0, leituras antecipadas lob 0.
Planos de Execução
Propriedades SELECT Consulta 1
Propriedades SELECT Consulta 2
Conclusões:
Consulta 1
- Custo do lote 48%
- Leituras lógicas 5
- Leituras físicas 2
- Leituras antecipadas: 0
- Tempo de CPU: 0ms
- Tempo decorrido 24ms
- Custo estimado da subárvore: 0.0050276
- CompileCPU: 2
- CompileMemory: 384
- CompileTime: 2
Consulta 2
- Custo do lote 52%
- Leituras lógicas 4
- Listas Physcial 1
- Leituras de leitura antecipada: 8
- Tempo de CPU 0
- Tempo decorrido 23ms
- Custo estimado da subárvore: 0.0054782
- CompileCPU: 0
- CompileMemory: 192
- CompileTime: 0
Pessoalmente, embora a Consulta 2 tenha um custo de lote mais alto, de acordo com o plano gráfico, acho mais eficaz que a Consulta 1. Isso porque a consulta 2 requer leituras menos lógicas, tem um tempo decorrido um pouco menor, os valores compilecpu, compilememory e compiletime são mais baixo. as leituras de read-ahead são 8 para a consulta 2 e 0 para a consulta 1.
Atualização 12:03
Definição de índice clusterizado
ALTER TABLE [Sales].[Orders] ADD CONSTRAINT [PK_Orders] PRIMARY KEY CLUSTERED
(
[orderid] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
GO
Índice não clusterizado idx_nc_custid
CREATE NONCLUSTERED INDEX [idx_nc_custid] ON [Sales].[Orders]
(
[custid] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
GO
fonte
Respostas:
Adoro sua abordagem para uma consideração cuidadosa para consultar opções de ajuste e revisão. Eu gostaria que mais desenvolvedores fizessem isso. Uma precaução seria: sempre teste com muitas linhas, observando as leituras lógicas, esta é uma tabela pequena. Tente gerar uma carga de amostra e execute a consulta novamente. Um pequeno problema: na sua consulta superior, você não está solicitando um pedido; na consulta inferior, você é. Você deve compará-los e contrastá-los com os pedidos.
Acabei de criar rapidamente uma tabela SalesOrders com 200.000 pedidos de vendas - ainda não muito grande com a imaginação. E executou as consultas com o ORDER BY em cada um. Eu também brinquei um pouco com os índices.
Sem índice clusterizado no OrderID, apenas um índice não clusterizado no CustID. A segunda consulta teve um desempenho superior. Especialmente com o pedido incluído em cada um. Havia duas vezes mais leituras na primeira consulta do que na segunda e as porcentagens de custo foram de 67% / 33% entre as consultas.
Com um índice em cluster no OrderID e um índice não em cluster apenas no CustID. Eles executaram em uma velocidade semelhante e no mesmo número exato de leituras.
Então, sugiro que você aumente o número de linhas e faça mais alguns testes. Mas minha análise final sobre suas consultas -
Você pode achar que eles se comportam de maneira mais semelhante ao que você imagina quando aumenta as linhas; portanto, lembre-se dessa advertência e teste dessa maneira.
Se tudo o que você deseja retornar é o número máximo de pedidos para cada cliente, e você deseja determinar que, pelo número de pedidos, o maior número de pedidos, a segunda consulta dessas duas é a melhor maneira de sair da minha cabeça - é um pouco mais simples e, embora um pouco mais caro, com base no custo da subárvore, é uma declaração mais rápida e fácil de decifrar. Se você pretende adicionar outras colunas ao seu conjunto de resultados algum dia? Em seguida, a primeira consulta permite que você faça isso.
Atualizado: um dos seus comentários em sua pergunta foi:
Mas a melhor maneira de fazer esse teste com mais dados - sempre garante que você tenha dados consistentes com a produção e a produção futura esperada. Os planos de consulta começam a procurar dados quando você fornece mais linhas para as tabelas e tenta manter a distribuição que você esperaria na produção. E preste atenção em coisas como incluir Order By ou não, aqui não acho que faça muita diferença no final, mas ainda vale a pena investigar.
Sua abordagem para comparar esse nível de detalhe e dados é boa. Os custos das subárvores são arbitrários e sem sentido, principalmente, mas ainda valem pelo menos uma comparação entre edições / alterações ou mesmo entre consultas. Observar as estatísticas de tempo e o pedido de veiculação é bastante importante, assim como observar o plano para qualquer coisa que pareça inadequada para o tamanho dos dados com os quais você está trabalhando e o que está tentando fazer.
fonte