Entspricht LIMIT und OFFSET für SQL Server?

172

In PostgreSQL gibt es die Schlüsselwörter Limitund, Offsetdie eine sehr einfache Paginierung von Ergebnismengen ermöglichen.

Was ist die äquivalente Syntax für SQL Server?

Earlz
quelle
Für SQL Server 2012 ist diese Funktion auf einfache Weise implementiert. Siehe meine Antwort
Somnath Muluk
Vielen Dank für diese Frage, wir sind gezwungen, von MySQL zu MsSQL zu
wechseln
Sie können Offset verwenden und die nächste Anweisung in SQL Server mit der Reihenfolge nach Klausel abrufen. Probieren Sie es aus youtu.be/EqHkAiiBwPc
Amresh Kumar Singh

Antworten:

139

Das Äquivalent zu LIMITist SET ROWCOUNT, aber wenn Sie eine generische Paginierung wünschen, ist es besser, eine Abfrage wie folgt zu schreiben:

;WITH Results_CTE AS
(
    SELECT
        Col1, Col2, ...,
        ROW_NUMBER() OVER (ORDER BY SortCol1, SortCol2, ...) AS RowNum
    FROM Table
    WHERE <whatever>
)
SELECT *
FROM Results_CTE
WHERE RowNum >= @Offset
AND RowNum < @Offset + @Limit

Der Vorteil hierbei ist die Parametrisierung des Versatzes und des Grenzwerts für den Fall, dass Sie Ihre Paging-Optionen ändern (oder dem Benutzer dies erlauben).

Hinweis: Der @OffsetParameter sollte hierfür eine einbasierte Indizierung anstelle der normalen nullbasierten Indizierung verwenden.

Aaronaught
quelle
22
Jetzt alt. SQL Server 2012 und höher unterstützen OFFSET / FETCH
Joel Coehoorn
31
@ JoelCoehoorn Nicht alt. Ich wurde gerade mit SLQ Server 2008 einem Projekt zugewiesen, nachdem ich in der Vergangenheit nur MySQL verwendet hatte ...
Cthulhu
Das ist ziemlich gut, muss aber ein wenig angepasst werdenWHERE RowNum >= (@Offset + 1)
Eric Herlitz
5
The ORDER BY clause is invalid in views, inline functions, derived tables, subqueries, and common table expressions, unless TOP or FOR XML is also specified. MSSQL2008 R2.
Paul
2
@Aaronaught Wenn mein TableDatensatz 200.000 Datensätze hat, werden alle zuerst abgerufen und dann das Limit angewendet? Ist diese Abfrage effizient?
Jigar
231

Diese Funktion ist jetzt in SQL Server 2012 vereinfacht. Dies funktioniert ab SQL Server 2012.

Mit Versatz begrenzen, um 11 bis 20 Zeilen in SQL Server auszuwählen:

SELECT email FROM emailTable 
WHERE user_id=3
ORDER BY Id
OFFSET 10 ROWS
FETCH NEXT 10 ROWS ONLY;
  • OFFSET: Anzahl der übersprungenen Zeilen
  • NEXT: erforderliche Anzahl der nächsten Zeilen

Referenz: https://docs.microsoft.com/en-us/sql/t-sql/queries/select-order-by-clause-transact-sql?view=sql-server-2017

Somnath Muluk
quelle
4
Gibt es ein Äquivalent SQL_CALC_FOUND_ROWSdazu?
Petah
1
@Petah @@ Rowcount wird Ihnen geben, dass ich denke
Rob Sedgwick
GOTCHA: Sie können dies nicht innerhalb eines CTE verwenden. Es muss in der Hauptabfrage verwendet werden. Ich wollte die Anzahl der zurückgegebenen Zeilen begrenzen (Paginierung) und dann eine teure Berechnung auf die etwa 10 zurückgegebenen Zeilen durchführen, anstatt die Zeilen zu bestimmen, die teure Berechnung durchzuführen und dann zu überspringen / zu nehmen, was ich brauchte. Die Antwort von @ Aaronaught funktioniert für diejenigen, die Zeilen innerhalb eines CTE einschränken müssen.
Derreck Dean
@ Somnath Muluk Dieses Versetzen und Abrufen nimmt viel Zeit für ein höheres Datenvolumen, beispielsweise mit einem Versatz von 1000000. Wie kann ich damit umgehen?
Saroj Shrestha
1
@SarojShrestha: Dies ist kein Offset- und Abrufproblem. Sie sollten die Architektur Ihrer Tabelle jetzt erneut besuchen. Erwägen Sie die Partitionierung von Tabellen, Ihrer Datenzeile und der verschiedenen Spaltentypen sowie der Gesamttabellengröße. Archivieren Sie einige Zeilen, wenn dies nicht regelmäßig erforderlich ist. Überprüfen Sie Ihre Serverspezifikationen.
Somnath Muluk
23
select top {LIMIT HERE} * from (
      select *, ROW_NUMBER() over (order by {ORDER FIELD}) as r_n_n 
      from {YOUR TABLES} where {OTHER OPTIONAL FILTERS}
) xx where r_n_n >={OFFSET HERE}

Ein Hinweis: Diese Lösung funktioniert nur in SQL Server 2005 oder höher, da dies der Zeitpunkt ROW_NUMBER()war, an dem sie implementiert wurde.

Jorgeu
quelle
Ich benutze diese Abfrage jetzt schon eine Weile und sie funktioniert großartig. Vielen Dank dafür. Ich frage mich nur, was das 'xx' darstellt?
Urbley
Die Unterabfrage erfordert einen Namen. da ich es nicht benutze, lege einfach xx dort hin
jorgeu
2
Das xx ist nur ein Tabellenalias. Es könnte ein wenig klarer sein, wenn Sie sagtenAS xx
Concrete Gannet
weiß jemand, wie man bei dieser Abfrage links beitritt?
Drenyl
12

Sie können ROW_NUMBER in einem allgemeinen Tabellenausdruck verwenden, um dies zu erreichen.

;WITH My_CTE AS
(
     SELECT
          col1,
          col2,
          ROW_NUMBER() OVER(ORDER BY col1) AS row_number
     FROM
          My_Table
     WHERE
          <<<whatever>>>
)
SELECT
     col1,
     col2
FROM
     My_CTE
WHERE
     row_number BETWEEN @start_row AND @end_row
Tom H.
quelle
4

Für mich war die Verwendung von OFFSET und FETCH zusammen langsam, daher habe ich eine Kombination aus TOP und OFFSET wie diese verwendet (die schneller war):

SELECT TOP 20 * FROM (SELECT columname1, columname2 FROM tablename
    WHERE <conditions...> ORDER BY columname1 OFFSET 100 ROWS) aliasname

Hinweis: Wenn Sie TOP und OFFSET zusammen in derselben Abfrage verwenden wie:

SELECT TOP 20 columname1, columname2 FROM tablename
    WHERE <conditions...> ORDER BY columname1 OFFSET 100 ROWS

Dann erhalten Sie eine Fehlermeldung. Um TOP und OFFSET zusammen zu verwenden, müssen Sie sie durch eine Unterabfrage trennen.

Und wenn Sie SELECT DISTINCT verwenden müssen, lautet die Abfrage wie folgt:

SELECT TOP 20 FROM (SELECT DISTINCT columname1, columname2
    WHERE <conditions...> ORDER BY columname1 OFFSET 100 ROWS) aliasname

Hinweis: Die Verwendung von SELECT ROW_NUMBER mit DISTINCT hat bei mir nicht funktioniert.

sebasdev
quelle
1
Ich erhalte die Meldung "Ein TOP kann nicht in derselben Abfrage oder Unterabfrage wie ein OFFSET verwendet werden."
Michael Rushton
Sie haben Recht @MichaelRushton, kann nicht in derselben Abfrage oder in derselben Unterabfrage verwendet werden, dann müssen Sie eine Unterabfrage verwenden, um sie zu trennen. Wenn Sie also SQL mögen SELECT TOP 20 id FROM table1 where id > 10 order by date OFFSET 20 rows, müssen Sie es wie transformieren SELECT TOP 20 * FROM (SELECT id FROM table1 where id > 10 order by date OFFSET 20 ROWS) t1. Ich werde meine Antwort bearbeiten. Danke und entschuldige mein Englisch.
sebasdev
2

Ein weiteres Beispiel:

declare @limit int 
declare @offset int 
set @offset = 2;
set @limit = 20;
declare @count int
declare @idxini int 
declare @idxfim int 
select @idxfim = @offset * @limit
select @idxini = @idxfim - (@limit-1);
WITH paging AS
    (
        SELECT 
             ROW_NUMBER() OVER (order by object_id) AS rowid, *
        FROM 
            sys.objects 
    )
select *
    from 
        (select COUNT(1) as rowqtd from paging) qtd, 
            paging 
    where 
        rowid between @idxini and @idxfim
    order by 
        rowid;
Dummkopf
quelle
15
Ich habe Ihre Anti-Microsoft-Hassrede entfernt. Besprechen Sie hier keine heiligen Kriege. Beantworten und stellen Sie Fragen auf nicht subjektive Weise.
Earlz
2

Es gibt hier jemanden, der über diese Funktion in SQL 2011 erzählt. Es ist traurig, dass er ein etwas anderes Schlüsselwort "OFFSET / FETCH" gewählt hat, aber es ist nicht Standard, dann ok.

keepkeywordspleeeease
quelle
2

Ich füge eine geringfügige Variation der Aaronaught-Lösung hinzu und parametrisiere normalerweise die Seitenzahl (@PageNum) und die Seitengröße (@PageSize). Auf diese Weise sendet jedes Seitenklickereignis nur die angeforderte Seitenzahl zusammen mit einer konfigurierbaren Seitengröße:

begin
    with My_CTE  as
    (
         SELECT col1,
              ROW_NUMBER() OVER(ORDER BY col1) AS row_number
     FROM
          My_Table
     WHERE
          <<<whatever>>>
    )
    select * from My_CTE
            WHERE RowNum BETWEEN (@PageNum - 1) * (@PageSize + 1) 
                              AND @PageNum * @PageSize

end
Tom
quelle
2

Das nächste, was ich machen könnte, ist

select * FROM( SELECT *, ROW_NUMBER() over (ORDER BY ID ) as ct from [db].[dbo].[table] ) sub where ct > fromNumber  and ct <= toNumber

Was ich ähnlich denke select * from [db].[dbo].[table] LIMIT 0, 10

user2991730
quelle
1
select top (@TakeCount) * --FETCH NEXT
from(
    Select  ROW_NUMBER() OVER (order by StartDate) AS rowid,*
    From YourTable
)A
where Rowid>@SkipCount --OFFSET
Scheune
quelle
1
@nombre_row :nombre ligne par page  
@page:numero de la page

//--------------code sql---------------

declare  @page int,@nombre_row int;
    set @page='2';
    set @nombre_row=5;
    SELECT  *
FROM    ( SELECT    ROW_NUMBER() OVER ( ORDER BY etudiant_ID ) AS RowNum, *
      FROM      etudiant

    ) AS RowConstrainedResult
WHERE   RowNum >= ((@page-1)*@nombre_row)+1
    AND RowNum < ((@page)*@nombre_row)+1
ORDER BY RowNum
noureddine ahmer el kaab
quelle
1

Da noch niemand diesen Code zur Verfügung gestellt hat:

SELECT TOP @limit f1, f2, f3...
FROM t1
WHERE c1 = v1, c2 > v2...
AND
    t1.id NOT IN
        (SELECT TOP @offset id
         FROM t1
         WHERE c1 = v1, c2 > v2...
         ORDER BY o1, o2...)
ORDER BY o1, o2...

Wichtige Punkte:

  • ORDER BY muss identisch sein
  • @limit kann durch die Anzahl der abzurufenden Ergebnisse ersetzt werden.
  • @offset ist die Anzahl der zu überspringenden Ergebnisse
  • Bitte vergleichen Sie die Leistung mit früheren Lösungen, da diese möglicherweise effizienter sind
  • Diese Lösung dupliziert whereund order byKlauseln und liefert falsche Ergebnisse, wenn sie nicht synchron sind
  • Auf der anderen Seite order bygibt es explizit, wenn es das ist, was benötigt wird
przemo_li
quelle
1
-- @RowsPerPage  can be a fixed number and @PageNumber number can be passed 
DECLARE @RowsPerPage INT = 10, @PageNumber INT = 2

SELECT *

FROM MemberEmployeeData

ORDER BY EmployeeNumber

OFFSET @PageNumber*@RowsPerPage ROWS

FETCH NEXT 10 ROWS ONLY
Shakeel
quelle
1

Speziell für SQL-SERVER können Sie dies auf viele verschiedene Arten erreichen. Als Beispiel haben wir hier die Kundentabelle genommen.

Beispiel 1: Mit "SET ROWCOUNT"

SET ROWCOUNT 10
SELECT CustomerID, CompanyName from Customers
ORDER BY CompanyName

Um alle Zeilen zurückzugeben, setzen Sie ROWCOUNT auf 0

SET ROWCOUNT 0  
SELECT CustomerID, CompanyName from Customers
    ORDER BY CompanyName

Beispiel 2: Mit "ROW_NUMBER and OVER"

With Cust AS
( SELECT CustomerID, CompanyName,
ROW_NUMBER() OVER (order by CompanyName) as RowNumber 
FROM Customers )
select *
from Cust
Where RowNumber Between 0 and 10

Beispiel 3: Mit "OFFSET and FETCH", aber mit diesem "ORDER BY" ist obligatorisch

SELECT CustomerID, CompanyName FROM Customers
ORDER BY CompanyName
OFFSET 0 ROWS
FETCH NEXT 10 ROWS ONLY

Hoffe das hilft dir.

Humayoun_Kabir
quelle
-1

In SQL Server würden Sie TOP zusammen mit ROW_NUMBER () verwenden.

SQLMenace
quelle
-1

Da ich dieses Skript öfter teste, um 1 Million Datensätze auf jeder Seite 100 Datensätze mit Paginierung schneller zu arbeiten, führt mein PC dieses Skript 0 Sekunden lang aus, während der Vergleich mit MySQL ein eigenes Limit hat und etwa 4,5 Sekunden versetzt, um das Ergebnis zu erhalten.

Jemand versteht möglicherweise nicht, dass Row_Number () immer nach bestimmten Feldern sortiert ist. Falls wir nur eine Zeile nacheinander definieren müssen, sollten Sie Folgendes verwenden:

ROW_NUMBER () OVER (ORDER BY (SELECT NULL))

SELECT TOP {LIMIT} * FROM (
      SELECT TOP {LIMIT} + {OFFSET} ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) AS ROW_NO,*
      FROM  {TABLE_NAME}
) XX WHERE ROW_NO > {OFFSET}

Erklären:

  • {LIMIT}: Anzahl der Datensätze für jede Seite
  • {OFFSET}: Anzahl der Sprungdatensätze
Vanda Ros
quelle
2
Während dieser Code die Frage lösen kann, einschließlich einer Erklärung, wie und warum dies das Problem löst, würde dies wirklich dazu beitragen, die Qualität Ihres Beitrags zu verbessern, und wahrscheinlich zu mehr Up-Votes führen. Denken Sie daran, dass Sie in Zukunft die Frage für die Leser beantworten, nicht nur für die Person, die jetzt fragt. Bitte bearbeiten Sie Ihre Antwort, um Erklärungen hinzuzufügen und anzugeben, welche Einschränkungen und Annahmen gelten.
Brian