Zeilenversatz in SQL Server

133

Gibt es in SQL Server eine Möglichkeit, die Ergebnisse ab einem bestimmten Offset zu erhalten? In einer anderen Art von SQL-Datenbank ist beispielsweise Folgendes möglich:

SELECT * FROM MyTable OFFSET 50 LIMIT 25

um Ergebnisse zu erhalten 51-75. Dieses Konstrukt scheint in SQL Server nicht vorhanden zu sein.

Wie kann ich dies erreichen, ohne alle Zeilen zu laden, die mir egal sind? Vielen Dank!

Alex
quelle
Sie können Offset verwenden und die nächste Anweisung abrufen. youtu.be/EqHkAiiBwPc
Amresh Kumar Singh

Antworten:

152

Ich würde es vermeiden, es zu benutzen SELECT *. Geben Sie die gewünschten Spalten an, auch wenn es sich möglicherweise um alle handelt.

SQL Server 2005+

SELECT col1, col2 
FROM (
    SELECT col1, col2, ROW_NUMBER() OVER (ORDER BY ID) AS RowNum
    FROM MyTable
) AS MyDerivedTable
WHERE MyDerivedTable.RowNum BETWEEN @startRow AND @endRow

SQL Server 2000

Effizientes Blättern durch große Ergebnismengen in SQL Server 2000

Eine effizientere Methode zum Blättern durch große Ergebnismengen

Brian Kim
quelle
6
Warum schlagen Sie vor, SELECT zu vermeiden, auch wenn Sie alle Spalten auswählen?
Adam Ness
12
Ich bin sicher, er hat "*" verwendet, weil es einfacher zu tippen war und den Punkt besser vermittelt hat als "col1, col2, ... colN"
gillonba
9
Was , warum es nicht zu benutzen, SELECT *bedeutet , dass , wenn die Struktur der Tabelle ändert, Ihre Anfrage noch läuft, aber unterschiedliche Ergebnisse. Wenn eine Spalte hinzugefügt wird, kann dies nützlich sein (obwohl Sie sie noch irgendwo namentlich verwenden müssen). Wenn eine Spalte gelöscht oder umbenannt wird, ist es besser, wenn Ihre SQL sichtbar unterbrochen wird, als wenn sich der Code weiter unten merkwürdig verhält, da eine Variable nicht initialisiert ist.
IMSoP
5
Alle Daten der Tabelle auswählen und ausschneiden? Wenn 5000000000 Zeilen haben? 5000000000 Zeilen auswählen und für jede Abfrage ausschneiden? Es ist nicht effizient für CPU und Speicher des Servers.
E-Info128
3
Bitte beachten Sie, dass 2012+ es viel besser implementiert hat. Siehe Antwort von + Martin Smith
Meridius
100

Wenn Sie alle Seiten der Reihe nach verarbeiten, TOP (25) ... WHERE Key > @last_key ORDER BY Keykann es die beste Methode sein, sich den letzten Schlüsselwert auf der vorherigen Seite zu merken und zu verwenden , wenn geeignete Indizes vorhanden sind, damit dies effizient gesucht werden kann - oder einen API-Cursor, wenn dies nicht der Fall ist .

Für die Auswahl einer beliebigen Seite ist wahrscheinlich ROW_NUMBERund die beste Lösung für SQL Server 2005 - 2008 R2BETWEEN

Für SQL Server 2012+ können Sie die erweiterte ORDER BY- Klausel für diesen Bedarf verwenden.

SELECT  *
FROM     MyTable 
ORDER BY OrderingColumn ASC 
OFFSET  50 ROWS 
FETCH NEXT 25 ROWS ONLY 

Obwohl es bleibt auch abzuwarten , wie die Durchführung wird diese Option sein .

Martin Smith
quelle
2
Es ist jetzt in SQL Server Compact 4.0 verfügbar -> msdn.microsoft.com/en-us/library/gg699618(v=sql.110).aspx
Bart Verkoeijen
13
Es ist an der Zeit, dass sie dies zu tSQL hinzufügen
JohnFx
3
Nur für SQL Server 2012 :(
e-info128
22

Dies ist eine Möglichkeit (SQL2000)

SELECT * FROM
(
    SELECT TOP (@pageSize) * FROM
    (
        SELECT TOP (@pageNumber * @pageSize) *
        FROM tableName 
        ORDER BY columnName ASC
    ) AS t1 
    ORDER BY columnName DESC
) AS t2 
ORDER BY columnName ASC

und dies ist ein anderer Weg (SQL 2005)

;WITH results AS (
    SELECT 
        rowNo = ROW_NUMBER() OVER( ORDER BY columnName ASC )
        , *
    FROM tableName 
) 
SELECT * 
FROM results
WHERE rowNo between (@pageNumber-1)*@pageSize+1 and @pageNumber*@pageSize
Leoinfo
quelle
Nur um das erste zu verdeutlichen ... (@pageSize) ist hier ein Platzhalter für den tatsächlichen Wert. Sie müssen speziell 'TOP 25' ausführen. SQL Server 2000 unterstützt keine Variablen in einer TOP-Klausel. Dies macht es zu einem Problem mit dynamischem SQL.
Cowan
5
Diese Lösung für SQL2000 funktioniert nicht für die letzte Seite in der Ergebnismenge, es sei denn, die Gesamtzahl der Zeilen beträgt zufällig ein Vielfaches der Seitengröße.
Bill Karwin
10

Sie können die ROW_NUMBER()Funktion verwenden, um das zu erhalten, was Sie möchten:

SELECT *
FROM (SELECT ROW_NUMBER() OVER(ORDER BY id) RowNr, id FROM tbl) t
WHERE RowNr BETWEEN 10 AND 20
Matthias Meid
quelle
7

Es gibt OFFSET .. FETCHin SQL Server 2012, aber Sie müssen eine ORDER BYSpalte angeben .

Wenn Sie wirklich keine explizite Spalte haben, die Sie als ORDER BYSpalte übergeben könnten (wie andere vorgeschlagen haben), können Sie diesen Trick verwenden:

SELECT * FROM MyTable 
ORDER BY @@VERSION 
OFFSET 50 ROWS FETCH NEXT 25 ROWS ONLY

... oder

SELECT * FROM MyTable 
ORDER BY (SELECT 0)
OFFSET 50 ROWS FETCH NEXT 25 ROWS ONLY

Wir verwenden es in jOOQ, wenn Benutzer keine Reihenfolge explizit angeben. Dies führt dann zu einer ziemlich zufälligen Bestellung ohne zusätzliche Kosten.

Lukas Eder
quelle
6

Für Tabellen mit mehr und großen Datenspalten bevorzuge ich:

SELECT 
  tablename.col1,
  tablename.col2,
  tablename.col3,
  ...
FROM
(
  (
    SELECT
      col1
    FROM 
    (
      SELECT col1, ROW_NUMBER() OVER (ORDER BY col1 ASC) AS RowNum
      FROM tablename
      WHERE ([CONDITION])
    )
    AS T1 WHERE T1.RowNum BETWEEN [OFFSET] AND [OFFSET + LIMIT]
  )
  AS T2 INNER JOIN tablename ON T2.col1=tablename.col1
);

- -

[CONDITION] can contain any WHERE clause for searching.
[OFFSET] specifies the start,
[LIMIT] the maximum results.

Bei Tabellen mit großen Datenmengen wie BLOBs ist die Leistung viel besser, da die Funktion ROW_NUMBER nur eine Spalte durchsuchen muss und nur die übereinstimmenden Zeilen mit allen Spalten zurückgegeben werden.

Arthur van Dijk
quelle
5

Siehe meine Auswahl für Paginator

SELECT TOP @limit * FROM (
   SELECT ROW_NUMBER() OVER (ORDER BY colunx ASC) offset, * FROM (

     -- YOU SELECT HERE
     SELECT * FROM mytable


   ) myquery
) paginator
WHERE offset > @offset

Dies löst die Paginierung;)

PerfectLion
quelle
3
SELECT TOP 75 * FROM MyTable
EXCEPT 
SELECT TOP 50 * FROM MyTable
Jithin Shaji
quelle
Die Leistung scheint nicht optimal zu sein, da die Abfrage dann unnötigerweise zweimal ausgeführt wird. Insbesondere wenn der Benutzer zu den höheren Seiten geht, dauert die Abfrage zum Verwerfen von Zeilen, dh des Teils unter EXCEPT, immer länger.
Vanval
2

Abhängig von Ihrer Version können Sie dies nicht direkt tun, aber Sie könnten etwas Hackiges tun

select top 25 *
from ( 
  select top 75 *
  from   table 
  order by field asc
) a 
order by field desc 

Dabei ist 'Feld' der Schlüssel.

Ungeschnitten
quelle
4
Diese Lösung für SQL2000 funktioniert nicht für die letzte Seite in der Ergebnismenge, es sei denn, die Gesamtzahl der Zeilen beträgt zufällig ein Vielfaches der Seitengröße.
Bill Karwin
2

Im Folgenden werden 25 Datensätze mit Ausnahme der ersten 50 Datensätze angezeigt, die in SQL Server 2012 ausgeführt werden.

SELECT * FROM MyTable ORDER BY ID OFFSET 50 ROWS FETCH NEXT 25 ROWS ONLY;

Sie können die ID als Ihre Anforderung ersetzen

Shb
quelle
Bitte fügen Sie auch hinzu, dass dies in SQL Server 2012
Usman Younas
2

Sie sollten vorsichtig sein, wenn Sie die ROW_NUMBER() OVER (ORDER BY)Anweisung verwenden, da die Leistung ziemlich schlecht ist. Gleiches gilt für die Verwendung von Common Table Expressions ROW_NUMBER(), was noch schlimmer ist. Ich verwende das folgende Snippet, das sich als etwas schneller erwiesen hat als die Verwendung einer Tabellenvariablen mit einer Identität zur Angabe der Seitenzahl.

DECLARE @Offset INT = 120000
DECLARE @Limit INT = 10

DECLARE @ROWCOUNT INT = @Offset+@Limit
SET ROWCOUNT @ROWCOUNT

SELECT * FROM MyTable INTO #ResultSet
WHERE MyTable.Type = 1

SELECT * FROM
(
    SELECT *, ROW_NUMBER() OVER(ORDER BY SortConst ASC) As RowNumber FROM
    (
        SELECT *, 1 As SortConst FROM #ResultSet
    ) AS ResultSet
) AS Page
WHERE RowNumber BETWEEN @Offset AND @ROWCOUNT

DROP TABLE #ResultSet
Patrik Melander
quelle
Dies wird 11 Zeilen zurückgeben, nicht 10.
Aaron Bertrand
1

Ich benutze diese Technik zur Paginierung. Ich hole nicht alle Zeilen. Wenn auf meiner Seite beispielsweise die obersten 100 Zeilen angezeigt werden müssen, rufe ich nur die 100 mit der where-Klausel ab. Die Ausgabe von SQL sollte einen eindeutigen Schlüssel haben.

Die Tabelle enthält Folgendes:

ID, KeyId, Rank

Der gleiche Rang wird für mehr als eine KeyId vergeben.

SQL ist select top 2 * from Table1 where Rank >= @Rank and ID > @Id

Zum ersten Mal übergebe ich 0 für beide. Der zweite Durchgang 1 & 14. 3. Durchgang 2 und 6 ....

Der Wert des 10. Datensatzes Rank & Id wird an den nächsten übergeben

11  21  1
14  22  1
7   11  1
6   19  2
12  31  2
13  18  2

Dies wird das System am wenigsten belasten

Ravi Ramaswamy
quelle
1

In SqlServer2005 können Sie Folgendes tun:

DECLARE @Limit INT
DECLARE @Offset INT
SET @Offset = 120000
SET @Limit = 10

SELECT 
    * 
FROM
(
   SELECT 
       row_number() 
   OVER 
      (ORDER BY column) AS rownum, column2, column3, .... columnX
   FROM   
     table
) AS A
WHERE 
 A.rownum BETWEEN (@Offset) AND (@Offset + @Limit-1) 
Aheho
quelle
Sollte es nicht sein @Offset + @Limit - 1? Wenn @Limit 10 ist, werden 11 Zeilen zurückgegeben.
Aaron Bertrand
1

Der beste Weg, dies zu tun, ohne Zeit für die Bestellung von Datensätzen zu verschwenden, ist folgender:

select 0 as tmp,Column1 from Table1 Order by tmp OFFSET 5000000 ROWS FETCH NEXT 50 ROWS ONLY

es dauert weniger als eine Sekunde!
beste Lösung für große Tische.

Pishgaman.org
quelle
0

Ich habe jetzt eine Weile nach dieser Antwort gesucht (für generische Abfragen) und eine andere Möglichkeit gefunden, dies unter SQL Server 2000+ mit ROWCOUNT und Cursorn und ohne TOP oder eine temporäre Tabelle zu tun.

Mit SET ROWCOUNT [OFFSET+LIMIT]können Sie die Ergebnisse einschränken und mit Cursorn direkt zu der gewünschten Zeile gehen und dann bis zum Ende eine Schleife ausführen.

Ihre Anfrage würde also so aussehen:

SET ROWCOUNT 75 -- (50 + 25)
DECLARE MyCursor SCROLL CURSOR FOR SELECT * FROM pessoas
OPEN MyCursor
FETCH ABSOLUTE 50 FROM MyCursor -- OFFSET
WHILE @@FETCH_STATUS = 0 BEGIN
    FETCH next FROM MyCursor
END
CLOSE MyCursor
DEALLOCATE MyCursor
SET ROWCOUNT 0
Capilé
quelle
Ich würde es hassen, die Leistung zu sehen, wenn Sie gegen Ende der Tabelle kommen ...
Aaron Bertrand
0

Mit SQL Server 2012 (11.x) und höher und Azure SQL Database können Sie auch "fetch_row_count_expression" und die ORDER BY-Klausel verwenden.

USE AdventureWorks2012;  
GO  
-- Specifying variables for OFFSET and FETCH values    
DECLARE @skip int = 0  , @take int = 8;  
SELECT DepartmentID, Name, GroupName  
FROM HumanResources.Department  
ORDER BY DepartmentID ASC   
    OFFSET @skip ROWS   
    FETCH NEXT @take ROWS ONLY; 

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

Hinweis OFFSET Gibt die Anzahl der zu überspringenden Zeilen an, bevor Zeilen aus dem Abfrageausdruck zurückgegeben werden. Es ist NICHT die Nummer der Startzeile. Es muss also 0 sein, um den ersten Datensatz einzuschließen.

Tejasvi Hegde
quelle