LIMIT 10..20 in SQL Server

161

Ich versuche so etwas wie:

SELECT * FROM table LIMIT 10,20

oder

SELECT * FROM table LIMIT 10 OFFSET 10

aber mit SQL Server

Die einzige Lösung, die ich gefunden habe, sieht nach Overkill aus:

SELECT * FROM ( 
  SELECT *, ROW_NUMBER() OVER (ORDER BY name) as row FROM sys.databases 
 ) a WHERE row > 5 and row <= 10

Ich fand auch :

SELECT TOP 10 * FROM stuff; 

... aber es ist nicht das, was ich tun möchte, da ich das Startlimit nicht angeben kann.

Gibt es eine andere Möglichkeit für mich, das zu tun?

Nur neugierig, gibt es einen Grund, warum SQL Server die LIMITFunktion oder ähnliches nicht unterstützt? Ich möchte nicht gemein sein, aber das klingt wirklich nach etwas, das ein DBMS braucht ... Wenn ja, dann tut es mir leid, dass ich so unwissend bin! Ich arbeite seit 5 Jahren mit MySQL und SQL +, also ...

marcgg
quelle
1
Es ist am besten, einen CTE für ROW_NUMBER()und eine Begrenzung TOPfür die Breite des Bereichs und eine WHEREBedingung für eine Grenze des Bereichs zu verwenden, die ich erreichen konnte. Ich habe auch eine viel bessere Leistung festgestellt, wenn die TOPKlausel ein Literal anstelle einer Variablen verwendet
Jodrell
Das Problem bei jeder Lösung mit ROW_NUMBER () ist, dass Sie eine "Die Spalte" erhalten, wenn Sie nicht im Voraus wissen, welche Spalten Sie haben und welche Verknüpfungen Sie haben und welche verknüpften Tabellen denselben Spaltennamen haben 'xxx' wurde mehrfach angegeben ". Dies ist nicht so ungewöhnlich, wie es zunächst klingen mag. Ich verwende Dapper und meine Tabellen haben alle eine ID-Spalte. Dapper teilt und ordnet das zu, daher möchte ich sie nicht umbenennen, aber ich kann den Alias ​​SELECT * FROM ([ursprüngliche Abfrage]) nicht verwenden. Ich habe noch keine Lösung gefunden!
Steve Owen

Antworten:

104

Die LIMITKlausel ist nicht Teil von Standard-SQL. Es wird als Herstellererweiterung von SQL von MySQL, PostgreSQL und SQLite unterstützt.

Andere Datenbankmarken verfügen möglicherweise über ähnliche Funktionen (z. B. TOPin Microsoft SQL Server), diese funktionieren jedoch nicht immer identisch.

TOPIn Microsoft SQL Server ist es schwierig, die LIMITKlausel nachzuahmen . Es gibt Fälle, in denen es einfach nicht funktioniert.

Die von Ihnen gezeigte Lösung ROW_NUMBER()ist in Microsoft SQL Server 2005 und höher verfügbar. Dies ist (vorerst) die beste Lösung, die ausschließlich als Teil der Abfrage funktioniert.

Eine andere Lösung besteht darin TOP, die ersten Zeilen mit Anzahl + Versatz abzurufen und dann mithilfe der API nach den ersten Versatzzeilen zu suchen .

Siehe auch:

Bill Karwin
quelle
135

Für SQL Server 2012 + können Sie verwenden .

SELECT  *
FROM     sys.databases
ORDER BY name 
OFFSET  5 ROWS 
FETCH NEXT 5 ROWS ONLY 
Martin Smith
quelle
10
SQl Server 2012 muss ORDER BY angeben, wenn Sie OFFSET 5 ROWS verwenden. NUR NÄCHSTE 5 REIHEN FETCHEN, während MySql und SQLite ORDER BY nicht benötigen, wenn Sie LIMIT 5,5
Tomas Kubes verwenden.
4
@ qub1n - MySQL garantiert jedoch nicht, welche Zeilen Sie in diesem Fall zurückerhalten.
Martin Smith
3
Müssen Sie verwenden offsetoder können Sie diese Zeile weglassen (vorausgesetzt, Sie möchten keinen Versatz)?
Cullub
Sie Beispielabfrage läuft gut , aber wenn ich den Tabellennamen und Reihenfolge von col wie unten SELECT * FROM ändern DimProduct ORDER BY ProductKey OFFSET 5 ROWS FETCH NEXT - 5 ROWS ONLY Es gibt FehlerParse error at line: 4, column: 1: Incorrect syntax near 'OFFSET'
Shashwat
36

Wie Sie festgestellt haben, ist dies die bevorzugte SQL Server-Methode:

SELECT * FROM ( 
  SELECT *, ROW_NUMBER() OVER (ORDER BY name) as row FROM sys.databases 
 ) a WHERE a.row > 5 and a.row <= 10
KM.
quelle
Warum die anach der inneren Auswahl? Ich nehme an, Sie geben der inneren Auswahl einen Alias, aber dann scheinen Sie ihn nie zu verwenden ... Sollten Sie dann a.rowstatt nur tun row?
Lucas
3
@Lucas, Sie müssen einen Alias ​​nach der ( )abgeleiteten Tabelle einfügen, dieser wird jedoch freigegeben, wenn Sie dann vergessen, ihn zum Verweisen auf die Spalten zu verwenden. Ich habe es aber behoben ...
KM.
danke, ich habe das auf die harte Tour herausgefunden (habe versucht, den Alias ​​wegzulassen).
Lucas
1
Abstimmung mit +1: Die Antwort von @MartinSmith wird jedoch häufiger bewertet. Nachdem ich den Ausführungsplan mit dem dieses Ansatzes verglichen hatte, stellte ich fest, dass diese Lösung viel schneller funktioniert.
Harter
10

Wenn Sie mit SQL Server 2012+ Votum für Martin Smith Antwort und verwenden Sie die OFFSETund FETCH NEXTErweiterungen ORDER BY,

Wenn Sie das Pech haben, an einer früheren Version festzuhalten, können Sie so etwas tun:

WITH Rows AS
(
    SELECT
              ROW_NUMBER() OVER (ORDER BY [dbo].[SomeColumn]) [Row]
            , *
        FROM
              [dbo].[SomeTable]
)
SELECT TOP 10
          *
     FROM
         Rows
    WHERE Row > 10

Ich glaube, ist funktional gleichbedeutend mit

SELECT * FROM SomeTable LIMIT 10 OFFSET 10 ORDER BY SomeColumn

und die leistungsstärkste Art und Weise, wie ich sie in TSQL vor MS SQL 2012 kenne.


Wenn sehr viele Zeilen vorhanden sind, erzielen Sie möglicherweise eine bessere Leistung, wenn Sie eine temporäre Tabelle anstelle eines CTE verwenden.

Jodrell
quelle
Upvoted für den Hinweis auf Martin Smiths Antwort (und deren Verknüpfung) bei der Bereitstellung einer Lösung vor 2012. Auch für die temporäre Tabelle Ratschläge, weil Sie richtig sind :)
Fujiiface
7

Leider ist das ROW_NUMBER()das Beste, was Sie tun können. Es ist tatsächlich korrekter, weil die Ergebnisse einer limitoder top-Klausel ohne Berücksichtigung einer bestimmten Reihenfolge keine wirkliche Bedeutung haben. Aber es ist immer noch ein Schmerz zu tun.

Update: SQL Server 2012 fügt eine limitähnliche Funktion über die Schlüsselwörter OFFSET und FETCH hinzu . Dies ist der Ansi-Standard-Ansatz im Gegensatz zu LIMITeiner nicht standardmäßigen MySql-Erweiterung.

Joel Coehoorn
quelle
@Joel: Können Sie erklären, warum ROW_NUMBER () die Zeilen nicht so nummerieren kann, wie sie aus ORDER BY stammen? Ich habe mich immer gefragt, warum "OVER (ORDER BY name)" obligatorisch ist, aber ich denke, es gibt einen guten Grund dafür. Oder zumindest einen Grund.
Tomalak
3
weil es keine Bestellung ohne Order by-Klausel gibt. Sie erhalten die Reihenfolge, in der die Datensätze für den Server verfügbar waren. Dies kann sich von Abfrageanforderung zu Abfrageanforderung ändern .
Joel Coehoorn
1
@marcgg: Ich habe noch nie einen Hinweis darauf gelesen, dass Microsoft die Implementierung von LIMIT plant. Selbst wenn sie einen solchen Plan haben, neigen Closed-Source-Anbieter dazu, Funktionen nicht vorab anzukündigen. Es wäre sicherlich eine hilfreiche Funktion, aber wir wissen nicht, wie viel Arbeit die Implementierung angesichts ihres Codes bedeuten würde.
Bill Karwin
3
Wenn Sie sich in der ORDER BY-Klausel nicht wiederholen möchten, verwenden Sie den Alias ​​ROW_NUMBER () anstelle des ursprünglichen Spaltensatzes.
Peter Radocchia
2
@Tomalak: In Bezug auf SQL Server hängt die zur Berechnung von ROW_NUMBER () verwendete Reihenfolge völlig unabhängig von der Reihenfolge der Ergebnismenge ab. Deshalb müssen Sie sie separat angeben.
LukeH
6

Wie wäre es damit?

SET ROWCOUNT 10 

SELECT TOP 20 *
FROM sys.databases
ORDER BY database_id DESC

Es gibt Ihnen die letzten 10 Zeilen der ersten 20 Zeilen. Ein Nachteil ist, dass die Reihenfolge umgekehrt ist, aber zumindest ist es leicht zu merken.

David Patrick
quelle
6
Was ist, wenn die Tabelle nur 14 Zeilen enthält? Sie erhalten die Zeilen 14 bis 5, was nicht mit den von LIMIT 10 OFFSET 10 zurückgegebenen Zeilen identisch ist (sollten die Zeilen 14 bis 11 sein).
Bill Karwin
2
SELECT TOP 10 *
FROM TABLE
WHERE IDCOLUMN NOT IN (SELECT TOP 10 IDCOLUMN FROM TABLE)

Sollte Aufzeichnungen 11-20 geben. Wahrscheinlich nicht zu effizient, wenn Sie inkrementieren, um weitere Seiten zu erhalten, und nicht sicher sind, wie sich dies auf die Bestellung auswirkt. Möglicherweise muss dies in beiden WHERE-Anweisungen angegeben werden.

Andy
quelle
1

Ein guter Weg ist, eine Prozedur zu erstellen:

create proc pagination (@startfrom int ,@endto int) as
SELECT * FROM ( 
  SELECT *, ROW_NUMBER() OVER (ORDER BY name desc) as row FROM sys.databases 
 ) a WHERE a.row > @startfrom and a.row <= @endto

Genau wie Limit 0,2 //////////////// Paginierung 0,4 ausführen

Wahaj Latif
quelle
1

Nur für die Datensatzlösung, die in den meisten Datenbankmodulen funktioniert, ist sie möglicherweise nicht die effizienteste:

Select Top (ReturnCount) *
From (
    Select Top (SkipCount + ReturnCount) *
    From SourceTable
    Order By ReverseSortCondition
) ReverseSorted
Order By SortCondition

Pelase-Hinweis: Die letzte Seite würde immer noch ReturnCount-Zeilen enthalten, unabhängig davon, was SkipCount ist. Aber das könnte in vielen Fällen eine gute Sache sein.

YB
quelle
1

Das Äquivalent von LIMIT ist SET ROWCOUNT. Wenn Sie jedoch eine generische Paginierung wünschen, ist es besser, eine Abfrage wie die folgende 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
Satish Kumar Sonker
quelle
0
select * from (select id,name,ROW_NUMBER() OVER (ORDER BY id  asc) as row
from tableName1) tbl1
where tbl1.row>=10 and tbl1.row<=15

Druckt Zeilen von 10 bis 15.

sjith
quelle
0

Bisher funktioniert dieses Format für mich (allerdings nicht die beste Leistung):

SELECT TOP {desired amount of rows} * 
FROM (SELECT *, ROW_NUMBER() OVER (ORDER BY {order columns} asc)__row__ FROM {table})tmp
WHERE __row__ > {offset row count}

Ein Hinweis nebenbei, das Paginieren über dynamische Daten kann zu seltsamen / unerwarteten Ergebnissen führen.

Charlie Affumigato
quelle
0

In der Online-Dokumentation zu MS SQL Server ( http://technet.microsoft.com/en-us/library/ms186734.aspx ) finden Sie hier ein Beispiel, das ich getestet habe und das zum Abrufen eines bestimmten Satzes von Zeilen funktioniert. ROW_NUMBER erfordert ein OVER, aber Sie können nach Belieben bestellen:

WITH OrderedOrders AS
(
  SELECT SalesOrderID, OrderDate,
  ROW_NUMBER() OVER (ORDER BY OrderDate) AS RowNumber
  FROM Sales.SalesOrderHeader 
) 
SELECT SalesOrderID, OrderDate, RowNumber  
FROM OrderedOrders 
WHERE RowNumber BETWEEN 50 AND 60;
Shannon WM
quelle
0

Verwenden Sie alle SQL Server :; mit tbl als (SELECT ROW_NUMBER () over (Reihenfolge nach (Auswahl 1)) als RowIndex, * aus Tabelle) wählen Sie Top 10 * aus tbl aus, wobei RowIndex> = 10

Phạm Tấn Lợi
quelle
-3
 SELECT * FROM users WHERE Id Between 15 and 25

Es wird von 15 bis 25 als gleiches Limit in MYSQl gedruckt

SR1
quelle
2
Was ist, wenn der Benutzer einen Datensatz zwischen 15 und 25 gelöscht hat?
Gökçer Gökdal