LIMIT 10..20 in SQL Server


161

Sto cercando di fare qualcosa del tipo:

SELECT * FROM table LIMIT 10,20

o

SELECT * FROM table LIMIT 10 OFFSET 10

ma utilizzando SQL Server

L'unica soluzione che ho trovato sembra eccessiva:

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

Ho anche trovato :

SELECT TOP 10 * FROM stuff; 

... ma non è quello che voglio fare poiché non riesco a specificare il limite iniziale.

C'è un altro modo per farlo?

Inoltre, solo curioso, c'è un motivo per cui SQL Server non supporta la LIMITfunzione o qualcosa di simile? Non voglio essere cattivo, ma sembra davvero qualcosa di cui un DBMS ha bisogno ... Se lo fa, allora mi dispiace di essere così ignorante! Ho lavorato con MySQL e SQL + negli ultimi 5 anni, quindi ...


1
L'uso di un CTE per ROW_NUMBER()e la limitazione con TOPper l'ampiezza dell'intervallo e una WHEREcondizione per un limite dell'intervallo è il migliore che sono riuscito a raggiungere. Ho anche notato prestazioni molto migliori se la TOPclausola utilizza un valore letterale anziché variabile
Jodrell

Il problema con qualsiasi soluzione che coinvolge ROW_NUMBER () è che se non sai in anticipo quali colonne avrai, e avrai dei join e le tabelle unite avranno lo stesso nome di colonna, otterrai un "La colonna "xxx" è stato specificato più volte ". Questo non è così insolito come potrebbe sembrare inizialmente. Uso Dapper e le mie tabelle hanno tutte una colonna Id. Dapper si divide e ci mappa, quindi non voglio rinominarli, ma non posso usare l'alias SELECT * FROM ([query originale]). Non ho ancora trovato una soluzione!
Steve Owen,

Risposte:


104

La LIMITclausola non fa parte di SQL standard. È supportato come estensione del fornitore per SQL da MySQL, PostgreSQL e SQLite.

Altre marche di database potrebbero avere caratteristiche simili (ad es. TOPIn Microsoft SQL Server), ma non sempre funzionano in modo identico.

È difficile da utilizzare TOPin Microsoft SQL Server per imitare la LIMITclausola. Ci sono casi in cui semplicemente non funziona.

La soluzione che hai mostrato, utilizzando ROW_NUMBER()è disponibile in Microsoft SQL Server 2005 e versioni successive. Questa è la soluzione migliore (per ora) che funziona esclusivamente come parte della query.

Un'altra soluzione è utilizzare TOPper recuperare il primo conteggio + righe di offset , quindi utilizzare l'API per cercare oltre le prime righe di offset .

Guarda anche:


135

Per SQL Server 2012 + è possibile utilizzare .

SELECT  *
FROM     sys.databases
ORDER BY name 
OFFSET  5 ROWS 
FETCH NEXT 5 ROWS ONLY 

10
SQl Server 2012 richiede di specificare ORDER BY quando si utilizza OFFSET 5 ROWS FETCH NEXT 5 ROWS SOLO mentre MySql e SQLite non richiedono ORDER BY quando si utilizza LIMIT 5,5
Tomas Kubes

4
@ qub1n - MySQL non garantisce tuttavia quali righe si ottengano in quel caso.
Martin Smith,

3
Devi usare offseto puoi lasciare quella linea (supponendo che non desideri un offset)?
Cullub,


La tua query di esempio funziona bene, ma se cambio il nome della tabella e ordina per col come sotto SELEZIONA * DA DimProduct ORDINA PER ProductKey OFFSET 5 FILE FETCH SUCCESSIVO 5 FILE SOLO Dà erroreParse error at line: 4, column: 1: Incorrect syntax near 'OFFSET'
shashwat

36

come hai trovato, questo è il metodo server SQL preferito:

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

Perché il adopo l'interno seleziona? Suppongo che stai dando un alias alla selezione interna, ma poi non sembri mai usarlo ... Dovresti fare a.rowinvece invece di solo row?
Lucas,

3
@Lucas, ti viene richiesto di inserire un alias dopo la ( )tabella derivata, ma lo lascerà andare se ti dimentichi di usarlo per fare riferimento alle colonne. L'ho risolto però ...
KM.

grazie, l'ho scoperto nel modo più duro (ho cercato di lasciare fuori l'alias).
Lucas,

1
Votato +1: Tuttavia, la risposta di @MartinSmith viene votata di più, dopo aver confrontato il piano di esecuzione con quello di questo approccio, ho scoperto che questa soluzione funziona molto più velocemente.
Duro

10

Se si utilizza il voto di SQL Server 2012+ per la risposta di Martin Smith e si utilizzano le estensioni OFFSETe ,FETCH NEXTORDER BY

Se sei abbastanza sfortunato da rimanere bloccato con una versione precedente, potresti fare qualcosa del genere,

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

Credo funzionalmente equivalente a

SELECT * FROM SomeTable LIMIT 10 OFFSET 10 ORDER BY SomeColumn

e il modo migliore che conosco per farlo in TSQL, prima di MS SQL 2012.


Se sono presenti molte righe, è possibile ottenere prestazioni migliori utilizzando una tabella temporanea anziché un CTE.


È stato votato per aver sottolineato la risposta di Martin Smith (e per collegarsi ad essa) fornendo una soluzione precedente al 2012. Anche per i consigli sulla tabella delle temp perché hai ragione :)
fujiiface,

7

Sfortunatamente, ROW_NUMBER()è il meglio che puoi fare. In realtà è più corretto, perché i risultati di una clausola limito topnon hanno davvero significato senza rispetto di un ordine specifico. Ma è ancora un dolore da fare.

Aggiornamento: SQL Server 2012 aggiunge una limitfunzione simile tramite le parole chiave OFFSET e FETCH . Questo è l'approccio ansi standard, al contrario LIMIT, che è un'estensione MySql non standard.


@Joel: puoi spiegare perché ROW_NUMBER () non è in grado di numerare le righe nel modo in cui escono da ORDER BY? Mi sono sempre chiesto perché "OVER (ORDER BY name)" sia obbligatorio, ma immagino che ci sia una buona ragione per questo. O almeno un motivo.
Tomalak,

3
perché non esiste un ordine senza una clausola order by. È possibile ottenere qualsiasi ordine i record fossero disponibili per il server e ciò potrebbe cambiare da richiesta di query a richiesta di query.
Joel Coehoorn,

1
@marcgg: non ho mai letto alcuna indicazione che Microsoft abbia intenzione di implementare LIMIT. Anche se hanno un piano del genere, i fornitori di sorgenti chiuse tendono a non annunciare le funzionalità. Sarebbe sicuramente una funzione utile, ma non sappiamo quanto lavoro sarebbe da implementare, dato il loro codice.
Bill Karwin,

3
Se non vuoi ripetere te stesso nella clausola ORDER BY, usa l'alias ROW_NUMBER () anziché l'insieme originale di colonne.
Peter Radocchia,

2
@Tomalak: per quanto riguarda SQL Server, l'ordinamento utilizzato per calcolare ROW_NUMBER () non è completamente correlato all'ordinamento del set di risultati. Ecco perché devi specificarli separatamente.
Luca,

6

Cosa ne pensi di questo?

SET ROWCOUNT 10 

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

Ti dà le ultime 10 righe delle prime 20 righe. Uno svantaggio è che l'ordine è invertito, ma almeno è facile da ricordare.


6
Cosa succede se ci sono solo 14 righe nella tabella? Ottieni le righe da 14 a 5, che non è la stessa delle righe restituite da LIMIT 10 OFFSET 10 (dovrebbero essere le righe da 14 a 11).
Bill Karwin,

2
SELECT TOP 10 *
FROM TABLE
WHERE IDCOLUMN NOT IN (SELECT TOP 10 IDCOLUMN FROM TABLE)

Dovrebbe dare record 11-20. Probabilmente non troppo efficiente se si incrementa per ottenere ulteriori pagine, e non sono sicuro di come potrebbe essere influenzato dall'ordinamento. Potrebbe essere necessario specificarlo in entrambe le istruzioni WHERE.


1

Un buon modo è quello di creare una procedura:

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

proprio come limite 0,2 /////////////// eseguire l'impaginazione 0,4


1

Solo per la soluzione record che funziona sulla maggior parte dei motori di database potrebbe non essere la più efficiente:

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

Nota Pelase: l'ultima pagina conterrebbe comunque le righe ReturnCount, indipendentemente da SkipCount. Ma potrebbe essere una buona cosa in molti casi.


1

L'equivalente di LIMIT è SET ROWCOUNT, ma se si desidera l'impaginazione generica è meglio scrivere una query come questa:

;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

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

Stampa le righe da 10 a 15.


0

Finora questo formato è ciò che funziona per me (non la migliore prestazione però):

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}

Una nota a lato, l'impaginazione su dati dinamici può portare a risultati strani / imprevisti.


0

Dalla documentazione online di MS SQL Server ( http://technet.microsoft.com/en-us/library/ms186734.aspx ), ecco il loro esempio che ho testato e funziona, per recuperare un set specifico di righe. ROW_NUMBER richiede un OVER, ma puoi ordinare come preferisci:

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;

0

Usa tutto il server SQL:; con tbl as (SELECT ROW_NUMBER () su (ordina per (seleziona 1)) come RowIndex, * dalla tabella) seleziona i primi 10 * da tbl dove RowIndex> = 10


-3
 SELECT * FROM users WHERE Id Between 15 and 25

verrà stampato da 15 a 25 come limite simile in MYSQl


2
Cosa succede se l'utente cancella un record tra 15 e 25?
Gökçer Gökdal
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.