Sou um programador, lidando com uma grande mesa cujo esquema é o seguinte:
UpdateTime, PK, datetime, notnull
Name, PK, char(14), notnull
TheData, float
Há um índice agrupado emName, UpdateTime
Eu queria saber o que deveria ser mais rápido:
SELECT MAX(UpdateTime)
FROM [MyTable]
ou
SELECT MAX([UpdateTime]) AS value
from
(
SELECT [UpdateTime]
FROM [MyTable]
group by [UpdateTime]
) as t
As inserções nesta tabela estão em blocos de 50.000 linhas com a mesma data . Então pensei que agrupar por poderia facilitar o MAX
cálculo.
Em vez de tentar encontrar o máximo de 150.000 linhas, agrupar por até 3 linhas e o cálculo de MAX
seria mais rápido? Minha suposição está correta ou agrupar por também é caro?
Criei a tabela big_table de acordo com seu esquema
Em seguida, preenchi a tabela com 50.000 linhas com este código:
Usando o SSMS, testei então as duas consultas e percebi que na primeira consulta você está procurando o MAX de TheData e na segunda, o MAX de updatetime
Assim, modifiquei a primeira consulta para obter também o MAX de updatetime
Usando o Statistics Time , recupero o número de milissegundos necessários para analisar, compilar e executar cada instrução
Usando o Statistics IO , recebo informações sobre a atividade do disco
STATISTICS TIME e STATISTICS IO fornecem informações úteis. Tais como as tabelas temporárias usadas (indicadas por worktable). 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 do cache.
Em seguida, ativo o plano de execução com CTRL+M (ativa a exibição do plano de execução real) e, em seguida, executo com F5.
Isso fornecerá uma comparação de ambas as consultas.
Aqui está a saída da guia Mensagens
-- Consulta 1
Tabela 'grande_tabela'. Contagem de varredura 1, leituras lógicas 543 , leituras físicas 0, leituras antecipadas 0, leituras lógicas lob 0, leituras físicas lob 0, leituras antecipadas 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 varredura 0, leituras lógicas 0, leituras físicas 0, leituras antecipadas 0, leituras lógicas lob 0, leituras físicas lob 0, leituras antecipadas lob 0.
Tabela 'grande_tabela'. Contagem de varredura 1, leituras lógicas 543 , leituras físicas 0, leituras antecipadas 0, leituras lógicas lob 0, leituras físicas lob 0, leituras antecipadas 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 enquanto a primeira tem apenas 6ms. Você também notará que a segunda consulta resulta no uso de tabelas temporárias em tempdb, indicadas pela palavra worktable . Mesmo que todos os valores para worktable estejam em 0, o trabalho ainda foi feito em 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 do lote de 64%, enquanto a primeira custa apenas 36% do lote total, portanto, a primeira consulta requer 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, estão sendo usados para satisfazer essas consultas.
Uma observação adicional a ser lembrada ao testar é limpar o cache antes do teste, se possível. Isso ajuda a garantir que as comparações sejam precisas e isso é importante ao pensar na atividade do disco. Começo com DBCC DROPCLEANBUFFERS e DBCC FREEPROCCACHE para limpar todo o cache. Tenha cuidado, porém, para não usar esses comandos em um servidor de produção realmente em uso , pois você efetivamente forçará o servidor a ler tudo do disco para a memória.
Aqui está a documentação relevante.
O uso desses comandos pode não ser possível dependendo de como seu ambiente é usado.
Atualizado em 28/10 12h46
Foram feitas correções na imagem do plano de execução e na saída das estatísticas.
A reescrita pode ter ajudado se o SQL Server implementasse a verificação de salto de índice, mas não.
A varredura de salto 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) no meio. No seu caso, o skip-scan permitiria que o mecanismo encontrasse o
MAX(UpdateTime)
primeiroName
, pule paraMAX(UpdateTime)
o segundoName
... e assim por diante. A etapa final seria encontrar oMAX(UpdateTime)
dos candidatos 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 o skip-scan embutido seria:
Esse plano executa uma busca singleton para cada distinto
Name
e, em seguida, encontra o mais altoUpdateTime
dos candidatos. Seu desempenho em relação a uma varredura completa simples da tabela depende de quantas duplicatas existem porName
, e se as páginas tocadas pelas buscas de 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
somente: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:Este plano consome apenas alguns IOs lógicos (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 - ela simplesmente retorna uma linha do 'final' 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 quantos forem os
UpdateTime
valores exclusivos, embora toda consulta que altere dados na tabela base tenha operadores extras adicionados ao seu plano de execução para manter a exibição indexada. A consulta para encontrar oUpdateTime
valor máximo seria: