web-dev-qa-db-fra.com

Comment fonctionne la pagination avec ROW_NUMBER dans SQL Server?

J'ai une table Employee 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 tblEmployee sélectionne tous les enregistrements de la table Employee (pour créer les valeurs MyRowNumber).

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

Est-ce que ça marche vraiment? Ou SQL Server est-il optimisé pour sélectionner uniquement les 5 enregistrements de la table Employee 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 
13
LCJ

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 frappez 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 le nouveau OFFSET/FETCH NEXT syntaxe:

;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 ...; 

J'ai également blogué à ce sujet plus en détail ici:

18
Aaron Bertrand

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

Les versions les plus récentes de SQL Server font un assez bon travail d'optimisation, 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 qui sont si complexes et/ou mal codées ou indexées, il vaut peut-être mieux renvoyer tout 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 comprendre, vous devez travailler sur l'optimisation de votre requête. Vérifiez les estimations. Demandez à l'analyseur de requêtes. Testez quelques alternatives.

2
JeffO

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 ....

2
Sam Raj