Sqlserver
 sql >> Base de Dados >  >> RDS >> Sqlserver

Desempenho do SQL:WHERE vs WHERE(ROW_NUMBER)


Como outros já apontaram, as consultas retornam resultados diferentes e estão comparando maçãs com laranjas.

Mas a questão subjacente permanece:o que é mais rápido:paginação orientada por conjunto de chaves ou paginação orientada por número de linha?

Paginação de conjunto de chaves


A paginação orientada por conjunto de chaves depende de lembrar as teclas superior e inferior da última página exibida e solicitar o conjunto de linhas seguinte ou anterior, com base no conjunto de chaves superior/último:

Próxima página:
select top (<pagesize>) ...
from <table>
where key > @last_key_on_current_page
order by key;

Página anterior:
select top (<pagesize>)
from <table>
where key < @first_key_on_current_page
order by key desc;

Essa abordagem tem duas vantagens principais sobre a abordagem ROW_NUMBER ou sobre a abordagem LIMIT equivalente do MySQL:
  • está correto :ao contrário da abordagem baseada em número de linha, ele lida corretamente com novas entradas e entradas excluídas. A última linha da página 4 não aparece como primeira linha da página 5 apenas porque a linha 23 da página 2 foi excluída nesse meio tempo. Nem as linhas desaparecem misteriosamente entre as páginas. Essas anomalias são comuns com a abordagem baseada em número_linha, mas a solução baseada em conjunto de chaves faz um trabalho muito melhor para evitá-las.
  • é rápido :todas as operações podem ser resolvidas com um posicionamento de linha rápido seguido por uma varredura de intervalo na direção desejada

No entanto, essa abordagem é difícil para implementar, difícil de entender pelo programador médio e não suportado pelas ferramentas.

Acionado por número de linha


Esta é a abordagem comum introduzida com consultas Linq:
select ...
from (
  select ..., row_number() over (...) as rn
  from table)
where rn between @firstRow and @lastRow;

(ou uma consulta semelhante usando TOP) Essa abordagem é fácil para implementar e é suportado por ferramentas (especificamente pelos operadores Linq .Limit e .Take). Mas essa abordagem é garantida para varrer o índice para contar as linhas. Essa abordagem geralmente funciona muito rápido para a página 1 e diminui gradualmente à medida que a página vai para números de página cada vez mais altos.

Como bônus, com esta solução é muito fácil alterar a ordem de classificação (basta alterar a cláusula OVER).

No geral, dada a facilidade das soluções baseadas em ROW_NUMBER(), o suporte que elas têm do Linq, a simplicidade de usar ordens arbitrárias para conjuntos de dados moderados as soluções baseadas em ROW_NUMBER são adequadas. Para conjuntos de dados grandes e muito grandes, ROW_NUMBER() pode ocorrer sérios problemas de desempenho.

Uma outra coisa a considerar é que muitas vezes há um padrão definido de acesso. Muitas vezes as primeiras páginas são quentes e as páginas depois de 10 são basicamente nunca visualizadas (por exemplo, posts mais recentes). Nesse caso, a penalidade que ocorre com ROW_NUMBER() por visitar as páginas inferiores (páginas de exibição para as quais um grande número de linhas deve ser contado para obter a linha de resultado inicial) pode ser ignorada.

E, finalmente, a paginação do conjunto de chaves é ótima para navegação de dicionário, que ROW_NUMBER() não pode acomodar facilmente. A navegação do dicionário é onde, em vez de usar o número da página, os usuários podem navegar para determinadas âncoras, como letras do alfabeto. Exemplo típico sendo um contato Rolodex como barra lateral, você clica em M e navega para o primeiro nome do cliente que começa com M.