Je veux faire une requête de base de données avec pagination. J'ai donc utilisé une expression de table commune et une fonction classée pour y parvenir. Regardez l'exemple ci-dessous.
declare @table table (name varchar(30));
insert into @table values ('Jeanna Hackman');
insert into @table values ('Han Fackler');
insert into @table values ('Tiera Wetherbee');
insert into @table values ('Hilario Mccray');
insert into @table values ('Mariela Edinger');
insert into @table values ('Darla Tremble');
insert into @table values ('Mammie Cicero');
insert into @table values ('Raisa Harbour');
insert into @table values ('Nicholas Blass');
insert into @table values ('Heather Hayashi');
declare @pagenumber int = 2;
declare @pagesize int = 3;
declare @total int;
with query as
(
select name, ROW_NUMBER() OVER(ORDER BY name ASC) as line from @table
)
select top (@pagesize) name from query
where line > (@pagenumber - 1) * @pagesize
Ici, je peux spécifier les variables @pagesize et @pagenumber pour me donner juste les enregistrements que je veux. Cependant, cet exemple (qui provient d'une procédure stockée) est utilisé pour créer une pagination de grille dans une application Web. Cette application Web nécessite d'afficher les numéros de page. Par exemple, si un a 12 enregistrements dans la base de données et la taille de la page est de 3, alors je devrai montrer 4 liens, chacun représentant une page.
Mais je ne peux pas faire cela sans savoir combien d'enregistrements sont là, et cet exemple me donne juste le sous-ensemble d'enregistrements.
Ensuite, j'ai changé la procédure stockée pour retourner le nombre (*).
declare @pagenumber int = 2;
declare @pagesize int = 3;
declare @total int;
with query as
(
select name, ROW_NUMBER() OVER(ORDER BY name ASC) as line, total = count(*) over()from @table
)
select top (@pagesize) name, total from query
where line > (@pagenumber - 1) * @pagesize
Ainsi, avec chaque ligne, il affichera le nombre total d'enregistrements. Mais je n'aimais pas ça.
Ma question est de savoir s'il existe un meilleur moyen (performance) de le faire, peut-être en définissant la variable @total sans renvoyer ces informations dans SELECT. Ou cette colonne totale est-elle quelque chose qui ne nuira pas trop aux performances?
Merci
En supposant que vous utilisez MSSQL 2012, vous pouvez utiliser Offset and Fetch
qui nettoie considérablement la pagination côté serveur. Nous avons constaté que les performances sont bonnes et, dans la plupart des cas, meilleures. En ce qui concerne le nombre total de colonnes, utilisez simplement la fonction de fenêtre ci-dessous en ligne ... elle n'inclura pas les limites imposées par 'offset' et 'fetch'.
Pour Row_Number, vous pouvez utiliser les fonctions de la fenêtre comme vous l'avez fait, mais je vous recommande de calculer ce côté client comme (pagenumber * pagesize + resultsetRowNumber), donc si vous êtes sur la 5e page de 10 résultats et sur la troisième ligne vous afficherait la ligne 53.
Appliqué à une table Commandes avec environ 2 millions de commandes, j'ai trouvé ce qui suit:
VERSION RAPIDE
Cela a duré moins d'une seconde. La bonne chose à ce sujet est que vous pouvez effectuer votre filtrage dans l'expression de table commune une fois et cela s'applique à la fois au processus de pagination et au nombre. Lorsque vous avez de nombreux prédicats dans la clause where, cela simplifie les choses.
declare @skipRows int = 25,
@takeRows int = 100,
@count int = 0
;WITH Orders_cte AS (
SELECT OrderID
FROM dbo.Orders
)
SELECT
OrderID,
tCountOrders.CountOrders AS TotalRows
FROM Orders_cte
CROSS JOIN (SELECT Count(*) AS CountOrders FROM Orders_cte) AS tCountOrders
ORDER BY OrderID
OFFSET @skipRows ROWS
FETCH NEXT @takeRows ROWS ONLY;
VERSION LENTE
Cela a pris environ 10 secondes, et c'est le comte (*) qui a causé la lenteur. Je suis surpris que ce soit si lent, mais je soupçonne qu'il s'agit simplement de calculer le total pour chaque ligne. C'est très propre cependant.
declare @skipRows int = 25,
@takeRows int = 100,
@count int = 0
SELECT
OrderID,
Count(*) Over() AS TotalRows
FROM Location.Orders
ORDER BY OrderID
OFFSET @skipRows ROWS
FETCH NEXT @takeRows ROWS ONLY;
CONCLUSION
Nous avons déjà suivi ce processus d'optimisation des performances et nous avons constaté qu'il dépendait de la requête, des prédicats utilisés et des index impliqués. Par exemple, la seconde fois que nous avons introduit une vue, elle a été supprimée, nous interrogeons donc la table de base, puis joignons la vue (qui inclut la table de base) et elle fonctionne très bien.
Je suggérerais d'avoir quelques stratégies simples et de les appliquer à des requêtes à forte valeur ajoutée.
DECLARE @pageNumber INT = 1 ,
@RowsPerPage INT = 20
SELECT *
FROM TableName
ORDER BY Id
OFFSET ( ( @pageNumber - 1 ) * @RowsPerPage ) ROWS
FETCH NEXT @RowsPerPage ROWS ONLY;
@pagenumber=5
@pagesize=5
Créez une expression de table commune et écrivez une logique comme celle-ci
Between ((@pagenumber-1)*(@pagesize))+1 and (@pagenumber *@pagesize)
Et si vous calculez le nombre à l'avance?
declare @pagenumber int = 2;
declare @pagesize int = 3;
declare @total int;
SELECT @total = count(*)
FROM @table
with query as
(
select name, ROW_NUMBER() OVER(ORDER BY name ASC) as line from @table
)
select top (@pagesize) name, @total total from query
where line > (@pagenumber - 1) * @pagesize
Une autre façon consiste à calculer max(line)
. Vérifiez le lien
Renvoyer le nombre total d'enregistrements de SQL Server lors de l'utilisation de ROW_NUMBER
UPD:
Pour une requête unique, vérifiez la réponse de marc_s sur le lien ci-dessus.
with query as
(
select name, ROW_NUMBER() OVER(ORDER BY name ASC) as line from @table
)
select top (@pagesize) name,
(SELECT MAX(line) FROM query) AS total
from query
where line > (@pagenumber - 1) * @pagesize
Il existe de nombreuses façons de parvenir à la pagination: j'espère que ces informations vous seront utiles, ainsi qu'à d'autres.
Exemple 1: utilisation de la clause suivante offset-fetch. introduire en 2005
declare @table table (name varchar(30));
insert into @table values ('Jeanna Hackman');
insert into @table values ('Han Fackler');
insert into @table values ('Tiera Wetherbee');
insert into @table values ('Hilario Mccray');
insert into @table values ('Mariela Edinger');
insert into @table values ('Darla Tremble');
insert into @table values ('Mammie Cicero');
insert into @table values ('Raisa Harbour');
insert into @table values ('Nicholas Blass');
insert into @table values ('Heather Hayashi');
declare @pagenumber int = 1
declare @pagesize int = 3
--this is a CTE( common table expression and this is introduce in 2005)
with query as
(
select ROW_NUMBER() OVER(ORDER BY name ASC) as line, name from @table
)
--order by clause is required to use offset-fetch
select * from query
order by name
offset ((@pagenumber - 1) * @pagesize) rows
fetch next @pagesize rows only
Exemple 2: en utilisant la fonction row_number () et entre
declare @table table (name varchar(30));
insert into @table values ('Jeanna Hackman');
insert into @table values ('Han Fackler');
insert into @table values ('Tiera Wetherbee');
insert into @table values ('Hilario Mccray');
insert into @table values ('Mariela Edinger');
insert into @table values ('Darla Tremble');
insert into @table values ('Mammie Cicero');
insert into @table values ('Raisa Harbour');
insert into @table values ('Nicholas Blass');
insert into @table values ('Heather Hayashi');
declare @pagenumber int = 2
declare @pagesize int = 3
SELECT *
FROM
(select ROW_NUMBER() OVER (ORDER BY PRODUCTNAME) AS RowNum, * from Products)
as Prodcut
where RowNum between (((@pagenumber - 1) * @pageSize )+ 1)
and (@pagenumber * @pageSize )
J'espère que cela sera utile à tous