Come selezionare l'ennesima riga in una tabella del database SQL?


399

Sono interessato a imparare alcune (idealmente) di database agnostico modi per selezionare il n ° fila da una tabella del database. Sarebbe anche interessante vedere come questo può essere ottenuto utilizzando la funzionalità nativa dei seguenti database:

  • server SQL
  • MySQL
  • PostgreSQL
  • SQLite
  • Oracolo

Attualmente sto facendo qualcosa di simile al seguente in SQL Server 2005, ma sarei interessato a vedere altri approcci più agnostici:

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

Ringraziamo l'SQL sopra: il blog di Firoz Ansari

Aggiornamento: vedi la risposta di Troels Arvin in merito allo standard SQL. Troels, hai qualche link che possiamo citare?


3
Sì. Ecco un link alle informazioni sullo standard ISO SQL: troels.arvin.dk/db/rdbms/links/#standards
Troels Arvin,

13
Solo per sottolineare che dalla definizione di una relazione, le righe in una tabella non hanno ordine, quindi l'ennesima riga in una tabella non può essere selezionata. Ciò che può essere selezionato è l'ennesima riga in un set di righe restituito da (il resto di) una query, che è ciò che il tuo esempio e tutte le altre risposte realizzano. Per la maggior parte questo può essere solo una semantica, ma indica il problema alla base della domanda. Se è necessario tornare OrderNo N, quindi introdurre una colonna OrderSequenceNo nella tabella e generarla da un generatore di sequenza indipendente al momento della creazione di un nuovo ordine.
Damir Sudarevic,

2
Lo standard SQL definisce l'opzione offset x fetch first y rows only. Attualmente supportato da (almeno) Postgres, Oracle12, DB2.
a_horse_with_no_name

Risposte:


349

Esistono modi per farlo in parti opzionali dello standard, ma molti database supportano il proprio modo di farlo.

Un sito davvero buono che parla di questo e di altre cose è http://troels.arvin.dk/db/rdbms/#select-limit .

Fondamentalmente, PostgreSQL e MySQL supporta non standard:

SELECT...
LIMIT y OFFSET x 

Oracle, DB2 e MSSQL supporta le funzioni di windowing standard:

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

(che ho appena copiato dal sito collegato in precedenza poiché non uso mai quei DB)

Aggiornamento: a partire da PostgreSQL 8.4 sono supportate le funzioni di windowing standard, quindi aspettatevi che il secondo esempio funzioni anche per PostgreSQL.

Aggiornamento: SQLite ha aggiunto il supporto delle funzioni della finestra nella versione 3.25.0 del 15-09-2018, quindi entrambi i moduli funzionano anche in SQLite.


3
MySQL utilizza anche la sintassi OFFSET e LIMIT. Firebird utilizza le parole chiave FIRST e SKIP, ma vengono posizionate subito dopo SELECT.
Doug,

7
Non dovrebbe essere WHERE rownumber = nquello di ottenere solo l'ennesima fila?
Steve Bennett,

MySQL supporta le funzioni della finestra dalla versione 8. MariaDB dalla versione 10.2
Paul Spiegel il

102

PostgreSQL supporta le funzioni di windowing definite dallo standard SQL, ma sono scomode, quindi la maggior parte delle persone usa (non standard) LIMIT/OFFSET :

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

Questo esempio seleziona la 21a riga. OFFSET 20sta dicendo a Postgres di saltare i primi 20 record. Se non specifichi una ORDER BYclausola, non esiste alcuna garanzia su quale record otterrai, il che è raramente utile.


31

Non sono sicuro di tutto il resto, ma so che SQLite e MySQL non hanno alcun ordine di riga "predefinito". In quei due dialetti, almeno, il frammento seguente prende la 15a voce da the_table, ordinandola per data / ora in cui è stata aggiunta:

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

(ovviamente, dovresti avere un campo DATETIME aggiunto e impostarlo sulla data / ora in cui è stata aggiunta la voce ...)


Questo sembra il modo migliore per limitare la query con un valore di offset in linea. Ma non dovremmo usare 0,14 qui? 1,15 lascerà la prima riga.
Gladiatore,

Cosa significa il 15 però? So che 1 dice di ottenere un record. La virgola non viene utilizzato nell'esempio ho verificato 1keydata.com/sql/sql-limit.html
committedandroider

1
In realtà, da qui php.about.com/od/mysqlcommands/g/Limit_sql.htm , se volessi prendere la 15esima voce non dovresti fare LIMIT 14, 1 (0 ° è il primo elemento, 1 di lunghezza
commitandroider

sholud be SELEZIONA * DA the_table ORDINA DI aggiunto DESC LIMIT 15,1
JerryGoyal

25

SQL 2005 e versioni successive hanno questa funzione integrata. Utilizzare la funzione ROW_NUMBER (). È eccellente per le pagine Web con uno stile << Prec. E Succ. >>:

Sintassi:

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

Preferisco questa soluzione, poiché sembra più semplice.
FoxArc,

18

Sospetto che sia estremamente inefficiente, ma è un approccio piuttosto semplice, che ha funzionato su un piccolo set di dati su cui l'ho provato.

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

Questo otterrebbe il 5 ° oggetto, cambierebbe il secondo numero in alto per ottenere un altro n-esimo elemento

Solo server SQL (credo) ma dovrebbe funzionare su versioni precedenti che non supportano ROW_NUMBER ().


Lo userò perché ROW_NUMBER () non funziona in SQL 2000 (sì, abbiamo ancora un client su SQL 2000) In particolare, sostituirò '5' con una variabile iteratore di un ciclo e useremo che per copiare e modificare ogni riga di una tabella a turno. Forse qualcuno vedrà questo commento e lo troverà utile
Inversus,


14

Verifica su SQL Server:

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

Questo ti darà la decima riga della tabella emp!


Hai già fornito una risposta a questa domanda qui Elimina quella risposta che ritieni non sia corretta.Se pensi che entrambe le risposte siano giuste, allora pubblica entrambe le risposte in un unico posto
SpringLearner

11

Contrariamente a quanto sostengono alcune delle risposte, lo standard SQL non tace su questo argomento.

Da SQL: 2003, è stato possibile utilizzare le "funzioni finestra" per saltare le righe e limitare i set di risultati.

E in SQL: 2008, è stato aggiunto un approccio leggermente più semplice, usando
OFFSET skip ROWS FETCH FIRST n ROWS ONLY

Personalmente, non penso che l'aggiunta di SQL: 2008 fosse davvero necessaria, quindi se fossi stato ISO, l'avrei tenuto fuori da uno standard già piuttosto grande.


È bello che ci sia uno standard, rende le persone come me la vita più facili e così carino da parte di Microsoft fare le cose in modo standard :)
user230910

7

Quando lavoravamo in MSSQL 2000, facevamo quello che chiamavamo "triple-flip":

MODIFICATO

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

Non era elegante e non era veloce, ma ha funzionato.


Supponi di avere 25 righe e desideri la terza pagina di 10 pagine con le righe, ovvero le righe 21-25. La query più interna ottiene le prime 30 righe (righe 1-25). La query centrale ottiene le ultime 10 righe (righe 25-16). La query esterna li riordina e restituisce le righe 16-25. Questo è chiaramente sbagliato se si volevano le righe 21-25.
Bill Karwin,

Ora non funziona se vogliamo una pagina intermedia. Supponiamo di avere 25 righe e vogliamo la seconda pagina, ovvero le righe 11-20. La query interna ottiene le prime 2 * 10 = 20 righe o le righe 1-20. La query centrale ottiene le ultime 15 righe: 25 - ((2-1) * 10) = 15, che restituisce le righe 20-6. L'ultima query inverte l'ordine e restituisce le righe 6-20. Questa tecnica non funziona, a meno che il numero totale di righe sia un multiplo della dimensione della pagina desiderata.
Bill Karwin,

Forse la migliore conclusione è che dovremmo aggiornare tutte le rimanenti istanze di MS SQL Server 2000. :-) È quasi il 2012 e questo problema è stato risolto in modo migliore per molti anni!
Bill Karwin,

@ Bill Karwin: nota i IF / ELSE IFblocchi sotto il OuterPageSizecalcolo - alle pagine 1 e 2, lasceranno cadere ilOuterPageSize valore riportato a 10. Alla pagina 3 (righe 21-25) il calcolo restituirà correttamente 5 e su tutte le pagine 4 e successive, il risultato negativo dal calcolo verrà sostituito da 0 (anche se probabilmente sarebbe più veloce restituire immediatamente una riga di dati vuota in quel punto).
Adam V

Oh vedo ora. Bene, sono d'accordo con la mia opinione che l'utilizzo di MS SQL Server 2000 oggi non valga la pena.
Bill Karwin

6

SERVER SQL


Seleziona n ° record dall'alto

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

seleziona n ° record dal basso

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

5

Oracolo:

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

1
where ROWNUM = xfunzionerà solo per x = 1 in Oracle DB. cioè where ROWNUM = 2non restituirà alcuna riga.
aff.

5

In Oracle 12c, è possibile utilizzare l' OFFSET..FETCH..ROWS opzione conORDER BY

Ad esempio, per ottenere il 3 ° record dall'alto:

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

4

Ecco una rapida soluzione della tua confusione.

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

Qui puoi ottenere l'ultima riga riempiendo N = 0, la seconda ultima N = 1, la quarta ultima riempiendo N = 3 e così via.

Questa è una domanda molto comune durante l'intervista e questa è molto semplice.

Inoltre, se si desidera quantità, ID o un certo ordinamento numerico, è possibile utilizzare la funzione CAST in MySQL.

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

Qui Compilando N = 4 Sarai in grado di ottenere il quinto ultimo record della quantità più alta dalla tabella CART. Puoi adattare il tuo campo e il nome della tabella e trovare una soluzione.


3

INSERISCI:

LIMIT n,1

Ciò limiterà i risultati a un risultato a partire dal risultato n.


3

Ad esempio, se si desidera selezionare ogni 10a riga in MSSQL, è possibile utilizzare;

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

Basta prendere il MOD e cambiare il numero 10 qui qualsiasi numero desiderato.


3

Per SQL Server, un modo generico di andare per numero di riga è il seguente:

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

Per esempio:

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

Questo restituirà le informazioni sulla 20a fila. Assicurati di inserire il conteggio delle righe 0 in seguito.


2

LIMIT n, 1 non funziona in MS SQL Server. Penso che sia solo l'unico grande database che non supporta quella sintassi. Ad essere onesti, non fa parte dello standard SQL, anche se è così ampiamente supportato che dovrebbe essere. In tutto tranne SQL server LIMIT funziona alla grande. Per SQL Server, non sono stato in grado di trovare una soluzione elegante.


1
Ad eccezione di Oracle, DB2, praticamente ogni database di livello aziendale in tutto il mondo. PostgreSQL riguarda il solo database aziendale capace di supportare la parola chiave LIMIT, ed è principalmente perché essendo open source deve essere accessibile dalla folla ignorante di MySQL ACID.
David

3
@AlexD Questa "risposta" è stata postata ai vecchi tempi di StackOverflow prima dell'implementazione dei commenti. Avrei pubblicato questo come commento ad un'altra risposta, ma a tempo i commenti non esistevano.
Kibbee,

2

Ecco una versione generica di uno sproc che ho scritto di recente per Oracle che consente il paging / ordinamento dinamico - 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

Ma davvero, non sono davvero solo trucchi da salotto per una buona progettazione del database? Le poche volte in cui ho avuto bisogno di funzionalità come questa è stato per una semplice query una tantum fare un rapporto rapido. Per qualsiasi lavoro reale, usare trucchi come questi è un problema invitante. Se è necessaria la selezione di una riga specifica, è sufficiente disporre di una colonna con un valore sequenziale e procedere con essa.


2

Per il server SQL, quanto segue restituirà la prima riga dalla tabella di assegnazione.

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

Puoi scorrere i valori con qualcosa del genere:

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

Non dimenticare ORDER BY o non ha senso.


1

T-SQL: selezione di N'th RecordNumber da una tabella

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

Ad esempio, per selezionare il 5 ° record da una tabella Employee, la query dovrebbe essere

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

Ho scritto questa domanda per trovare l'ennesima riga. Esempio con questa query sarebbe

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

incredibile che puoi trovare un motore SQL che esegue questo ...

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

Niente di speciale, nessuna funzione speciale, nel caso in cui usi Caché come faccio io ...

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

Dato che hai una colonna ID o una colonna datestamp di cui ti puoi fidare.


0

È così che lo farei all'interno di DB2 SQL, credo che il RRN (numero record relativo) sia memorizzato nella tabella dall'O / S;

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;

Prima seleziona le prime 100 righe ordinando in ordine crescente, quindi seleziona l'ultima riga ordinando in ordine decrescente e limitando a 1. Tuttavia questa è una dichiarazione molto costosa in quanto accede ai dati due volte.


0

Mi sembra che, per essere efficiente, sia necessario 1) generare un numero casuale compreso tra 0 e uno in meno rispetto al numero di record del database e 2) essere in grado di selezionare la riga in quella posizione. Sfortunatamente, database diversi hanno generatori di numeri casuali diversi e modi diversi di selezionare una riga in una posizione in un set di risultati - di solito si specifica quante righe saltare e quante righe si desidera, ma viene fatto in modo diverso per database diversi. Ecco qualcosa che funziona per me in SQLite:

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

Dipende dal fatto di poter usare una sottoquery nella clausola limit (che in SQLite è LIMIT <recs da saltare>, <recs da prendere>) La selezione del numero di record in una tabella dovrebbe essere particolarmente efficiente, essendo parte del database metadati, ma ciò dipende dall'implementazione del database. Inoltre, non so se la query realizzerà effettivamente il set di risultati prima di recuperare l'ennesimo record, ma spero che non sia necessario. Nota che non sto specificando una clausola "ordina per". Potrebbe essere meglio "ordinare per" qualcosa come la chiave primaria, che avrà un indice - ottenere l'ennesimo record da un indice potrebbe essere più veloce se il database non può ottenere l'ennesimo record dal database stesso senza creare il set di risultati .


0

La risposta più adatta che ho visto in questo articolo per SQL Server

WITH myTableWithRows AS (
    SELECT (ROW_NUMBER() OVER (ORDER BY myTable.SomeField)) as row,*
    FROM myTable)
SELECT * FROM myTableWithRows WHERE row = 3
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.