Wie wähle ich die n-te Zeile in einer SQL-Datenbanktabelle aus?


399

Ich bin daran interessiert, einige (idealerweise) datenbankunabhängige Methoden zur Auswahl der n- ten Zeile aus einer Datenbanktabelle zu lernen . Es wäre auch interessant zu sehen, wie dies mit der nativen Funktionalität der folgenden Datenbanken erreicht werden kann:

  • SQL Server
  • MySQL
  • PostgreSQL
  • SQLite
  • Orakel

Ich mache derzeit in SQL Server 2005 so etwas wie das Folgende, aber ich wäre daran interessiert, die agnostischeren Ansätze anderer zu sehen:

WITH Ordered AS (
SELECT ROW_NUMBER() OVER (ORDER BY OrderID) AS RowNumber, OrderID, OrderDate
FROM Orders)
SELECT *
FROM Ordered
WHERE RowNumber = 1000000

Gutschrift für das oben genannte SQL: Firoz Ansaris Weblog

Update: Siehe Troels Arvins Antwort zum SQL-Standard. Troels, hast du irgendwelche Links, die wir zitieren können?


3
Ja. Hier ist ein Link zu Informationen über den ISO SQL-Standard: troels.arvin.dk/db/rdbms/links/#standards
Troels Arvin

13
Nur um darauf hinzuweisen, dass nach der Definition einer Beziehung Zeilen in einer Tabelle keine Reihenfolge haben, sodass die N-te Zeile in einer Tabelle nicht ausgewählt werden kann. Was ausgewählt werden kann, ist die N-te Zeile in einem Zeilensatz, der von (dem Rest) einer Abfrage zurückgegeben wird. Dies ist das, was Ihr Beispiel und alle anderen Antworten bewirken. Für die meisten mag dies nur Semantik sein, aber es weist auf das zugrunde liegende Problem der Frage hin. Wenn Sie zurückkehren müssen OrderNo N, fügen Sie eine OrderSequenceNo- Spalte in die Tabelle ein und generieren Sie sie beim Erstellen einer neuen Bestellung aus einem unabhängigen Sequenzgenerator .
Damir Sudarevic

2
Der SQL-Standard definiert die Option offset x fetch first y rows only. Wird derzeit von (mindestens) Postgres, Oracle12, DB2 unterstützt.
a_horse_with_no_name

Antworten:


349

Es gibt Möglichkeiten, dies in optionalen Teilen des Standards zu tun, aber viele Datenbanken unterstützen ihre eigene Vorgehensweise.

Eine wirklich gute Seite, die über dieses und andere Dinge spricht, ist http://troels.arvin.dk/db/rdbms/#select-limit .

Grundsätzlich unterstützen PostgreSQL und MySQL das Nicht-Standard:

SELECT...
LIMIT y OFFSET x 

Oracle, DB2 und MSSQL unterstützen die Standardfensterfunktionen:

SELECT * FROM (
  SELECT
    ROW_NUMBER() OVER (ORDER BY key ASC) AS rownumber,
    columns
  FROM tablename
) AS foo
WHERE rownumber <= n

(die ich gerade von der oben verlinkten Seite kopiert habe, da ich diese DBs nie benutze)

Update: Ab PostgreSQL 8.4 werden die Standardfensterfunktionen unterstützt. Erwarten Sie daher, dass das zweite Beispiel auch für PostgreSQL funktioniert.

Update: SQLite hat am 15.09.2018 Unterstützung für Fensterfunktionen in Version 3.25.0 hinzugefügt, sodass beide Formulare auch in SQLite funktionieren.


3
MySQL verwendet auch die OFFSET- und LIMIT-Syntax. Firebird verwendet die Schlüsselwörter FIRST und SKIP, diese werden jedoch direkt nach SELECT platziert.
Doug

7
Sollte das nicht WHERE rownumber = nnur die n-te Reihe sein?
Steve Bennett

MySQL unterstützt Fensterfunktionen seit Version 8. MariaDB seit Version 10.2
Paul Spiegel

102

PostgreSQL unterstützt Fensterfunktionen, wie sie im SQL-Standard definiert sind, aber sie sind umständlich, sodass die meisten Benutzer (den Nicht-Standard) LIMIT/ verwendenOFFSET :

SELECT
    *
FROM
    mytable
ORDER BY
    somefield
LIMIT 1 OFFSET 20;

In diesem Beispiel wird die 21. Zeile ausgewählt. OFFSET 20fordert Postgres auf, die ersten 20 Datensätze zu überspringen. Wenn Sie keine ORDER BYKlausel angeben , gibt es keine Garantie dafür, welchen Datensatz Sie zurückerhalten, was selten nützlich ist.


31

Ich bin mir über den Rest nicht sicher, aber ich weiß, dass SQLite und MySQL keine "Standard" -Zeilenreihenfolge haben. Zumindest in diesen beiden Dialekten erfasst das folgende Snippet den 15. Eintrag aus der Tabelle und sortiert nach dem Datum / der Uhrzeit, zu der es hinzugefügt wurde:

SELECT * FROM the_table ORDER BY added DESC LIMIT 1,15

(Natürlich müssten Sie ein DATETIME-Feld hinzufügen und es auf das Datum / die Uhrzeit einstellen, zu der der Eintrag hinzugefügt wurde ...)


Dies scheint der beste Weg zu sein, um die Abfrage mit einem Inline-Offset-Wert einzuschränken. Aber sollten wir hier nicht 0,14 verwenden? 1,15 verlassen die erste Reihe.
Gladiator

Was bedeutet die 15? Ich weiß, dass die 1 sagt, um eine Platte zu bekommen. Das Komma wird im Beispiel nicht verwendet ich ausgecheckt 1keydata.com/sql/sql-limit.html
committedandroider

1
Von hier aus, php.about.com/od/mysqlcommands/g/Limit_sql.htm , wenn Sie den 15. Eintrag holen wollten, würden Sie nicht LIMIT 14, 1 (0 ist das erste Element, 1 der Länge
Commitandroider

es sollte SELECT * FROM the_table ORDER BY hinzugefügt DESC LIMIT 15,1
JerryGoyal

25

In SQL 2005 und höher ist diese Funktion integriert. Verwenden Sie die Funktion ROW_NUMBER (). Es eignet sich hervorragend für Webseiten mit einem Browsing im Stil << Zurück und Weiter >>:

Syntax:

SELECT
    *
FROM
    (
        SELECT
            ROW_NUMBER () OVER (ORDER BY MyColumnToOrderBy) AS RowNum,
            *
        FROM
            Table_1
    ) sub
WHERE
    RowNum = 23

Ich bevorzuge diese Lösung, da sie sich direkter anfühlt.
FoxArc

18

Ich vermute, dass dies äußerst ineffizient ist, aber ein recht einfacher Ansatz ist, der an einem kleinen Datensatz funktioniert hat, an dem ich ihn ausprobiert habe.

select top 1 field
from table
where field in (select top 5 field from table order by field asc)
order by field desc

Dies würde den 5. Artikel erhalten, die zweite Top-Nummer ändern, um einen anderen n-ten Artikel zu erhalten

Nur SQL Server (glaube ich), sollte aber auf älteren Versionen funktionieren, die ROW_NUMBER () nicht unterstützen.


Ich werde dies verwenden, da ROW_NUMBER () in SQL 2000 nicht funktioniert (ja, wir haben noch einen Client in SQL 2000). Insbesondere werde ich die '5' durch eine Iteratorvariable einer Schleife ersetzen und verwenden das, um jede Zeile einer Tabelle der Reihe nach zu kopieren und zu ändern. Vielleicht wird jemand diesen Kommentar sehen und dies nützlich finden
Inversus


14

Überprüfen Sie es auf SQL Server:

Select top 10 * From emp 
EXCEPT
Select top 9 * From emp

Dies gibt Ihnen die 10. REIHE der Emp-Tabelle!


Sie haben bereits eine Antwort auf diese Frage hier löschen , die Antwort , die Sie denken nicht ist rechts drehen.Wenn Sie beide Antworten denken , sind rechts und dann schreiben die beide die Antworten in einem Ort
SpringLearner

11

Im Gegensatz zu einigen Antworten schweigt der SQL-Standard zu diesem Thema nicht.

Seit SQL: 2003 können Sie "Fensterfunktionen" verwenden, um Zeilen zu überspringen und Ergebnismengen einzuschränken.

In SQL: 2008 wurde ein etwas einfacherer Ansatz hinzugefügt
OFFSET skip ROWS FETCH FIRST n ROWS ONLY

Persönlich glaube ich nicht, dass die Hinzufügung von SQL: 2008 wirklich notwendig war. Wenn ich also ISO wäre, hätte ich es aus einem bereits ziemlich großen Standard herausgehalten.


Es ist schön, dass es einen Standard gibt, der Menschen wie mir das Leben leichter macht und so nett von Microsoft ist,
Dinge auf

7

Als wir in MSSQL 2000 gearbeitet haben, haben wir das gemacht, was wir "Triple-Flip" genannt haben:

BEARBEITET

DECLARE @InnerPageSize int
DECLARE @OuterPageSize int
DECLARE @Count int

SELECT @Count = COUNT(<column>) FROM <TABLE>
SET @InnerPageSize = @PageNum * @PageSize
SET @OuterPageSize = @Count - ((@PageNum - 1) * @PageSize)

IF (@OuterPageSize < 0)
    SET @OuterPageSize = 0
ELSE IF (@OuterPageSize > @PageSize)
    SET @OuterPageSize = @PageSize

DECLARE @sql NVARCHAR(8000)

SET @sql = 'SELECT * FROM
(
    SELECT TOP ' + CAST(@OuterPageSize AS nvarchar(5)) + ' * FROM
    (
        SELECT TOP ' + CAST(@InnerPageSize AS nvarchar(5)) + ' * FROM <TABLE> ORDER BY <column> ASC
    ) AS t1 ORDER BY <column> DESC
) AS t2 ORDER BY <column> ASC'

PRINT @sql
EXECUTE sp_executesql @sql

Es war nicht elegant und nicht schnell, aber es funktionierte.


Angenommen, Sie haben 25 Zeilen und möchten die dritte Seite mit 10 Zeilen Seitengröße, dh Zeilen 21-25. Die innerste Abfrage erhält die obersten 30 Zeilen (Zeilen 1-25). Die mittlere Abfrage erhält die letzten 10 Zeilen (Zeilen 25-16). Die äußere Abfrage ordnet sie neu und gibt die Zeilen 16-25 zurück. Dies ist eindeutig falsch, wenn Sie die Zeilen 21-25 wollten.
Bill Karwin

Jetzt funktioniert es nicht, wenn wir eine mittlere Seite wollen. Angenommen, wir haben 25 Zeilen und möchten die zweite Seite, dh die Zeilen 11-20. Die innere Abfrage erhält die obersten 2 * 10 = 20 Zeilen oder Zeilen 1-20. Die mittlere Abfrage erhält die letzten 15 Zeilen: 25 - ((2-1) * 10) = 15, was die Zeilen 20-6 ergibt. Die letzte Abfrage kehrt die Reihenfolge um und gibt die Zeilen 6-20 zurück. Diese Technik funktioniert nur, wenn die Gesamtzahl der Zeilen ein Vielfaches der gewünschten Seitengröße beträgt.
Bill Karwin

Die vielleicht beste Schlussfolgerung ist, dass wir alle verbleibenden MS SQL Server 2000-Instanzen aktualisieren sollten. :-) Es ist fast 2012 und dieses Problem wurde seit vielen Jahren besser gelöst!
Bill Karwin

@ Bill Karwin: Beachten Sie die IF / ELSE IFBlöcke unter der OuterPageSizeBerechnung - auf den Seiten 1 und 2 wird der OuterPageSizeWert auf 10 zurückgesetzt. Auf Seite 3 (Zeilen 21-25) gibt die Berechnung korrekt 5 zurück und auf allen Seiten 4 und höher. Das negative Ergebnis der Berechnung wird durch 0 ersetzt (obwohl es wahrscheinlich nur schneller wäre, eine leere Datenzeile sofort an diesem Punkt zurückzugeben).
Adam V

Oh, ich verstehe jetzt. Nun, ich stehe zu meiner Meinung, dass die Verwendung von MS SQL Server 2000 heute die Mühe nicht wert ist.
Bill Karwin

6

SQL SERVER


Wählen Sie den n-ten Datensatz von oben aus

SELECT * FROM (
SELECT 
ID, NAME, ROW_NUMBER() OVER(ORDER BY ID) AS ROW
FROM TABLE 
) AS TMP 
WHERE ROW = n

Wählen Sie den n-ten Datensatz von unten aus

SELECT * FROM (
SELECT 
ID, NAME, ROW_NUMBER() OVER(ORDER BY ID DESC) AS ROW
FROM TABLE 
) AS TMP 
WHERE ROW = n

5

Orakel:

select * from (select foo from bar order by foo) where ROWNUM = x

1
where ROWNUM = xfunktioniert nur für x = 1 in Oracle DB. dh es where ROWNUM = 2werden keine Zeilen zurückgegeben.
Aff

5

In Oracle 12c können Sie die OFFSET..FETCH..ROWS Option mit verwendenORDER BY

Zum Beispiel, um den 3. Datensatz von oben zu erhalten:

SELECT * 
FROM   sometable
ORDER BY column_name
OFFSET 2 ROWS FETCH NEXT 1 ROWS ONLY;

4

Hier ist eine schnelle Lösung Ihrer Verwirrung.

SELECT * FROM table ORDER BY `id` DESC LIMIT N, 1

Hier können Sie die letzte Zeile durch Füllen von N = 0, die vorletzte durch N = 1, die vierte letzte durch Füllen von N = 3 usw. erhalten.

Dies ist eine sehr häufige Frage während des Interviews und dies ist eine sehr einfache Antwort darauf.

Weiter Wenn Sie Betrag, ID oder eine numerische Sortierreihenfolge wünschen, können Sie sich für die CAST-Funktion in MySQL entscheiden.

SELECT DISTINCT (`amount`) FROM cart ORDER BY CAST( `amount` AS SIGNED ) DESC LIMIT 4 , 1

Hier Durch Ausfüllen von N = 4 können Sie die fünftletzte Aufzeichnung der höchsten Menge aus der CART-Tabelle erhalten. Sie können Ihren Feld- und Tabellennamen anpassen und eine Lösung finden.


3

HINZUFÜGEN:

LIMIT n,1

Dadurch werden die Ergebnisse ab Ergebnis n auf ein Ergebnis begrenzt.


3

Wenn Sie beispielsweise jede 10. Zeile in MSSQL auswählen möchten, können Sie Folgendes verwenden:

SELECT * FROM (
  SELECT
    ROW_NUMBER() OVER (ORDER BY ColumnName1 ASC) AS rownumber, ColumnName1, ColumnName2
  FROM TableName
) AS foo
WHERE rownumber % 10 = 0

Nehmen Sie einfach den MOD und ändern Sie hier die Nummer 10 nach Belieben.


3

Für SQL Server ist ein generischer Weg, nach Zeilennummer zu gehen, wie folgt:

SET ROWCOUNT @row --@row = the row number you wish to work on.

Zum Beispiel:

set rowcount 20   --sets row to 20th row

select meat, cheese from dbo.sandwich --select columns from table at 20th row

set rowcount 0   --sets rowcount back to all rows

Dies gibt die Informationen der 20. Zeile zurück. Stellen Sie sicher, dass Sie danach die Zeilenanzahl 0 eingeben.


2

LIMIT n, 1 funktioniert in MS SQL Server nicht. Ich denke, es ist nur die einzige große Datenbank, die diese Syntax nicht unterstützt. Um fair zu sein, ist es nicht Teil des SQL-Standards, obwohl es so weit verbreitet ist, dass es sein sollte. In allem außer SQL Server funktioniert LIMIT hervorragend. Für SQL Server konnte ich keine elegante Lösung finden.


1
Mit Ausnahme von Oracle, DB2, nahezu jeder Datenbank auf Unternehmensebene auf der ganzen Welt. PostgreSQL ist die einzige unternehmensfähige Datenbank, die das Schlüsselwort LIMIT unterstützt. Dies liegt hauptsächlich daran, dass Open Source für die ACID-ignorierende MySQL-Masse zugänglich sein muss.
David

3
@AlexD Diese "Antwort" wurde in den alten Tagen von Stackoverflow veröffentlicht, bevor Kommentare implementiert wurden. Ich hätte dies als Kommentar zu einer anderen Antwort gepostet, aber zu diesem Zeitpunkt gab es keine Kommentare.
Kibbee

2

Hier ist eine generische Version eines Sproc, den ich kürzlich für Oracle geschrieben habe und der dynamisches Paging / Sortieren ermöglicht - HTH

-- p_LowerBound = first row # in the returned set; if second page of 10 rows,
--                this would be 11 (-1 for unbounded/not set)
-- p_UpperBound = last row # in the returned set; if second page of 10 rows,
--                this would be 20 (-1 for unbounded/not set)

OPEN o_Cursor FOR
SELECT * FROM (
SELECT
    Column1,
    Column2
    rownum AS rn
FROM
(
    SELECT
        tbl.Column1,
        tbl.column2
    FROM MyTable tbl
    WHERE
        tbl.Column1 = p_PKParam OR
        tbl.Column1 = -1
    ORDER BY
        DECODE(p_sortOrder, 'A', DECODE(p_sortColumn, 1, Column1, 'X'),'X'),
        DECODE(p_sortOrder, 'D', DECODE(p_sortColumn, 1, Column1, 'X'),'X') DESC,
        DECODE(p_sortOrder, 'A', DECODE(p_sortColumn, 2, Column2, sysdate),sysdate),
        DECODE(p_sortOrder, 'D', DECODE(p_sortColumn, 2, Column2, sysdate),sysdate) DESC
))
WHERE
    (rn >= p_lowerBound OR p_lowerBound = -1) AND
    (rn <= p_upperBound OR p_upperBound = -1);

2

Aber wirklich, ist das nicht wirklich nur ein Salon-Trick für ein gutes Datenbankdesign? Die wenigen Male, die ich solche Funktionen benötigte, war es für eine einfache einmalige Abfrage, einen schnellen Bericht zu erstellen. Für jede echte Arbeit führt die Verwendung solcher Tricks zu Problemen. Wenn die Auswahl einer bestimmten Zeile erforderlich ist, haben Sie einfach eine Spalte mit einem sequentiellen Wert und fertig.


2

Für SQL Server wird im Folgenden die erste Zeile aus der angegebenen Tabelle zurückgegeben.

declare @rowNumber int = 1;
    select TOP(@rowNumber) * from [dbo].[someTable];
EXCEPT
    select TOP(@rowNumber - 1) * from [dbo].[someTable];

Sie können die Werte folgendermaßen durchlaufen:

WHILE @constVar > 0
BEGIN
    declare @rowNumber int = @consVar;
       select TOP(@rowNumber) * from [dbo].[someTable];
    EXCEPT
       select TOP(@rowNumber - 1) * from [dbo].[someTable];  

       SET @constVar = @constVar - 1;    
END;

1

In Sybase SQL Anywhere:

SELECT TOP 1 START AT n * from table ORDER BY whatever

Vergessen Sie nicht die Bestellung von oder es ist bedeutungslos.


1

T-SQL - Auswahl der N-ten Datensatznummer aus einer Tabelle

select * from
 (select row_number() over (order by Rand() desc) as Rno,* from TableName) T where T.Rno = RecordNumber

Where  RecordNumber --> Record Number to Select
       TableName --> To be Replaced with your Table Name

Um beispielsweise den 5. Datensatz aus einer Tabelle Mitarbeiter auszuwählen, sollte Ihre Abfrage lauten

select * from
 (select row_number() over (order by Rand() desc) as Rno,* from Employee) T where T.Rno = 5

1
SELECT * FROM emp a
WHERE  n = (SELECT COUNT( _rowid)
              FROM emp b
             WHERE a. _rowid >= b. _rowid);

1
SELECT
    top 1 *
FROM
    table_name
WHERE
    column_name IN (
        SELECT
            top N column_name
        FROM
            TABLE
        ORDER BY
            column_name
    )
ORDER BY
    column_name DESC

Ich habe diese Abfrage geschrieben, um die N-te Zeile zu finden. Beispiel mit dieser Abfrage wäre

SELECT
    top 1 *
FROM
    Employee
WHERE
    emp_id IN (
        SELECT
            top 7 emp_id
        FROM
            Employee
        ORDER BY
            emp_id
    )
ORDER BY
    emp_id DESC

0

Es ist unglaublich, dass Sie eine SQL-Engine finden, die diese ausführt ...

WITH sentence AS
(SELECT 
    stuff,
    row = ROW_NUMBER() OVER (ORDER BY Id)
FROM 
    SentenceType
    )
SELECT
    sen.stuff
FROM sentence sen
WHERE sen.row = (ABS(CHECKSUM(NEWID())) % 100) + 1

0

Nichts Besonderes, keine besonderen Funktionen, falls Sie Caché wie ich verwenden ...

SELECT TOP 1 * FROM (
  SELECT TOP n * FROM <table>
  ORDER BY ID Desc
)
ORDER BY ID ASC

Vorausgesetzt, Sie haben eine ID-Spalte oder eine Datenstempelspalte, der Sie vertrauen können.


0

So würde ich es in DB2 SQL machen. Ich glaube, die RRN (relative Datensatznummer) wird vom Betriebssystem in der Tabelle gespeichert.

SELECT * FROM (                        
   SELECT RRN(FOO) AS RRN, FOO.*
   FROM FOO                         
   ORDER BY RRN(FOO)) BAR             
 WHERE BAR.RRN = recordnumber

0
select * from 
(select * from ordered order by order_id limit 100) x order by 
x.order_id desc limit 1;

Wählen Sie zuerst die obersten 100 Zeilen in aufsteigender Reihenfolge und dann die letzte Zeile in absteigender Reihenfolge und begrenzen Sie sie auf 1. Dies ist jedoch eine sehr teure Aussage, da zweimal auf die Daten zugegriffen wird.


0

Es scheint mir, dass Sie, um effizient zu sein, 1) eine Zufallszahl zwischen 0 und eins weniger als die Anzahl der Datenbankeinträge generieren müssen und 2) die Zeile an dieser Position auswählen können. Leider haben verschiedene Datenbanken unterschiedliche Zufallszahlengeneratoren und unterschiedliche Möglichkeiten, eine Zeile an einer Position in einer Ergebnismenge auszuwählen. Normalerweise geben Sie an, wie viele Zeilen übersprungen werden sollen und wie viele Zeilen Sie möchten. Für verschiedene Datenbanken wird dies jedoch unterschiedlich ausgeführt. Hier ist etwas, das für mich in SQLite funktioniert:

select * 
from Table 
limit abs(random()) % (select count(*) from Words), 1;

Es hängt davon ab, ob eine Unterabfrage in der Limit-Klausel verwendet werden kann (in SQLite LIMIT <zu überspringende Recs>, <zu nehmende Recs>). Die Auswahl der Anzahl der Datensätze in einer Tabelle sollte besonders effizient sein und Teil der Datenbank sein Metadaten, aber das hängt von der Implementierung der Datenbank ab. Ich weiß auch nicht, ob die Abfrage tatsächlich die Ergebnismenge erstellt, bevor der N-te Datensatz abgerufen wird, aber ich würde hoffen, dass dies nicht erforderlich ist. Beachten Sie, dass ich keine "order by" -Klausel spezifiziere. Es ist möglicherweise besser, nach dem Primärschlüssel zu "ordnen", der einen Index enthält. Das Abrufen des N-ten Datensatzes aus einem Index ist möglicherweise schneller, wenn die Datenbank den N-ten Datensatz nicht aus der Datenbank selbst abrufen kann, ohne die Ergebnismenge zu erstellen .


0

Die am besten geeignete Antwort, die ich in diesem Artikel für SQL Server gesehen habe

WITH myTableWithRows AS (
    SELECT (ROW_NUMBER() OVER (ORDER BY myTable.SomeField)) as row,*
    FROM myTable)
SELECT * FROM myTableWithRows WHERE row = 3
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.