Le nouveau OFFSET ... FETCH
modèle introduit avec SQL Server 2012 offre une pagination simple et plus rapide. Pourquoi y a-t-il des différences si l'on considère que les deux formes sont sémantiquement identiques et très communes?
On pourrait supposer que l'optimiseur reconnaît les deux et les optimise (trivialement) au maximum.
Voici un cas très simple où OFFSET ... FETCH
est ~ 2x plus rapide selon l'estimation des coûts.
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
On peut faire varier ce cas de test en créant un CI sur object_id
ou en ajoutant des filtres mais il est impossible de supprimer toutes les différences de plan. OFFSET ... FETCH
est toujours plus rapide car il fait moins de travail au moment de l'exécution.
Réponses:
Les exemples de la question ne produisent pas tout à fait les mêmes résultats (l'
OFFSET
exemple a une erreur de coup par coup). Les formulaires mis à jour ci-dessous corrigent ce problème, suppriment le tri supplémentaire pour leROW_NUMBER
cas et utilisent des variables pour rendre la solution plus générale:Le
ROW_NUMBER
plan a un coût estimé à 0,0197935 :Le
OFFSET
plan a un coût estimé à 0,0196955 :Cela représente une économie de 0,000098 unité de coût estimée (bien que le
OFFSET
plan nécessite des opérateurs supplémentaires si vous souhaitez renvoyer un numéro de ligne pour chaque ligne). LeOFFSET
plan sera toujours légèrement moins cher, de manière générale, mais n'oubliez pas que les coûts estimés sont exactement cela - de vrais tests sont toujours nécessaires. La majeure partie du coût dans les deux plans est le coût du type complet de l'ensemble d'entrée, donc des index utiles bénéficieraient aux deux solutions.Lorsque des valeurs littérales constantes sont utilisées (par exemple
OFFSET 30
dans l'exemple d'origine), l'optimiseur peut utiliser un tri TopN au lieu d'un tri complet suivi d'un Top. Lorsque les lignes nécessaires du tri TopN sont un littéral constant et <= 100 (la somme deOFFSET
etFETCH
), le moteur d'exécution peut utiliser un algorithme de tri différent qui peut fonctionner plus rapidement que le tri TopN généralisé. Les trois cas ont globalement des caractéristiques de performance différentes.Quant à savoir pourquoi l'optimiseur ne transforme pas automatiquement le
ROW_NUMBER
modèle de syntaxe à utiliserOFFSET
, il existe un certain nombre de raisons:OFFSET
plan n'est pas garanti d'être meilleur dans tous les casUn exemple pour le troisième point ci-dessus se produit lorsque l'ensemble de pagination est assez large. Il peut être beaucoup plus efficace de rechercher les clés nécessaires à l' aide d'un index non cluster et de rechercher manuellement l'index cluster par rapport à l'analyse de l'index avec
OFFSET
ouROW_NUMBER
. Il y a d' autres problèmes à considérer si l'application de pagination a besoin de savoir combien de lignes ou de pages il y a au total. Il y a une autre bonne discussion des mérites relatifs des méthodes de «recherche de clé» et de «compensation» ici .Dans l'ensemble, il est probablement préférable que les utilisateurs prennent une décision éclairée de modifier leurs requêtes de pagination à utiliser
OFFSET
, le cas échéant, après des tests approfondis.la source
Avec un léger tripotage de votre requête, j'obtiens une estimation de coût égale (50/50) et des statistiques d'E / S égales:
Cela évite le tri supplémentaire qui apparaît dans votre version en triant au
r
lieu deobject_id
.la source
r
au lieu de la colonne de base, ne serait-ce que parce qu'elle correspond à ce que je ferais dans une requête non imbriquée et classer par une expression - j'utiliserais l'alias attribué à l'expression au lieu de répéter l'expression.Ils ont modifié l'optimiseur de requête pour ajouter cette fonctionnalité. Cela signifie qu'ils ont implémenté des mécanismes spécifiquement pour prendre en charge la commande offset ... fetch. En d'autres termes, pour la requête supérieure, SQL Server doit faire beaucoup plus de travail. D'où la différence dans les plans de requête.
la source