Sqlserver
 sql >> Base de données >  >> RDS >> Sqlserver

Performances SQL :WHERE et WHERE(ROW_NUMBER)

Comme d'autres l'ont déjà souligné, les requêtes renvoient des résultats différents et comparent des pommes à des oranges.

Mais la question sous-jacente demeure :qu'est-ce qui est le plus rapide :la pagination basée sur le jeu de clés ou la pagination basée sur le numéro de ligne ?

Recherche par jeu de touches

La pagination pilotée par jeu de clés repose sur la mémorisation des clés du haut et du bas de la dernière page affichée et sur la demande du jeu de lignes suivant ou précédent, en fonction du jeu de clés supérieur/dernier :

Page suivante :

select top (<pagesize>) ...
from <table>
where key > @last_key_on_current_page
order by key;

Page précédente :

select top (<pagesize>)
from <table>
where key < @first_key_on_current_page
order by key desc;

Cette approche présente deux avantages principaux par rapport à l'approche ROW_NUMBER ou à l'approche LIMIT équivalente de MySQL :

  • est correct  :contrairement à l'approche basée sur le numéro de ligne, il gère correctement les nouvelles entrées et les entrées supprimées. La dernière ligne de la page 4 n'apparaît pas comme première ligne de la page 5 simplement parce que la ligne 23 de la page 2 a été supprimée entre-temps. Les rangées ne disparaissent pas non plus mystérieusement entre les pages. Ces anomalies sont courantes avec l'approche basée sur le nombre de lignes, mais la solution basée sur l'ensemble de clés fait un bien meilleur travail pour les éviter.
  • est rapide :toutes les opérations peuvent être résolues avec un positionnement rapide des rangées suivi d'un balayage de distance dans la direction souhaitée

Cependant, cette approche est difficile à mettre en œuvre, difficile à comprendre par le programmeur moyen et non pris en charge par les outils.

Généré par le nombre de lignes

Il s'agit de l'approche courante introduite avec les requêtes Linq :

select ...
from (
  select ..., row_number() over (...) as rn
  from table)
where rn between @firstRow and @lastRow;

(ou une requête similaire utilisant TOP)Cette approche est simple à implémenter et est supporté par des outils (en particulier par les opérateurs Linq .Limit et .Take). Mais cette approche est garantie pour parcourir l'index afin de compter les lignes. Cette approche fonctionne généralement très rapidement pour la page 1 et ralentit progressivement au fur et à mesure que celle-ci passe à des numéros de page de plus en plus élevés.

En prime, avec cette solution il est très facile de changer l'ordre de tri (il suffit de changer la clause OVER).

Dans l'ensemble, étant donné la facilité des solutions basées sur ROW_NUMBER(), le support qu'elles ont de Linq, la simplicité d'utilisation d'ordres arbitraires pour les ensembles de données modérés les solutions basées sur ROW_NUMBER sont adéquates. Pour les ensembles de données volumineux et très volumineux, le ROW_NUMBER() peut entraîner de graves problèmes de performances.

Une autre chose à considérer est que, souvent, il existe un modèle d'accès défini. Souvent, les premières pages sont chaudes et les pages après 10 ne sont pratiquement jamais consultées (par exemple, les messages les plus récents). Dans ce cas, la pénalité qui se produit avec ROW_NUMBER() pour visiter les pages inférieures (pages d'affichage pour lesquelles un grand nombre de lignes doivent être comptées pour obtenir la ligne de résultat de départ) peut être bien ignorée.

Et enfin, la pagination du jeu de clés est idéale pour la navigation dans le dictionnaire, ce que ROW_NUMBER() ne peut pas gérer facilement. La navigation dans le dictionnaire est l'endroit où, au lieu d'utiliser le numéro de page, les utilisateurs peuvent naviguer vers certaines ancres, comme les lettres de l'alphabet. Exemple typique étant un contact Rolodex comme la barre latérale, vous cliquez sur M et vous accédez au premier nom de client qui commence par M.