Quelle est la meilleure façon de paginer les résultats dans SQL Server

475

Quelle est la meilleure façon (en termes de performances) de paginer les résultats dans SQL Server 2000, 2005, 2008, 2012 si vous souhaitez également obtenir le nombre total de résultats (avant la pagination)?

Panagiotis Korros
la source
26
Je me suis toujours demandé pourquoi ils ne prenaient pas simplement en charge la spécification d'un décalage dans le cadre de TOP (comme la prise en charge MySQL / Posgresql avec LIMIT / OFFSET). Par exemple, ils pourraient simplement avoir la syntaxe "SELECT TOP x, y ...." où x = nombre de lignes, y = décalage de départ. Il serait également rétrocompatible.
gregmac
3
hé, moi aussi ... l'implémentation de la pagination 2005 de sql c'est vraiment si gênant ...
opensas
6
@gregmac - Sql Server 2012 a maintenant une limite / décalage.
OO
2
La solution acceptée ne montre pas en quoi c'est le meilleur moyen (en termes de performances). Des données les sauvegardant sur de grands ensembles de données?
OO
3
@OO: Une bonne référence peut être trouvée ici: 4guysfromrolla.com/webtech/042606-1.shtml . Cependant, la méthode de recherche surperformera toute pagination basée sur le décalage.
Lukas Eder

Réponses:

465

Obtenir le nombre total de résultats et paginer sont deux opérations différentes. Pour les besoins de cet exemple, supposons que la requête que vous traitez est

SELECT * FROM Orders WHERE OrderDate >= '1980-01-01' ORDER BY OrderDate

Dans ce cas, vous détermineriez le nombre total de résultats en utilisant:

SELECT COUNT(*) FROM Orders WHERE OrderDate >= '1980-01-01'

... qui peut sembler inefficace, mais est en fait assez performant, en supposant que tous les index, etc. sont correctement configurés.

Ensuite, pour obtenir les résultats réels de manière paginée, la requête suivante serait la plus efficace:

SELECT  *
FROM    ( SELECT    ROW_NUMBER() OVER ( ORDER BY OrderDate ) AS RowNum, *
          FROM      Orders
          WHERE     OrderDate >= '1980-01-01'
        ) AS RowConstrainedResult
WHERE   RowNum >= 1
    AND RowNum < 20
ORDER BY RowNum

Cela renverra les lignes 1 à 19 de la requête d'origine. La chose intéressante ici, en particulier pour les applications Web, est que vous n'avez pas à conserver d'état, sauf les numéros de ligne à renvoyer.

mdb
la source
37
Juste pour noter que ROW_NUMBER () n'existe pas dans SQL Server 2000
John Hunter
6
cela renvoie-t-il toutes les lignes de la requête interne puis filtre-t-il en fonction de la requête externe? par exemple: la requête interne renvoie 100 000 et la requête externe ne renvoie que 20.
SoftwareGeek
2
@SoftwareGeek: pensez-y comme la sous-requête (requête interne) renvoyant un flux, qui est ensuite lu jusqu'à ce que la clause WHERE externe soit satisfaite. La façon dont les lignes peuvent être impliquées avec cela dépend entièrement de la requête, mais l'optimiseur fait généralement un très bon travail pour minimiser ce nombre. L'utilisation de la visionneuse de plan d'exécution graphique dans SQL Server Management Studio (utilisez Query / Include Actual Execution Plan) est très pédagogique à cet égard.
mdb
2
ok, que se passe-t-il si vous êtes dupliqué dans la sélection interne (comme lorsque vous avez une jointure interne) comment utilisez-vous distinct car RowNumber est différent et cela ne fonctionne pas
user217648
10
Microsoft a ajouté une nouvelle fonctionnalité à SQL 2012 qui rend la pagination similaire à MySQL. Suivez ce lien pour savoir comment. C'est un article intéressant: dbadiaries.com/…
Arash
513

Enfin, Microsoft SQL Server 2012 est sorti, j'aime beaucoup sa simplicité pour une pagination, vous n'avez pas besoin d'utiliser des requêtes complexes comme répondu ici.

Pour obtenir les 10 lignes suivantes, exécutez simplement cette requête:

SELECT * FROM TableName ORDER BY id OFFSET 10 ROWS FETCH NEXT 10 ROWS ONLY;

https://docs.microsoft.com/en-us/sql/t-sql/queries/select-order-by-clause-transact-sql#using-offset-and-fetch-to-limit-the-rows- revenu

Points clés à considérer lors de son utilisation:

  • ORDER BYest obligatoire d'utiliser la OFFSET ... FETCHclause.
  • OFFSET clause est obligatoire avec FETCH . Vous ne pouvez pas utiliserORDER BY ... FETCH .
  • TOPne peut pas être combiné avec OFFSETet FETCHdans la même expression de requête.
Õzbek
la source
12
Toujours en attente LISTAGG()/ GROUP_CONCAT().
Bacon Bits
1
@BaconBits Voir cette réponse pour une façon sournoise de le faire avec FOR XML: stackoverflow.com/a/273330/429949
Richard Marskell - Drackir
1
@ RichardMarskell-Drackir Il y a beaucoup de problèmes avec FOR XML PATH (''). Tout d'abord, il remplace les caractères de contrôle XML par des codes d'entité XML. Je espère que vous n'avez pas <, >ou &dans vos données! Deuxièmement, la FOR XML PATH ('')syntaxe en fait non utilisée est utilisée de cette manière. Vous êtes censé spécifier une colonne nommée ou un autre nom d'élément. Ne faire ni l'un ni l'autre n'est pas dans le document, ce qui signifie que le comportement n'est pas fiable. Troisièmement, plus nous acceptons la FOR XML PATH ('')syntaxe cassée , moins il est probable que MS fournisse réellement une fonction réelle LISTAGG() [ OVER() ] comme ils en avaient besoin.
Bacon Bits
4
dommage que la perf soit si mauvaise mssqlgirl.com/…
Jon
5
@Jon, cet article de blog lié n'est pas représentatif, dans le sens où il fait des comparaisons basées sur le retour du résultat de la page en recherchant les valeurs de la colonne id.
Noel Abrahams
103

Incroyablement, aucune autre réponse n'a mentionné le moyen le plus rapide de paginer dans toutes les versions de SQL Server. Les décalages peuvent être terriblement lents pour les grands nombres de pages, comme c'est le cas ici . Il existe un moyen entièrement différent et beaucoup plus rapide d'effectuer la pagination en SQL. Ceci est souvent appelé la "méthode de recherche" ou la "pagination par jeu de clés" comme décrit dans cet article de blog ici .

SELECT TOP 10 first_name, last_name, score, COUNT(*) OVER()
FROM players
WHERE (score < @previousScore)
   OR (score = @previousScore AND player_id < @previousPlayerId)
ORDER BY score DESC, player_id DESC

Le "prédicat de recherche"

Les valeurs @previousScoreet @previousPlayerIdsont les valeurs respectives du dernier enregistrement de la page précédente. Cela vous permet de récupérer la page "suivante". Si la ORDER BYdirection est ASC, utilisez simplement à la >place.

Avec la méthode ci-dessus, vous ne pouvez pas passer immédiatement à la page 4 sans avoir d'abord récupéré les 40 enregistrements précédents. Mais souvent, vous ne voulez pas sauter aussi loin de toute façon. Au lieu de cela, vous obtenez une requête beaucoup plus rapide qui peut être en mesure de récupérer des données en temps constant, en fonction de votre indexation. De plus, vos pages restent "stables", peu importe si les données sous-jacentes changent (par exemple à la page 1, pendant que vous êtes à la page 4).

C'est le meilleur moyen d'implémenter la pagination lors du chargement paresseux de données supplémentaires dans des applications Web, par exemple.

Notez que la "méthode de recherche" est également appelée pagination du jeu de clés .

Nombre total d'enregistrements avant la pagination

La COUNT(*) OVER()fonction de fenêtre vous aidera à compter le nombre total d'enregistrements "avant la pagination". Si vous utilisez SQL Server 2000, vous devrez recourir à deux requêtes pour le COUNT(*).

Lukas Eder
la source
2
@ user960567: En termes de performances, la pagination du jeu de clés sera toujours supérieure à la pagination offset, que vous implémentiez la pagination offset avec la norme SQL OFFSET .. FETCHou avec des ROW_NUMBER()astuces précédentes .
Lukas Eder
21
J'ai trois problèmes avec la méthode de recherche. [1] Un utilisateur ne peut pas accéder à la page. [2] cela suppose des clés séquentielles, c'est-à-dire que si quelqu'un supprime 3 lignes, j'obtiens une page de 7 éléments au lieu de 10. RowNumberme donne 10 éléments cohérents par page. [3] cela ne fonctionne pas avec les grilles existantes qui supposent pagenumberet pagesize.
Rebecca
7
@Junto: la pagination du jeu de clés n'est pas appropriée dans tous les cas. Ce n'est certainement pas pour les grilles de données. Mais c'est parfait pour des scénarios comme le défilement infini de la page de flux Facebook. Peu importe si de nouveaux messages sont ajoutés en haut, vos messages de flux suivants seront correctement ajoutés en bas pendant que vous faites défiler vers le bas. Exemple d'utilisation parfait pour cela ... Une telle chose serait beaucoup plus difficile à implémenter en utilisant la limite / extraction de décalage en utilisant uniquement des nombres.
Robert Koritnik
4
Je dois être d'accord avec Junto. Cette méthode exclut complètement un client qui a une interface de pagination assez standard de "Précédent 1 2 3 (4) 5 6 Suivant" où les utilisateurs peuvent aller de l'avant. Ce n'est pas exactement un cas de bord dans mon expérience ...
AaronHS
3
Article de pagination du jeu de clés ici
Stéphane
31

À partir de SQL Server 2012, nous pouvons utiliser OFFSETet FETCH NEXTClause pour réaliser la pagination.

Essayez ceci, pour SQL Server:

Dans SQL Server 2012, une nouvelle fonctionnalité a été ajoutée dans la clause ORDER BY pour interroger l'optimisation d'un ensemble de données, ce qui facilite le travail avec la pagination des données pour quiconque écrit en T-SQL ainsi que pour l'ensemble du plan d'exécution dans SQL Server.

Sous le script T-SQL avec la même logique utilisée dans l'exemple précédent.

--CREATING A PAGING WITH OFFSET and FETCH clauses IN "SQL SERVER 2012"
DECLARE @PageNumber AS INT, @RowspPage AS INT
SET @PageNumber = 2
SET @RowspPage = 10 
SELECT ID_EXAMPLE, NM_EXAMPLE, DT_CREATE
FROM TB_EXAMPLE
ORDER BY ID_EXAMPLE
OFFSET ((@PageNumber - 1) * @RowspPage) ROWS
FETCH NEXT @RowspPage ROWS ONLY;

TechNet: pagination d'une requête avec SQL Server

Mohan
la source
réponse la plus précise de cet essai
Vikrant
17

MSDN: ROW_NUMBER (Transact-SQL)

Renvoie le numéro séquentiel d'une ligne dans une partition d'un jeu de résultats, en commençant à 1 pour la première ligne de chaque partition.

L'exemple suivant retourne des lignes avec les nombres 50 à 60 inclus dans l'ordre de OrderDate.

WITH OrderedOrders AS
(
    SELECT
        ROW_NUMBER() OVER(ORDER BY FirstName DESC) AS RowNumber, 
        FirstName, LastName, ROUND(SalesYTD,2,1) AS "Sales YTD"
    FROM [dbo].[vSalesPerson]
) 
SELECT RowNumber, 
    FirstName, LastName, Sales YTD 
FROM OrderedOrders 
WHERE RowNumber > 50 AND RowNumber < 60;
  RowNumber FirstName    LastName               SalesYTD
  --- -----------  ---------------------- -----------------
  1   Linda        Mitchell               4251368.54
  2   Jae          Pak                    4116871.22
  3   Michael      Blythe                 3763178.17
  4   Jillian      Carson                 3189418.36
  5   Ranjit       Varkey Chudukatil      3121616.32
  6   José         Saraiva                2604540.71
  7   Shu          Ito                    2458535.61
  8   Tsvi         Reiter                 2315185.61
  9   Rachel       Valdez                 1827066.71
  10  Tete         Mensa-Annan            1576562.19
  11  David        Campbell               1573012.93
  12  Garrett      Vargas                 1453719.46
  13  Lynn         Tsoflias               1421810.92
  14  Pamela       Ansman-Wolfe           1352577.13
Dinesh Rabara
la source
15

Il existe un bon aperçu des différentes techniques de pagination sur http://www.codeproject.com/KB/aspnet/PagingLarge.aspx

J'ai utilisé la méthode ROWCOUNT assez souvent, principalement avec SQL Server 2000 (fonctionnera également avec 2005 et 2008, il suffit de mesurer les performances par rapport à ROW_NUMBER), c'est rapide comme l'éclair, mais vous devez vous assurer que les colonnes triées ont (principalement ) des valeurs uniques.

liggett78
la source
1
Fait intéressant, cet article ne mentionne pas la méthode de recherche , qui est capable d'effectuer la pagination en temps constant ... Encore un bon article
Lukas Eder
6

Pour SQL Server 2000, vous pouvez simuler ROW_NUMBER () à l'aide d'une variable de table avec une colonne IDENTITY:

DECLARE @pageNo int -- 1 based
DECLARE @pageSize int
SET @pageNo = 51
SET @pageSize = 20

DECLARE @firstRecord int
DECLARE @lastRecord int
SET @firstRecord = (@pageNo - 1) * @pageSize + 1 -- 1001
SET @lastRecord = @firstRecord + @pageSize - 1   -- 1020

DECLARE @orderedKeys TABLE (
  rownum int IDENTITY NOT NULL PRIMARY KEY CLUSTERED,
  TableKey int NOT NULL
)

SET ROWCOUNT @lastRecord
INSERT INTO @orderedKeys (TableKey) SELECT ID FROM Orders WHERE OrderDate >= '1980-01-01' ORDER BY OrderDate

SET ROWCOUNT 0

SELECT t.*
FROM Orders t
  INNER JOIN @orderedKeys o ON o.TableKey = t.ID
WHERE o.rownum >= @firstRecord
ORDER BY o.rownum

Cette approche peut être étendue aux tables avec des clés multi-colonnes, et elle n'entraîne pas la surcharge de performances de l'utilisation de OR (qui ignore l'utilisation de l'index). L'inconvénient est la quantité d'espace temporaire utilisée si l'ensemble de données est très volumineux et que l'on se trouve près de la dernière page. Je n'ai pas testé les performances du curseur dans ce cas, mais cela pourrait être mieux.

Notez que cette approche pourrait être optimisée pour la première page de données. En outre, ROWCOUNT a été utilisé car TOP n'accepte pas de variable dans SQL Server 2000.

Thomas S. Trias
la source
3

La meilleure façon de paginer dans SQL Server 2012 est d'utiliser le décalage et la récupération suivante dans une procédure stockée. Mot - clé OFFSET - Si nous utilisons offset avec la clause order by, la requête ignorera le nombre d'enregistrements que nous avons spécifié dans OFFSET n Rows.

Mots - clés FETCH NEXT - Lorsque nous utilisons Fetch Next uniquement avec une clause order by, il renvoie le nombre de lignes que vous souhaitez afficher dans la pagination, sans décalage, puis SQL génère une erreur. voici l'exemple donné ci-dessous.

create procedure sp_paging
(
 @pageno as int,
 @records as int
)
as
begin
declare @offsetcount as int
set @offsetcount=(@pageno-1)*@records
select id,bs,variable from salary order by id offset @offsetcount rows fetch Next @records rows only
end

vous pouvez l'exécuter comme suit.

exec sp_paging 2,3
Debendra Dash
la source
2

Ce sont mes solutions pour paginer le résultat d'une requête côté serveur SQL. ces approches sont différentes entre SQL Server 2008 et 2012. De plus, j'ai ajouté le concept de filtrage et de tri par une seule colonne. Il est très efficace lorsque vous effectuez une recherche, un filtrage et une commande dans votre Gridview.

Avant de tester, vous devez créer un exemple de table et insérer une ligne dans ce tableau: (Dans le monde réel, vous devez modifier la clause Where en tenant compte des champs de votre table et peut-être avez-vous une jointure et une sous-requête dans la partie principale de select)

Create Table VLT
(
    ID int IDentity(1,1),
    Name nvarchar(50),
    Tel Varchar(20)
)
GO


Insert INTO VLT
VALUES
    ('NAME' + Convert(varchar(10),@@identity),'FAMIL' + Convert(varchar(10),@@identity))
GO 500000

Dans tous ces exemples, je souhaite interroger 200 lignes par page et je récupère la ligne pour le numéro de page 1200.

Dans SQL Server 2008, vous pouvez utiliser le concept CTE. Pour cette raison, j'ai écrit deux types de requêtes pour SQL Server 2008+

- SQL Server 2008+

DECLARE @PageNumber Int = 1200
DECLARE @PageSize INT = 200
DECLARE @SortByField int = 1 --The field used for sort by
DECLARE @SortOrder nvarchar(255) = 'ASC' --ASC or DESC
DECLARE @FilterType nvarchar(255) = 'None' --The filter type, as defined on the client side (None/Contain/NotContain/Match/NotMatch/True/False/)
DECLARE @FilterValue nvarchar(255) = '' --The value the user gave for the filter
DECLARE @FilterColumn int = 1 --The column to wich the filter is applied, represents the column number like when we send the information.

SELECT 
  Data.ID,
  Data.Name,
  Data.Tel
FROM
  (  
    SELECT 
      ROW_NUMBER() 
        OVER( ORDER BY 
                CASE WHEN @SortByField = 1 AND @SortOrder = 'ASC'
                      THEN VLT.ID END ASC,
                CASE WHEN @SortByField = 1 AND @SortOrder = 'DESC'
                      THEN VLT.ID END DESC,
                CASE WHEN @SortByField = 2 AND @SortOrder = 'ASC'
                      THEN VLT.Name END ASC,
                CASE WHEN @SortByField = 2 AND @SortOrder = 'DESC'
                      THEN VLT.Name END ASC,
                CASE WHEN @SortByField = 3 AND @SortOrder = 'ASC'
                      THEN VLT.Tel END ASC,
                CASE WHEN @SortByField = 3 AND @SortOrder = 'DESC'
                      THEN VLT.Tel END ASC
         ) AS RowNum
      ,*  
    FROM VLT 
    WHERE
      ( -- We apply the filter logic here
        CASE
          WHEN @FilterType = 'None' THEN 1

          -- Name column filter
          WHEN @FilterType = 'Contain' AND @FilterColumn = 1
            AND ( -- In this case, when the filter value is empty, we want to show everything.
                VLT.ID LIKE '%' + @FilterValue + '%'
               OR
                @FilterValue = ''
               ) THEN 1
          WHEN @FilterType = 'NotContain' AND @FilterColumn = 1
            AND ( -- In this case, when the filter value is empty, we want to show everything.
                VLT.ID NOT LIKE '%' + @FilterValue + '%'
               OR
                @FilterValue = ''
               ) THEN 1
          WHEN @FilterType = 'Match' AND @FilterColumn = 1
            AND VLT.ID = @FilterValue THEN 1
          WHEN @FilterType = 'NotMatch' AND @FilterColumn = 1
            AND VLT.ID <> @FilterValue THEN 1               

          -- Name column filter
          WHEN @FilterType = 'Contain' AND @FilterColumn = 2
            AND ( -- In this case, when the filter value is empty, we want to show everything.
                VLT.Name LIKE '%' + @FilterValue + '%'
               OR
                @FilterValue = ''
               ) THEN 1
          WHEN @FilterType = 'NotContain' AND @FilterColumn = 2
            AND ( -- In this case, when the filter value is empty, we want to show everything.
                VLT.Name NOT LIKE '%' + @FilterValue + '%'
               OR
                @FilterValue = ''
               ) THEN 1
          WHEN @FilterType = 'Match' AND @FilterColumn = 2
            AND VLT.Name = @FilterValue THEN 1
          WHEN @FilterType = 'NotMatch' AND @FilterColumn = 2
            AND VLT.Name <> @FilterValue THEN 1         

         -- Tel column filter   
         WHEN @FilterType = 'Contain' AND @FilterColumn = 3
            AND ( -- In this case, when the filter value is empty, we want to show everything.
                VLT.Tel LIKE '%' + @FilterValue + '%'
               OR
                @FilterValue = ''
               ) THEN 1
          WHEN @FilterType = 'NotContain' AND @FilterColumn = 3
            AND ( -- In this case, when the filter value is empty, we want to show everything.
                VLT.Tel NOT LIKE '%' + @FilterValue + '%'
               OR
                @FilterValue = ''
               ) THEN 1
          WHEN @FilterType = 'Match' AND @FilterColumn = 3
            AND VLT.Tel = @FilterValue THEN 1
          WHEN @FilterType = 'NotMatch' AND @FilterColumn = 3
            AND VLT.Tel <> @FilterValue THEN 1    

        END
      ) = 1   
  ) AS Data
WHERE Data.RowNum > @PageSize * (@PageNumber - 1)
  AND Data.RowNum <= @PageSize * @PageNumber
ORDER BY Data.RowNum

GO

Et deuxième solution avec CTE dans SQL Server 2008+

DECLARE @PageNumber Int = 1200
DECLARE @PageSize INT = 200
DECLARE @SortByField int = 1 --The field used for sort by
DECLARE @SortOrder nvarchar(255) = 'ASC' --ASC or DESC
DECLARE @FilterType nvarchar(255) = 'None' --The filter type, as defined on the client side (None/Contain/NotContain/Match/NotMatch/True/False/)
DECLARE @FilterValue nvarchar(255) = '' --The value the user gave for the filter
DECLARE @FilterColumn int = 1 --The column to wich the filter is applied, represents the column number like when we send the information.

;WITH
  Data_CTE
  AS
  (  
    SELECT 
      ROW_NUMBER() 
        OVER( ORDER BY 
                CASE WHEN @SortByField = 1 AND @SortOrder = 'ASC'
                      THEN VLT.ID END ASC,
                CASE WHEN @SortByField = 1 AND @SortOrder = 'DESC'
                      THEN VLT.ID END DESC,
                CASE WHEN @SortByField = 2 AND @SortOrder = 'ASC'
                      THEN VLT.Name END ASC,
                CASE WHEN @SortByField = 2 AND @SortOrder = 'DESC'
                      THEN VLT.Name END ASC,
                CASE WHEN @SortByField = 3 AND @SortOrder = 'ASC'
                      THEN VLT.Tel END ASC,
                CASE WHEN @SortByField = 3 AND @SortOrder = 'DESC'
                      THEN VLT.Tel END ASC
         ) AS RowNum
      ,*  
    FROM VLT
    WHERE
      ( -- We apply the filter logic here
        CASE
          WHEN @FilterType = 'None' THEN 1

          -- Name column filter
          WHEN @FilterType = 'Contain' AND @FilterColumn = 1
            AND ( -- In this case, when the filter value is empty, we want to show everything.
                VLT.ID LIKE '%' + @FilterValue + '%'
               OR
                @FilterValue = ''
               ) THEN 1
          WHEN @FilterType = 'NotContain' AND @FilterColumn = 1
            AND ( -- In this case, when the filter value is empty, we want to show everything.
                VLT.ID NOT LIKE '%' + @FilterValue + '%'
               OR
                @FilterValue = ''
               ) THEN 1
          WHEN @FilterType = 'Match' AND @FilterColumn = 1
            AND VLT.ID = @FilterValue THEN 1
          WHEN @FilterType = 'NotMatch' AND @FilterColumn = 1
            AND VLT.ID <> @FilterValue THEN 1               

          -- Name column filter
          WHEN @FilterType = 'Contain' AND @FilterColumn = 2
            AND ( -- In this case, when the filter value is empty, we want to show everything.
                VLT.Name LIKE '%' + @FilterValue + '%'
               OR
                @FilterValue = ''
               ) THEN 1
          WHEN @FilterType = 'NotContain' AND @FilterColumn = 2
            AND ( -- In this case, when the filter value is empty, we want to show everything.
                VLT.Name NOT LIKE '%' + @FilterValue + '%'
               OR
                @FilterValue = ''
               ) THEN 1
          WHEN @FilterType = 'Match' AND @FilterColumn = 2
            AND VLT.Name = @FilterValue THEN 1
          WHEN @FilterType = 'NotMatch' AND @FilterColumn = 2
            AND VLT.Name <> @FilterValue THEN 1         

         -- Tel column filter   
         WHEN @FilterType = 'Contain' AND @FilterColumn = 3
            AND ( -- In this case, when the filter value is empty, we want to show everything.
                VLT.Tel LIKE '%' + @FilterValue + '%'
               OR
                @FilterValue = ''
               ) THEN 1
          WHEN @FilterType = 'NotContain' AND @FilterColumn = 3
            AND ( -- In this case, when the filter value is empty, we want to show everything.
                VLT.Tel NOT LIKE '%' + @FilterValue + '%'
               OR
                @FilterValue = ''
               ) THEN 1
          WHEN @FilterType = 'Match' AND @FilterColumn = 3
            AND VLT.Tel = @FilterValue THEN 1
          WHEN @FilterType = 'NotMatch' AND @FilterColumn = 3
            AND VLT.Tel <> @FilterValue THEN 1    

        END
      ) = 1     
  )

SELECT 
  Data.ID,
  Data.Name,
  Data.Tel
FROM Data_CTE AS Data
WHERE Data.RowNum > @PageSize * (@PageNumber - 1)
  AND Data.RowNum <= @PageSize * @PageNumber
ORDER BY Data.RowNum

- SQL Server 2012+

DECLARE @PageNumber Int = 1200
DECLARE @PageSize INT = 200
DECLARE @SortByField int = 1 --The field used for sort by
DECLARE @SortOrder nvarchar(255) = 'ASC' --ASC or DESC
DECLARE @FilterType nvarchar(255) = 'None' --The filter type, as defined on the client side (None/Contain/NotContain/Match/NotMatch/True/False/)
DECLARE @FilterValue nvarchar(255) = '' --The value the user gave for the filter
DECLARE @FilterColumn int = 1 --The column to wich the filter is applied, represents the column number like when we send the information.

;WITH
  Data_CTE
  AS
  (  
    SELECT 
      *  
    FROM VLT
    WHERE
      ( -- We apply the filter logic here
        CASE
          WHEN @FilterType = 'None' THEN 1

          -- Name column filter
          WHEN @FilterType = 'Contain' AND @FilterColumn = 1
            AND ( -- In this case, when the filter value is empty, we want to show everything.
                VLT.ID LIKE '%' + @FilterValue + '%'
               OR
                @FilterValue = ''
               ) THEN 1
          WHEN @FilterType = 'NotContain' AND @FilterColumn = 1
            AND ( -- In this case, when the filter value is empty, we want to show everything.
                VLT.ID NOT LIKE '%' + @FilterValue + '%'
               OR
                @FilterValue = ''
               ) THEN 1
          WHEN @FilterType = 'Match' AND @FilterColumn = 1
            AND VLT.ID = @FilterValue THEN 1
          WHEN @FilterType = 'NotMatch' AND @FilterColumn = 1
            AND VLT.ID <> @FilterValue THEN 1               

          -- Name column filter
          WHEN @FilterType = 'Contain' AND @FilterColumn = 2
            AND ( -- In this case, when the filter value is empty, we want to show everything.
                VLT.Name LIKE '%' + @FilterValue + '%'
               OR
                @FilterValue = ''
               ) THEN 1
          WHEN @FilterType = 'NotContain' AND @FilterColumn = 2
            AND ( -- In this case, when the filter value is empty, we want to show everything.
                VLT.Name NOT LIKE '%' + @FilterValue + '%'
               OR
                @FilterValue = ''
               ) THEN 1
          WHEN @FilterType = 'Match' AND @FilterColumn = 2
            AND VLT.Name = @FilterValue THEN 1
          WHEN @FilterType = 'NotMatch' AND @FilterColumn = 2
            AND VLT.Name <> @FilterValue THEN 1         

         -- Tel column filter   
         WHEN @FilterType = 'Contain' AND @FilterColumn = 3
            AND ( -- In this case, when the filter value is empty, we want to show everything.
                VLT.Tel LIKE '%' + @FilterValue + '%'
               OR
                @FilterValue = ''
               ) THEN 1
          WHEN @FilterType = 'NotContain' AND @FilterColumn = 3
            AND ( -- In this case, when the filter value is empty, we want to show everything.
                VLT.Tel NOT LIKE '%' + @FilterValue + '%'
               OR
                @FilterValue = ''
               ) THEN 1
          WHEN @FilterType = 'Match' AND @FilterColumn = 3
            AND VLT.Tel = @FilterValue THEN 1
          WHEN @FilterType = 'NotMatch' AND @FilterColumn = 3
            AND VLT.Tel <> @FilterValue THEN 1    

        END
      ) = 1         
  )

SELECT 
  Data.ID,
  Data.Name,
  Data.Tel
FROM Data_CTE AS Data
ORDER BY 
    CASE WHEN @SortByField = 1 AND @SortOrder = 'ASC'
        THEN Data.ID END ASC,
    CASE WHEN @SortByField = 1 AND @SortOrder = 'DESC'
        THEN Data.ID END DESC,
    CASE WHEN @SortByField = 2 AND @SortOrder = 'ASC'
        THEN Data.Name END ASC,
    CASE WHEN @SortByField = 2 AND @SortOrder = 'DESC'
        THEN Data.Name END ASC,
    CASE WHEN @SortByField = 3 AND @SortOrder = 'ASC'
        THEN Data.Tel END ASC,
    CASE WHEN @SortByField = 3 AND @SortOrder = 'DESC'
        THEN Data.Tel END ASC
OFFSET @PageSize * (@PageNumber - 1) ROWS FETCH NEXT @PageSize ROWS ONLY;
Ardalan Shahgholi
la source
1

Essayez cette approche:

SELECT TOP @offset a.*
FROM (select top @limit b.*, COUNT(*) OVER() totalrows 
        from TABLENAME b order by id asc) a
ORDER BY id desc;
fatlion
la source
1

Dans le cas d'utilisation, les éléments suivants semblent faciles à utiliser et rapides. Définissez simplement le numéro de page.

use AdventureWorks
DECLARE @RowsPerPage INT = 10, @PageNumber INT = 6;
with result as(
SELECT SalesOrderDetailID, SalesOrderID, ProductID,
ROW_NUMBER() OVER (ORDER BY SalesOrderDetailID) AS RowNum
FROM Sales.SalesOrderDetail
where 1=1
)
select SalesOrderDetailID, SalesOrderID, ProductID from result
WHERE result.RowNum BETWEEN ((@PageNumber-1)*@RowsPerPage)+1
AND @RowsPerPage*(@PageNumber)

également sans CTE

use AdventureWorks
DECLARE @RowsPerPage INT = 10, @PageNumber INT = 6
SELECT SalesOrderDetailID, SalesOrderID, ProductID
FROM (
SELECT SalesOrderDetailID, SalesOrderID, ProductID,
ROW_NUMBER() OVER (ORDER BY SalesOrderDetailID) AS RowNum
FROM Sales.SalesOrderDetail
where 1=1
 ) AS SOD
WHERE SOD.RowNum BETWEEN ((@PageNumber-1)*@RowsPerPage)+1
AND @RowsPerPage*(@PageNumber)
Tonnerre
la source
1
Que fait où 1 = 1 monsieur?
Errol Paleracio
0

Eh bien, j'ai utilisé l'exemple de requête suivant dans ma base de données SQL 2000, cela fonctionne bien pour SQL 2005 aussi. La puissance qu'il vous donne est dynamiquement ordonnée en utilisant plusieurs colonnes. Je vous le dis ... c'est puissant :)

    ALTER PROCEDURE [dbo].[RE_ListingReports_SelectSummary] 

@CompanyID  int,
@pageNumber     int,
@pageSize   int, 
@sort       varchar(200)
AS

DECLARE @sql nvarchar(4000)
DECLARE @strPageSize nvarchar(20)
DECLARE @strSkippedRows nvarchar(20)
DECLARE @strFields nvarchar(4000)
DECLARE @strFilter nvarchar(4000)
DECLARE @sortBy nvarchar(4000)
DECLARE @strFrom nvarchar(4000)
DECLARE @strID nvarchar(100)

If(@pageNumber < 0)
  SET @pageNumber = 1
SET @strPageSize = CAST(@pageSize AS varchar(20)) 
SET @strSkippedRows = CAST(((@pageNumber - 1) * @pageSize) AS varchar(20))-- For    example if pageNumber is 5  pageSize is 10, then SkippedRows = 40.
SET @strID = 'ListingDbID'
SET @strFields = 'ListingDbID,
ListingID,  
[ExtraRoom]
'
SET @strFrom = ' vwListingSummary '

SET @strFilter = ' WHERE
        CompanyID = ' + CAST(@CompanyID As varchar(20)) 
End
SET @sortBy = ''
if(len(ltrim(rtrim(@sort))) > 0)
SET @sortBy = ' Order By ' + @sort

-- Total Rows Count

SET @sql =  'SELECT Count(' + @strID + ')  FROM ' + @strFROM + @strFilter
EXEC sp_executesql @sql

--// This technique is used in a Single Table pagination
SET @sql = 'SELECT ' + @strFields + ' FROM ' + @strFROM +
    ' WHERE ' + @strID +  ' IN ' + 
   '  (SELECT TOP ' + @strPageSize + ' ' + @strID + ' FROM ' + @strFROM + @strFilter + 
             ' AND  ' + @strID + ' NOT IN ' + '
          (SELECT TOP ' + @strSkippedRows + ' ' + @strID + ' FROM ' + @strFROM + @strFilter + @SortBy + ') ' 
   + @SortBy + ') ' + @SortBy
Print @sql 
EXEC sp_executesql @sql

La meilleure partie est que sp_executesql met en cache les appels ultérieurs, à condition de transmettre les mêmes paramètres, c'est-à-dire de générer le même texte SQL.

Jalal El-Shaer
la source
0
   CREATE view vw_sppb_part_listsource as 
    select row_number() over (partition by sppb_part.init_id order by sppb_part.sppb_part_id asc ) as idx, * from (
      select 
          part.SPPB_PART_ID
          , 0 as is_rev
          , part.part_number 
          , part.init_id 
      from t_sppb_init_part part 
      left join t_sppb_init_partrev prev on ( part.SPPB_PART_ID = prev.SPPB_PART_ID )
      where prev.SPPB_PART_ID is null 
      union 
      select 
          part.SPPB_PART_ID
          , 1 as is_rev
          , prev.part_number 
          , part.init_id 
      from t_sppb_init_part part 
      inner join t_sppb_init_partrev prev on ( part.SPPB_PART_ID = prev.SPPB_PART_ID )
    ) sppb_part

redémarrera idx en ce qui concerne les différents init_id

aden
la source
0

Pour la ROW_NUMBERtechnique, si vous n'avez pas de colonne de tri à utiliser, vous pouvez utiliser les éléments CURRENT_TIMESTAMPsuivants:

SELECT TOP 20 
    col1,
    col2,
    col3,
    col4
FROM (
    SELECT 
         tbl.col1 AS col1
        ,tbl.col2 AS col2
        ,tbl.col3 AS col3
        ,tbl.col4 AS col4
        ,ROW_NUMBER() OVER (
            ORDER BY CURRENT_TIMESTAMP
            ) AS sort_row
    FROM dbo.MyTable tbl
    ) AS query
WHERE query.sort_row > 10
ORDER BY query.sort_row

Cela a bien fonctionné pour moi pour les recherches sur des tailles de table allant jusqu'à 700 000.

Cela récupère les enregistrements 11 à 30.

user919426
la source
En tant que bonne pratique, avec la pagination, vous devez essayer de classer par un ensemble unique de colonnes dans l'ensemble de résultats, car l'ordre ne doit pas être considéré comme garanti.
Arin Taylor
2
Ceci récupère les records de 11 à 30.
Ardalan Shahgholi
0
create PROCEDURE SP_Company_List (@pagesize int = -1 ,@pageindex int= 0   ) > AS BEGIN  SET NOCOUNT ON;


    select  Id , NameEn     from Company  ORDER by Id ASC  
OFFSET (@pageindex-1 )* @pagesize   ROWS FETCH NEXt @pagesize ROWS ONLY END  GO

DECLARE   @return_value int

EXEC  @return_value = [dbo].[SP_Company_List]         @pagesize = 1 ,         > @pageindex = 2

SELECT    'Return Value' = @return_value

GO
salem albadawi
la source
0

Ce bit vous donne la possibilité de paginer à l'aide de SQL Server et des versions plus récentes de MySQL et porte le nombre total de lignes dans chaque ligne. Utilise votre clé pimary pour compter le nombre de lignes uniques.

WITH T AS
(  
  SELECT TABLE_ID, ROW_NUMBER() OVER (ORDER BY TABLE_ID) AS RN
  , (SELECT COUNT(TABLE_ID) FROM TABLE) AS TOTAL 
  FROM TABLE (NOLOCK)
)

SELECT T2.FIELD1, T2.FIELD2, T2.FIELD3, T.TOTAL 
FROM TABLE T2 (NOLOCK)
INNER JOIN T ON T2.TABLE_ID=T.TABLE_ID
WHERE T.RN >= 100
AND T.RN < 200
Alex M
la source
Pouvez-vous s'il vous plaît laisser des commentaires qui expliquent ce que fait votre code?
Doug F
0

À partir de 2012, nous pouvons utiliser OFFSET 10 ROWS FETCH NEXT 10 ROWS ONLY

Damitha
la source
-19

Vous n'avez pas spécifié la langue ni le pilote que vous utilisez. Je le décris donc de manière abstraite.

  • Créez un jeu de résultats / données déroulant. Cela nécessitait un primaire sur la table (s)
  • sauter à la fin
  • demander le nombre de lignes
  • passer au début de la page
  • faites défiler les lignes jusqu'à la fin de la page
Horcrux7
la source