Was ist der beste Weg, um Ergebnisse in SQL Server zu paginieren?

474

Was ist der beste Weg (in Bezug auf die Leistung), um Ergebnisse in SQL Server 2000, 2005, 2008, 2012 zu paginieren, wenn Sie auch die Gesamtzahl der Ergebnisse (vor dem Paginieren) erhalten möchten?

Panagiotis Korros
quelle
26
Ich habe mich immer gefragt, warum sie nicht nur die Angabe eines Offsets als Teil von TOP unterstützen (wie die Unterstützung von MySQL / Posgresql mit LIMIT / OFFSET). Zum Beispiel könnten sie einfach die Syntax "SELECT TOP x, y ...." haben, wobei x = Anzahl der Zeilen, y = Startoffset. Es wäre auch abwärtskompatibel.
Gregmac
3
Hey, ich auch ... SQL 2005 Paginierung Implementierung ist es wirklich so akward ...
eröffnet am
6
@gregmac - SQL Server 2012 hat jetzt Limit / Offset.
OO
2
Die akzeptierte Lösung zeigt nicht, wie es der beste Weg ist (in Bezug auf die Leistung). Gibt es Daten, die es in großen Datenmengen sichern?
OO
3
@OO: Einen guten Benchmark finden Sie hier: 4guysfromrolla.com/webtech/042606-1.shtml . Die Suchmethode übertrifft jedoch jede Offset-basierte Paginierung.
Lukas Eder

Antworten:

465

Das Abrufen der Gesamtzahl der Ergebnisse und das Paginieren sind zwei verschiedene Vorgänge. Nehmen wir für dieses Beispiel an, dass es sich bei der Abfrage handelt, mit der Sie sich befassen

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

In diesem Fall würden Sie die Gesamtzahl der Ergebnisse ermitteln mit:

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

... was ineffizient erscheinen mag, aber tatsächlich ziemlich performant ist, vorausgesetzt, alle Indizes usw. sind ordnungsgemäß eingerichtet.

Um die tatsächlichen Ergebnisse auf ausgelagerte Weise wiederherzustellen, ist die folgende Abfrage am effizientesten:

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

Dadurch werden die Zeilen 1 bis 19 der ursprünglichen Abfrage zurückgegeben. Das Coole dabei, insbesondere für Web-Apps, ist, dass Sie außer den zurückzugebenden Zeilennummern keinen Status beibehalten müssen.

mdb
quelle
37
Nur um zu beachten, dass ROW_NUMBER () in SQL Server 2000
John Hunter
6
Gibt dies alle Zeilen aus der inneren Abfrage zurück und filtert es dann basierend auf der äußeren Abfrage? Zum Beispiel: Die innere Abfrage gibt 100.000 zurück und die äußere Abfrage gibt nur 20 zurück.
SoftwareGeek
2
@SoftwareGeek: Stellen Sie sich das als Unterabfrage (innere Abfrage) vor, die einen Stream zurückgibt, der dann gelesen wird, bis die äußere WHERE-Klausel erfüllt ist. Wie Zeilen daran beteiligt sein können, hängt ganz von der Abfrage ab, aber der Optimierer leistet im Allgemeinen sehr gute Arbeit bei der Minimierung dieser Anzahl. Die Verwendung des grafischen Ausführungsplan-Viewers in SQL Server Management Studio (verwenden Sie den tatsächlichen Ausführungsplan abfragen / einschließen) ist in dieser Hinsicht sehr lehrreich.
MDB
2
ok, was ist, wenn Sie in inner select veröffentlicht werden (wie wenn Sie inner join haben), wie verwenden Sie different, weil RowNumber anders ist und es nicht funktioniert
user217648
10
Microsoft hat SQL 2012 um eine neue Funktion erweitert, mit der die Paginierung MySQL ähnelt. Folgen Sie diesem Link, um zu erfahren, wie. Es ist ein interessanter Artikel: dbadiaries.com/…
Arash
512

Schließlich wurde Microsoft SQL Server 2012 veröffentlicht. Ich mag die Einfachheit einer Paginierung sehr. Sie müssen keine komplexen Abfragen verwenden, wie sie hier beantwortet wurden.

Um die nächsten 10 Zeilen zu erhalten, führen Sie einfach diese Abfrage aus:

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- ist zurückgekommen

Wichtige Punkte, die bei der Verwendung zu beachten sind:

  • ORDER BYist obligatorisch, um OFFSET ... FETCHKlausel zu verwenden .
  • OFFSETKlausel ist obligatorisch mit FETCH. Sie können nicht verwenden ORDER BY ... FETCH.
  • TOPkann nicht mit OFFSETund FETCHim selben Abfrageausdruck kombiniert werden .
Õzbek
quelle
12
Ich warte immer noch auf LISTAGG()/ GROUP_CONCAT().
Bacon Bits
1
@ BaconBits Sehen Sie diese Antwort für eine hinterhältige Methode mit FOR XML: stackoverflow.com/a/273330/429949
Richard Marskell - Drackir
1
@ RichardMarskell-Drackir Es gibt viele Probleme mit FOR XML PATH (''). Erstens werden XML-Steuerzeichen durch XML-Entitätscodes ersetzt. Hoffe , dass Sie nicht haben <, >oder &in Ihren Daten! Zweitens FOR XML PATH ('')wird auf diese Weise tatsächlich eine undokumentierte Syntax verwendet. Sie sollten eine benannte Spalte oder einen alternativen Elementnamen angeben. Beides nicht zu tun ist nicht im Dokument enthalten, was bedeutet, dass das Verhalten unzuverlässig ist. Drittens, je mehr wir die fehlerhafte FOR XML PATH ('')Syntax akzeptieren, desto weniger wahrscheinlich ist es, dass MS tatsächlich eine echte LISTAGG() [ OVER() ] Funktion bereitstellt, wie sie benötigt wird.
Bacon Bits
4
Schade, dass der Perf so schlecht ist mssqlgirl.com/…
Jon
5
@Jon, dieser verknüpfte Blog-Beitrag ist nicht repräsentativ, da er Vergleiche auf der Grundlage der Rückgabe des Seitenergebnisses durch Nachschlagen der Werte der ID-Spalte durchführt.
Noel Abrahams
103

Unglaublicherweise hat keine andere Antwort den schnellsten Weg zur Paginierung in allen SQL Server-Versionen erwähnt. Offsets können für große Seitenzahlen furchtbar langsam sein, wie hier verglichen . Es gibt eine völlig andere, viel schnellere Möglichkeit, die Paginierung in SQL durchzuführen. Dies wird oft als "Suchmethode" oder "Keyset-Paginierung" bezeichnet, wie in diesem Blog-Beitrag hier beschrieben .

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

Das "Suchprädikat"

Die Werte @previousScoreund @previousPlayerIdsind die jeweiligen Werte des letzten Datensatzes von der vorherigen Seite. Auf diese Weise können Sie die "nächste" Seite abrufen. Wenn die ORDER BYRichtung ist ASC, verwenden Sie >stattdessen einfach .

Mit der obigen Methode können Sie nicht sofort zu Seite 4 springen, ohne zuvor die vorherigen 40 Datensätze abgerufen zu haben. Aber oft will man sowieso nicht so weit springen. Stattdessen erhalten Sie eine viel schnellere Abfrage, mit der Daten je nach Indizierung möglicherweise in konstanter Zeit abgerufen werden können. Außerdem bleiben Ihre Seiten "stabil", unabhängig davon, ob sich die zugrunde liegenden Daten ändern (z. B. auf Seite 1, während Sie sich auf Seite 4 befinden).

Dies ist der beste Weg, um die Paginierung zu implementieren, wenn beispielsweise mehr Daten in Webanwendungen verzögert geladen werden.

Beachten Sie, dass die " Suchmethode " auch als Keyset-Paginierung bezeichnet wird .

Gesamtzahl der Datensätze vor der Paginierung

Mit der COUNT(*) OVER()Fensterfunktion können Sie die Anzahl der Gesamtdatensätze "vor der Paginierung" zählen. Wenn Sie SQL Server 2000 verwenden, müssen Sie auf zwei Abfragen für das zurückgreifen COUNT(*).

Lukas Eder
quelle
2
@ user960567: In Bezug auf die Leistung schlägt Keyset-Paging immer das Offset-Paging, unabhängig davon, ob Sie das Offset-Paging mit dem SQL-Standard OFFSET .. FETCHoder mit früheren ROW_NUMBER()Tricks implementieren .
Lukas Eder
21
Ich habe drei Probleme mit der Suchmethode. [1] Ein Benutzer kann nicht zur Seite springen. [2] Es werden sequentielle Schlüssel angenommen, dh wenn jemand 3 Zeilen löscht, erhalte ich eine Seite mit 7 Elementen anstelle von 10. Dies RowNumbergibt mir konsistente 10 Elemente pro Seite. [3] Es funktioniert nicht mit vorhandenen Gittern, die pagenumberund annehmen pagesize.
Rebecca
7
@Junto: Keyset-Paging ist nicht für alle Fälle geeignet. Es ist definitiv nicht für Datenraster. Aber es ist perfekt für Szenarien wie das unendliche Scrollen der Facebook-Feed-Seite. Es spielt keine Rolle, ob oben neue Beiträge hinzugefügt werden. Ihre nachfolgenden Feed-Beiträge werden beim Scrollen nach unten korrekt unten hinzugefügt. Perfektes Verwendungsbeispiel dafür ... Es wäre viel schwieriger, so etwas mit Offset Limit / Fetch nur mit Zahlen zu implementieren.
Robert Koritnik
4
Ich muss Junto zustimmen. Diese Methode schließt einen Client mit einer Standard-Paginierungs-Benutzeroberfläche von "Zurück 1 2 3 (4) 5 6 Weiter" vollständig aus, bei der Benutzer vorwärts springen können. Dies ist meiner Erfahrung nach nicht gerade ein
Randfall
3
Keyset Paginierungsartikel hier
Stphane
31

Ab SQL Server 2012 können wir OFFSETund FETCH NEXTClause verwenden, um die Paginierung zu erreichen.

Versuchen Sie dies für SQL Server:

In SQL Server 2012 wurde in der ORDER BY-Klausel eine neue Funktion hinzugefügt, um die Optimierung von festgelegten Daten abzufragen und die Arbeit mit Daten-Paging für alle zu vereinfachen, die in T-SQL schreiben, sowie für den gesamten Ausführungsplan in SQL Server.

Unterhalb des T-SQL-Skripts mit derselben Logik wie im vorherigen Beispiel.

--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: Paging einer Abfrage mit SQL Server

Mohan
quelle
genaueste Antwort in dieser Studie
Vikrant
17

MSDN: ROW_NUMBER (Transact-SQL)

Gibt die fortlaufende Nummer einer Zeile innerhalb einer Partition einer Ergebnismenge zurück, beginnend bei 1 für die erste Zeile in jeder Partition.

Im folgenden Beispiel werden Zeilen mit den Nummern 50 bis einschließlich 60 in der Reihenfolge des OrderDate zurückgegeben.

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
quelle
15

Unter http://www.codeproject.com/KB/aspnet/PagingLarge.aspx finden Sie einen guten Überblick über verschiedene Paging-Techniken

Ich habe die ROWCOUNT-Methode ziemlich oft verwendet, meistens mit SQL Server 2000 (funktioniert auch mit 2005 und 2008, nur die Leistung im Vergleich zu ROW_NUMBER messen), sie ist blitzschnell, aber Sie müssen sicherstellen, dass die sortierten Spalten (meistens) haben ) eindeutige Werte.

liggett78
quelle
1
Interessanterweise erwähnt dieser Artikel nicht die Suchmethode , die in der Lage ist, Paging in konstanter Zeit durchzuführen ... Immer noch ein guter Artikel
Lukas Eder
6

Für SQL Server 2000 können Sie ROW_NUMBER () mithilfe einer Tabellenvariablen mit einer IDENTITY-Spalte simulieren:

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

Dieser Ansatz kann auf Tabellen mit mehrspaltigen Schlüsseln erweitert werden und verursacht keinen Leistungsaufwand bei der Verwendung von OR (wodurch die Indexverwendung übersprungen wird). Der Nachteil ist die Menge an temporärem Speicherplatz, der verbraucht wird, wenn der Datensatz sehr groß ist und sich einer in der Nähe der letzten Seite befindet. Ich habe die Cursorleistung in diesem Fall nicht getestet, aber es könnte besser sein.

Beachten Sie, dass dieser Ansatz für die erste Datenseite optimiert werden kann. Außerdem wurde ROWCOUNT verwendet, da TOP in SQL Server 2000 keine Variable akzeptiert.

Thomas S. Trias
quelle
3

Der beste Weg zum Paging in SQL Server 2012 ist die Verwendung von Offset und Fetch Next in einer gespeicherten Prozedur. OFFSET-Schlüsselwort - Wenn wir den Offset mit der order by-Klausel verwenden, überspringt die Abfrage die Anzahl der Datensätze, die wir in OFFSET n Rows angegeben haben.

FETCH NEXT-Schlüsselwörter - Wenn wir Fetch Next nur mit einer order by-Klausel verwenden, wird die Anzahl der Zeilen zurückgegeben, die beim Paging angezeigt werden sollen. Ohne Offset generiert SQL einen Fehler. Hier ist das folgende Beispiel.

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

Sie können es wie folgt ausführen.

exec sp_paging 2,3
Debendra Dash
quelle
2

Dies sind meine Lösungen zum Auslagern des Abfrageergebnisses auf der SQL Server-Seite. Diese Ansätze unterscheiden sich zwischen SQL Server 2008 und 2012. Außerdem habe ich das Konzept des Filterns und Ordnens nach einer Spalte hinzugefügt. Es ist sehr effizient, wenn Sie in Ihrem Gridview blättern, filtern und bestellen.

Vor dem Testen müssen Sie eine Beispieltabelle erstellen und eine Zeile in diese Tabelle einfügen: (In der realen Welt müssen Sie die Where-Klausel unter Berücksichtigung Ihrer Tabellenfelder ändern, und möglicherweise haben Sie im Hauptteil von select eine Verknüpfung und Unterabfrage.)

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

In all diesen Beispielen möchte ich 200 Zeilen pro Seite abfragen und rufe die Zeile für die Seitenzahl 1200 ab.

In SQL Server 2008 können Sie das CTE-Konzept verwenden. Aus diesem Grund habe ich zwei Arten von Abfragen für SQL Server 2008+ geschrieben

- 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

Und zweite Lösung mit CTE in 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
quelle
1

Versuchen Sie diesen Ansatz:

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

In Bezug auf den Anwendungsfall scheint Folgendes einfach und schnell zu sein. Stellen Sie einfach die Seitenzahl ein.

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)

auch ohne 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)
Donner
quelle
1
Was macht wo 1 = 1 Sir?
Errol Paleracio
0

Nun, ich habe die folgende Beispielabfrage in meiner SQL 2000-Datenbank verwendet, sie funktioniert auch gut für SQL 2005. Die Leistung, die es Ihnen gibt, wird dynamisch geordnet, indem mehrere Spalten verwendet werden. Ich sage dir ... das ist mächtig :)

    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

Der beste Teil ist, dass sp_executesql spätere Aufrufe zwischenspeichert, vorausgesetzt, Sie übergeben dieselben Parameter, dh Sie generieren denselben SQL-Text.

Jalal El-Shaer
quelle
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

startet idx neu, wenn es um verschiedene init_id geht

Aden
quelle
0

ROW_NUMBERWenn Sie für die Technik keine zu verwendende Sortierspalte haben, können Sie CURRENT_TIMESTAMPFolgendes verwenden:

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

Dies hat bei Suchvorgängen über Tabellengrößen von bis zu 700.000 gut funktioniert.

Dies holt Datensätze 11 bis 30.

user919426
quelle
Als gute Vorgehensweise sollten Sie bei der Paginierung versuchen, nach einer eindeutigen Menge von Spalten in der Ergebnismenge zu ordnen, da die Reihenfolge nicht als garantiert angesehen werden sollte.
Arin Taylor
2
Dies holt Aufzeichnungen 11 bis 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
quelle
0

Mit diesem Bit können Sie mit SQL Server und neueren Versionen von MySQL paginieren und die Gesamtzahl der Zeilen in jeder Zeile übertragen. Verwendet Ihren Pimary-Schlüssel, um die Anzahl der eindeutigen Zeilen zu zählen.

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.
quelle
Können Sie bitte Kommentare hinterlassen, die erklären, was Ihr Code tut?
Doug F
0

Dies ist ein Duplikat der alten SO-Frage von 2012: Effiziente Implementierung von Paging

FROM [TableX] ORDER BY [FieldX] OFFSET 500 ROWS NUR NÄCHSTE 100 REIHEN ABRUFEN

Hier wird das Thema ausführlicher und mit alternativen Ansätzen diskutiert.

d.popov
quelle
0

Ab 2012 können wir verwenden OFFSET 10 ROWS FETCH NEXT 10 ROWS ONLY

Damitha
quelle
-19

Sie haben weder die Sprache noch den von Ihnen verwendeten Treiber angegeben. Deshalb beschreibe ich es abstrakt.

  • Erstellen Sie eine scrollbare Ergebnismenge / einen Datensatz. Dies erforderte eine Primärdatenbank auf dem Tisch.
  • bis zum Ende springen
  • Fordern Sie die Zeilenanzahl an
  • zum Anfang der Seite springen
  • Scrollen Sie durch die Zeilen bis zum Ende der Seite
Horkrux7
quelle