Criei a tabela big_table de acordo com seu esquema
create table big_table
(
updatetime datetime not null,
name char(14) not null,
TheData float,
primary key(Name,updatetime)
)
Em seguida, preenchi a tabela com 50.000 linhas com este código:
DECLARE @ROWNUM as bigint = 1
WHILE(1=1)
BEGIN
set @rownum = @ROWNUM + 1
insert into big_table values(getdate(),'name' + cast(@rownum as CHAR), cast(@rownum as float))
if @ROWNUM > 50000
BREAK;
END
Usando o SSMS, testei as duas consultas e percebi que, na primeira consulta, você está procurando o MAX do TheData e, na segunda, o MAX do updatetime
Assim, modifiquei a primeira consulta para obter também o MAX do tempo de atualização
set statistics time on -- execution time
set statistics io on -- io stats (how many pages read, temp tables)
-- query 1
SELECT MAX([UpdateTime])
FROM big_table
-- query 2
SELECT MAX([UpdateTime]) AS value
from
(
SELECT [UpdateTime]
FROM big_table
group by [UpdateTime]
) as t
set statistics time off
set statistics io off
Usando o tempo de estatística , recebo de volta o número de milissegundos necessário para analisar, compilar e executar cada instrução
Usando o Statistics IO , recebo informações sobre a atividade do disco
ESTATÍSTICAS TEMPO e ESTATÍSTICAS IO fornecem informações úteis. Como as tabelas temporárias usadas (indicadas pela mesa de trabalho). Além disso, quantas páginas lógicas lidas foram lidas, o que indica o número de páginas do banco de dados lidas no cache.
Ativei o plano de execução com CTRL + M (ativa o plano de execução atual) e depois executo com F5.
Isso fornecerá uma comparação das duas consultas.
Aqui está a saída da guia Mensagens
- Consulta 1
Tabela 'big_table'. Contagem de varreduras 1, leituras lógicas 543 , leituras físicas 0, leituras de read-ahead 0, leituras lógicas de lob 0, leituras físicas de lob 0, leituras físicas de lob 0, leituras de read-ahead de lob 0.
Tempos de execução do SQL Server:
tempo de CPU = 16 ms, tempo decorrido = 6 ms .
- Consulta 2
Tabela ' Mesa de trabalho '. Contagem de varreduras 0, leituras lógicas 0, leituras físicas 0, leituras de leitura antecipada 0, leituras lógicas de lob 0, leituras físicas de lob 0, leituras físicas de lob 0, leituras de leitura antecipada de lob 0.
Tabela 'big_table'. Contagem de varreduras 1, leituras lógicas 543 , leituras físicas 0, leituras de read-ahead 0, leituras lógicas de lob 0, leituras físicas de lob 0, leituras físicas de lob 0, leituras de read-ahead de lob 0.
Tempos de execução do SQL Server:
tempo de CPU = 0 ms, tempo decorrido = 35 ms .
Ambas as consultas resultam em 543 leituras lógicas, mas a segunda consulta tem um tempo decorrido de 35ms, sendo que a primeira possui apenas 6ms. Você também notará que a segunda consulta resulta no uso de tabelas temporárias no tempdb, indicadas pela palavra tabela de trabalho . Embora todos os valores da tabela de trabalho estejam em 0, o trabalho ainda foi realizado no tempdb.
Depois, há a saída da guia Plano de execução real ao lado da guia Mensagens
De acordo com o plano de execução fornecido pelo MSSQL, a segunda consulta que você forneceu tem um custo total de lote de 64%, enquanto a primeira custa apenas 36% do lote total, portanto, a primeira consulta exige menos trabalho.
Usando o SSMS, você pode testar e comparar suas consultas e descobrir exatamente como o MSSQL está analisando suas consultas e quais objetos: tabelas, índices e / ou estatísticas, se houver alguma que esteja sendo usada para satisfazer essas consultas.
Uma observação adicional a ter em mente quando o teste estiver limpando o cache antes do teste, se possível. Isso ajuda a garantir que as comparações sejam precisas e isso é importante quando se pensa em atividade do disco. Começo com DBCC DROPCLEANBUFFERS e DBCC FREEPROCCACHE para limpar todo o cache. Cuidado para não usar esses comandos em um servidor de produção realmente em uso, pois você forçará efetivamente o servidor a ler tudo do disco para a memória.
Aqui está a documentação relevante.
- Limpe o cache do plano com DBCC FREEPROCCACHE
- Limpe tudo do buffer pool com DBCC DROPCLEANBUFFERS
O uso desses comandos pode não ser possível, dependendo de como seu ambiente é usado.
Atualizado 10/28 12:46 pm
Correções na imagem do plano de execução e na saída de estatísticas.
getdate()
para fora do loopA reescrita pode ter ajudado se o SQL Server implementou a pular varredura de índice, mas isso não acontece.
A pular varredura de índice permite que um mecanismo de banco de dados busque o próximo valor de índice diferente em vez de varrer todas as duplicatas (ou subchaves irrelevantes) intermediárias. No seu caso, o skip-scan permitiria ao mecanismo encontrar
MAX(UpdateTime)
o primeiroName
,MAX(UpdateTime)
o segundo no segundoName
... e assim por diante. A etapa final seria encontrar osMAX(UpdateTime)
candidatos a um por nome.Você pode simular isso até certo ponto, usando um CTE recursivo, mas é um pouco confuso e não é tão eficiente quanto seria o skip-scan interno:
Esse plano realiza uma busca única para cada distinto e
Name
, em seguida, encontra o mais altoUpdateTime
dos candidatos. Seu desempenho em relação a uma verificação completa simples da tabela depende de quantas duplicatas existem porName
e se as páginas tocadas pelas buscas singleton estão na memória ou não.Soluções alternativas
Se você conseguir criar um novo índice nesta tabela, uma boa opção para esta consulta seria um índice
UpdateTime
sozinho:Este índice permitirá que o mecanismo de execução encontre o mais alto
UpdateTime
com uma busca singleton até o final da árvore b do índice:Esse plano consome apenas algumas E / S lógicas (para navegar nos níveis da árvore b) e é concluído imediatamente. Observe que a Varredura de índice no plano não é uma varredura completa do novo índice - apenas retorna uma linha da 'extremidade' do índice.
Se você não deseja criar um novo índice completo na tabela, considere uma exibição indexada contendo apenas os
UpdateTime
valores exclusivos :Isso tem a vantagem de criar apenas uma estrutura com tantas linhas quanto houver
UpdateTime
valores exclusivos , embora toda consulta que altere dados na tabela base tenha operadores adicionais adicionados ao seu plano de execução para manter a exibição indexada. A consulta para encontrar oUpdateTime
valor máximo seria:fonte