O novo OFFSET ... FETCH
modelo introduzido com o SQL Server 2012 oferece paginação simples e mais rápida. Por que existem diferenças considerando que as duas formas são semanticamente idênticas e muito comuns?
Alguém poderia supor que o otimizador reconhece ambos e os otimiza (trivialmente) ao máximo.
Aqui está um caso muito simples onde OFFSET ... FETCH
é ~2x mais rápido de acordo com a estimativa de custo.
SELECT * INTO #objects FROM sys.objects
SELECT *
FROM (
SELECT *, ROW_NUMBER() OVER (ORDER BY object_id) r
FROM #objects
) x
WHERE r >= 30 AND r < (30 + 10)
ORDER BY object_id
SELECT *
FROM #objects
ORDER BY object_id
OFFSET 30 ROWS FETCH NEXT 10 ROWS ONLY
Pode-se variar este caso de teste criando um IC object_id
ou adicionando filtros, mas é impossível remover todas as diferenças de plano. OFFSET ... FETCH
é sempre mais rápido porque faz menos trabalho em tempo de execução.
Os exemplos na questão não produzem exatamente os mesmos resultados (o
OFFSET
exemplo tem um erro de diferença). Os formulários atualizados abaixo corrigem esse problema, removem a classificação extra para oROW_NUMBER
caso e usam variáveis para tornar a solução mais geral:O
ROW_NUMBER
plano tem um custo estimado de 0,0197935 :O
OFFSET
plano tem um custo estimado de 0,0196955 :Isso é uma economia de 0,000098 unidades de custo estimado (embora o
OFFSET
plano exija operadores extras se você quiser retornar um número de linha para cada linha). OOFFSET
plano ainda será um pouco mais barato, em geral, mas lembre-se de que os custos estimados são exatamente isso - testes reais ainda são necessários. A maior parte do custo em ambos os planos é o custo da classificação completa do conjunto de entrada, portanto, índices úteis beneficiariam ambas as soluções.Onde valores literais constantes são usados (por exemplo
OFFSET 30
, no exemplo original), o otimizador pode usar uma classificação TopN em vez de uma classificação completa seguida por um Top. Quando as linhas necessárias da Classificação TopN são literais constantes e <= 100 (a soma deOFFSET
eFETCH
), o mecanismo de execução pode usar um algoritmo de classificação diferente que pode ser executado mais rapidamente do que a classificação TopN generalizada. Todos os três casos têm diferentes características de desempenho em geral.Existem vários motivos para o motivo pelo qual o otimizador não transforma automaticamente o
ROW_NUMBER
padrão de sintaxe a ser usadoOFFSET
:OFFSET
plano não é garantido para ser melhor em todos os casosUm exemplo para o terceiro ponto acima ocorre quando o conjunto de paginação é bastante amplo. Pode ser muito mais eficiente buscar as chaves necessárias usando um índice não clusterizado e pesquisar manualmente no índice clusterizado em comparação com a varredura do índice com
OFFSET
ouROW_NUMBER
. Há questões adicionais a serem consideradas se o aplicativo de paginação precisar saber quantas linhas ou páginas existem no total. Há outra boa discussão sobre os méritos relativos dos métodos 'key seek' e 'offset' aqui .No geral, provavelmente é melhor que as pessoas tomem uma decisão informada de alterar suas consultas de paginação para usar
OFFSET
, se apropriado, após testes completos.Com uma ligeira alteração em sua consulta, obtenho uma estimativa de custo igual (50/50) e estatísticas de IO iguais:
Isso evita a classificação adicional que aparece em sua versão, classificando em
r
vez deobject_id
.Eles modificaram o otimizador de consulta para adicionar esse recurso. Isso significa que implementaram mecanismos especificamente para oferecer suporte ao comando offset ... fetch. Em outras palavras, para a consulta principal, o SQL Server precisa fazer muito mais trabalho. Daí a diferença nos planos de consulta.