O novo OFFSET ... FETCH
modelo introduzido no 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 assumir que o otimizador reconhece os dois e os otimiza (trivialmente) ao máximo.
Aqui está um caso muito simples em que 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 esse 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 no tempo de execução.
Respostas:
Os exemplos na pergunta não produzem os mesmos resultados (o
OFFSET
exemplo tem um erro de um por um). 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 representa uma economia de 0,000098 unidades de custo estimado (embora o
OFFSET
plano exija operadores adicionais se você desejar retornar um número de linha para cada linha). OOFFSET
plano ainda será um pouco mais barato, de um modo 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 de todo o tipo de conjunto de entradas; 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 um literal constante e <= 100 (a soma deOFFSET
eFETCH
) o mecanismo de execução pode usar um algoritmo de classificação diferente, que pode executar mais rapidamente que a classificação TopN generalizada. Todos os três casos têm características de desempenho diferentes em geral.Quanto ao motivo pelo qual o otimizador não transforma automaticamente o
ROW_NUMBER
padrão de sintaxe para usoOFFSET
, há vários motivos:OFFSET
é garantido que o plano seja melhor em todos os casosUm exemplo para o terceiro ponto acima ocorre onde o conjunto de paginação é bastante amplo. Pode ser muito mais eficiente procurar as chaves necessárias usando um índice não clusterizado e procurar manualmente o índice clusterizado em comparação com a varredura do índice com
OFFSET
ouROW_NUMBER
. Existem problemas adicionais a serem considerados 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 'busca por chave' e 'compensação' 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.fonte
Com uma pequena brincadeira na sua consulta, recebo uma estimativa de custo igual (50/50) e estatísticas de IO iguais:
Isso evita a classificação adicional que aparece na sua versão, classificando em
r
vez deobject_id
.fonte
r
vez da coluna base, apenas porque corresponde ao que eu faria em uma consulta não aninhada e ordenar por uma expressão - eu usaria o alias atribuído à expressão em vez de repetir a expressão.Eles modificaram o otimizador de consulta para incluir esse recurso. Isso significa que implementaram mecanismos especificamente para dar suporte ao comando offset ... fetch. Em outras palavras, para a consulta superior, o SQL Server precisa fazer muito mais trabalho. Assim, a diferença nos planos de consulta.
fonte