Comment fonctionne la pagination avec ROW_NUMBER dans SQL Server?

13

J'ai une Employeetable qui contient un million d'enregistrements. J'ai suivi SQL pour paginer des données dans une application Web. Cela fonctionne bien. Cependant, ce que je vois comme un problème est - la table dérivée tblEmployeesélectionne tous les enregistrements de la Employeetable (pour créer les MyRowNumbervaleurs).

Je pense que cela entraîne la sélection de tous les enregistrements de la Employeetable.

Est-ce que ça marche vraiment? Ou SQL Server est-il optimisé pour sélectionner uniquement les 5 enregistrements de la Employeetable d' origine ?

DECLARE @Index INT;
DECLARE @PageSize INT;

SET @Index = 3;
SET @PageSize = 5;

SELECT *  FROM
  (SELECT  ROW_NUMBER() OVER (ORDER BY EmpID asc) as MyRowNumber,*
  FROM Employee) tblEmployee
WHERE MyRowNumber BETWEEN ( ((@Index - 1) * @PageSize )+ 1) AND @Index*@PageSize 
LCJ
la source
3
Voir sqlservercentral.com/articles/T-SQL/66030 et, plus important encore, la discussion qui a suivi .
Aaron Bertrand

Réponses:

17

Une alternative au test pourrait être:

;WITH x AS (SELECT EmpID, k = ROW_NUMBER() OVER (ORDER BY EmpID) FROM dbo.Emp)
SELECT e.columns
FROM x INNER JOIN dbo.Emp AS e
ON x.EmpID = e.EmpID
WHERE x.k BETWEEN (((@Index - 1) * @PageSize) + 1) AND @Index * @PageSize
ORDER BY ...;

Oui, vous avez frappé la table deux fois, mais dans le CTE où vous scannez la table entière, vous ne saisissez que la clé, pas TOUTES les données. Mais vous devriez vraiment regarder cet article:

http://www.sqlservercentral.com/articles/T-SQL/66030/

Et la discussion de suivi:

http://www.sqlservercentral.com/Forums/Topic672980-329-1.aspx

Dans SQL Server 2012, vous pouvez bien sûr utiliser la nouvelle syntaxe OFFSET/ FETCH NEXT:

;WITH x AS 
(
  SELECT EmpID FROM dbo.Emp
    ORDER BY EmpID
    OFFSET  @PageSize * (@Index - 1) ROWS
    FETCH NEXT @PageSize ROWS ONLY
)
SELECT e.columns
FROM x INNER JOIN dbo.Emp AS e
ON x.EmpID = e.EmpID
ORDER BY ...; 
Aaron Bertrand
la source
Il convient de noter cependant que OFFSET / FETCH NEXT n'offre aucun avantage en termes de performances par rapport à la méthode CTE
Akash
2
@Akash l'avez-vous soigneusement testé? J'ai observé des différences de plan mais je n'ai rien mentionné spécifiquement sur les performances car je n'ai effectué aucun test approfondi. Même si les performances sont les mêmes, la syntaxe est légèrement moins lourde. J'ai fait un blog à ce sujet ici: sqlblog.com/blogs/aaron_bertrand/archive/2010/11/10/…
Aaron Bertrand
1
Ah, vous avez raison, il y a une différence de performance. J'avais lu ceci: blogs.technet.com/b/dataplatforminsider/archive/2011/11/01/… où il ne mentionne aucune différence, mais je viens de voir channel9.msdn.com/posts/SQL11UPD03-REC-02 où il les montre beaucoup de différence .. (bien que dans le sous-accentuation audio la différence de performance)
Akash
2

Bien que vous ne connaissiez peut-être pas le mécanisme sous-jacent, vous pouvez le tester vous-même en comparant les performances de votre requête à: sélectionnez * dans Employé.

Les versions les plus récentes de SQL Server optimisent assez bien, mais cela peut dépendre de plusieurs facteurs.

Le fonctionnement de votre fonction ROW_NUMBER sera déterminé par la clause Order By. Dans votre exemple, la plupart penseraient que EmpID est la clé primaire.

Il y a des clauses where qui sont si complexes et / ou mal codées ou indexées, il est préférable de simplement renvoyer l'ensemble de données (c'est rare et peut être corrigé). L'utilisation de BETWEEN présente des problèmes.

Avant de supposer qu'il serait préférable de renvoyer toutes les lignes à votre application et de la laisser le comprendre, vous devez travailler sur l'optimisation de votre requête. Vérifiez les estimations. Demandez à l'analyseur de requêtes. Testez quelques alternatives.

JeffO
la source
2

Je sais que la question concerne row_number () mais je veux ajouter une nouvelle fonctionnalité de sql server 2012. Dans sql server 2012, la nouvelle fonctionnalité OFFSET Fetch a été introduite et elle est très rapide que row_number (). Je l'ai utilisé et cela me donne de bons résultats j'espère que vous remplissez également la même expérience.

J'ai trouvé un exemple sur http://blogfornet.com/2013/06/sql-server-2012-offset-use/

ce qui est utile. J'espère que cela vous aidera aussi à mettre en œuvre de nouvelles fonctionnalités ....

Sam Raj
la source
-2

Je ne pense pas qu'il évalue pour retourner toutes les lignes de la table d'origine. Le serveur SQL est optimisé. Sinon, il faudra énormément de temps pour sélectionner un million d'entrées. J'utilise actuellement cela et c'est beaucoup plus rapide que de sélectionner toutes les lignes. Donc, bien sûr, n'obtient pas toutes les lignes. Il est cependant plus lent que de simplement récupérer les cinq premières lignes, probablement en raison du temps nécessaire à la commande.

user3688672
la source
-2
DECLARE @PageIndex int;
DECLARE @PageSize int;
SET @PageIndex = 4;
SET @PageSize = 5;
;With ranked AS   --- Or you can make it a view
(
   SELECT ROW_NUMBER() OVER(ORDER BY IdentityId) AS RowNum,  *
   FROM logeventnew
)
SELECT *   --Your fields here
FROM Ranked
WHERE RowNum BETWEEN ((@PageIndex - 1) * @PageSize + 1)
    AND (@PageIndex * @PageSize)
ORDER BY IdentityId
Agnel Amodia
la source
4
Pourriez-vous développer votre réponse? La question était de savoir comment la pagination fonctionne en interne sur SQL Server - c'est-à-dire que fait le moteur de base de données pour répondre à la demande. Malheureusement, pour l'instant, votre réponse ne répond pas à la préoccupation réelle.
Mr.Brownstone