Equivalente di LIMIT e OFFSET per SQL Server?


172

In PostgreSQL sono presenti le parole chiave Limite Offsetche consentono una paginazione molto semplice dei set di risultati.

Qual è la sintassi equivalente per SQL Server?


Per SQL Server 2012, questa funzionalità è implementata in modo semplice. Vedi la mia risposta
Somnath Muluk,

Grazie per aver posto questa domanda, siamo costretti a passare da MySQL a MsSQL :(
tempcke,

È possibile utilizzare offset e recuperare l'istruzione successiva in SQL Server con la clausola order by. Provalo youtu.be/EqHkAiiBwPc
Amresh Kumar Singh

Risposte:


139

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

Il vantaggio qui è la parametrizzazione dell'offset e del limite nel caso in cui si decida di modificare le opzioni di paginazione (o consentire all'utente di farlo).

Nota: il @Offsetparametro dovrebbe utilizzare l'indicizzazione basata su uno per questo anziché la normale indicizzazione basata su zero.


22
Vecchio adesso. SQL Server 2012 e versioni successive supportano OFFSET / FETCH
Joel Coehoorn,

31
@JoelCoehoorn Non vecchio. Mi sono appena assegnato al progetto usando SLQ Server 2008 dopo aver usato solo mysql in passato ...
Cthulhu,

Questo è abbastanza buono ma deve essere aggiustato un po 'WHERE RowNum >= (@Offset + 1)
Eric Herlitz,

5
The ORDER BY clause is invalid in views, inline functions, derived tables, subqueries, and common table expressions, unless TOP or FOR XML is also specified. MSSQL2008 R2.
Paolo,

2
@Aaronaught Se il mio Tableha 200k record, recupererà prima tutto, quindi applicherà il limite? Questa query è efficiente?
Jigar,

231

Questa funzionalità è ora semplificata in SQL Server 2012. Funziona da SQL Server 2012 in poi.

Limita con offset per selezionare da 11 a 20 righe in SQL Server:

SELECT email FROM emailTable 
WHERE user_id=3
ORDER BY Id
OFFSET 10 ROWS
FETCH NEXT 10 ROWS ONLY;
  • OFFSET: numero di righe saltate
  • NEXT: numero richiesto di righe successive

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


4
C'è un equivalente di SQL_CALC_FOUND_ROWSquando si utilizza questo?
Petah,

1
@Petah @@ Rowcount ti darà quello che penso
Rob Sedgwick il

GOTCHA: Non puoi usarlo da un CTE. Deve essere utilizzato nella query principale. Volevo limitare la quantità di righe restituite (impaginazione) e quindi eseguire un calcolo costoso per circa 10 righe restituite, piuttosto che determinare le righe, eseguire il calcolo costoso e quindi saltare / prendere ciò di cui avevo bisogno. La risposta di @ Aaronaught funzionerà per coloro che devono limitare le righe all'interno di un CTE.
Derreck Dean,

@Somnath Muluk Questo offset e recupero richiede molto tempo per un volume maggiore di esempio di dati con offset di 1000000. Come posso gestirlo.
Saroj Shrestha,

1
@SarojShrestha: questo non è un problema di offset e recupero. Ora dovresti rivisitare l'architettura del tuo tavolo. Prendi in considerazione il partizionamento delle tabelle, la riga dei tuoi dati, i diversi tipi di colonna e le dimensioni totali della tabella, considera l'archiviazione di alcune righe se non richiesto regolarmente, controlla le specifiche del tuo server.
Somnath Muluk,

23
select top {LIMIT HERE} * from (
      select *, ROW_NUMBER() over (order by {ORDER FIELD}) as r_n_n 
      from {YOUR TABLES} where {OTHER OPTIONAL FILTERS}
) xx where r_n_n >={OFFSET HERE}

Nota: questa soluzione funzionerà solo in SQL Server 2005 o versioni successive, poiché era in quel momento che era ROW_NUMBER()stata implementata.


Sto usando questa query da un po 'di tempo e funziona benissimo, quindi grazie per quello. Mi chiedo solo cosa rappresenti la "xx"?
Urbley

la query secondaria richiede un nome. come non lo sto usando basta mettere xx lì
jorgeu,

2
La xx è solo un alias di tabella. Potrebbe essere un po 'più chiaro se lei ha dettoAS xx
Calcestruzzo Gannet

qualcuno sa come fare il join sinistro su questa query?
Drenyl il

12

A tal fine, puoi utilizzare ROW_NUMBER in un'espressione di tabella comune.

;WITH My_CTE AS
(
     SELECT
          col1,
          col2,
          ROW_NUMBER() OVER(ORDER BY col1) AS row_number
     FROM
          My_Table
     WHERE
          <<<whatever>>>
)
SELECT
     col1,
     col2
FROM
     My_CTE
WHERE
     row_number BETWEEN @start_row AND @end_row

4

Per me l'uso di OFFSET e FETCH insieme è stato lento, quindi ho usato una combinazione di TOP e OFFSET come questa (che era più veloce):

SELECT TOP 20 * FROM (SELECT columname1, columname2 FROM tablename
    WHERE <conditions...> ORDER BY columname1 OFFSET 100 ROWS) aliasname

Nota: se si utilizzano TOP e OFFSET insieme nella stessa query come:

SELECT TOP 20 columname1, columname2 FROM tablename
    WHERE <conditions...> ORDER BY columname1 OFFSET 100 ROWS

Quindi viene visualizzato un errore, quindi per utilizzare TOP e OFFSET insieme è necessario separarlo con una query secondaria.

E se è necessario utilizzare SELECT DISTINCT, la query è simile a:

SELECT TOP 20 FROM (SELECT DISTINCT columname1, columname2
    WHERE <conditions...> ORDER BY columname1 OFFSET 100 ROWS) aliasname

Nota: l'uso di SELECT ROW_NUMBER con DISTINCT non ha funzionato per me.


1
Ottengo "Un TOP non può essere utilizzato nella stessa query o sottoquery di un OFFSET".
Michael Rushton,

Hai ragione @MichaelRushton, non puoi utilizzarlo nella stessa query o nella stessa query secondaria, quindi devi utilizzare una query secondaria per separarla. Quindi se hai l'SQL simile SELECT TOP 20 id FROM table1 where id > 10 order by date OFFSET 20 rows, devi trasformarlo come SELECT TOP 20 * FROM (SELECT id FROM table1 where id > 10 order by date OFFSET 20 ROWS) t1. Modificherò la mia risposta. Grazie e mi scusi il mio inglese.
sebasdev,

2

Un altro campione:

declare @limit int 
declare @offset int 
set @offset = 2;
set @limit = 20;
declare @count int
declare @idxini int 
declare @idxfim int 
select @idxfim = @offset * @limit
select @idxini = @idxfim - (@limit-1);
WITH paging AS
    (
        SELECT 
             ROW_NUMBER() OVER (order by object_id) AS rowid, *
        FROM 
            sys.objects 
    )
select *
    from 
        (select COUNT(1) as rowqtd from paging) qtd, 
            paging 
    where 
        rowid between @idxini and @idxfim
    order by 
        rowid;

15
Ho rimosso il tuo discorso di odio anti-microsoft. Non discutere qui di guerre sante; rispondi e fai domande in modo non soggettivo.
Earlz,

2

C'è qui qualcuno dire su questa funzionalità in SQL 2011, la sua triste scelgono un po 'parola chiave diversa "Offset / fetch", ma la sua non standart quindi OK.


2

Aggiungendo una leggera variazione alla soluzione di Aaronaught, in genere parametrizzo il numero di pagina (@PageNum) e le dimensioni della pagina (@PageSize). In questo modo ogni evento di clic sulla pagina invia semplicemente il numero di pagina richiesto insieme a una dimensione di pagina configurabile:

begin
    with My_CTE  as
    (
         SELECT col1,
              ROW_NUMBER() OVER(ORDER BY col1) AS row_number
     FROM
          My_Table
     WHERE
          <<<whatever>>>
    )
    select * from My_CTE
            WHERE RowNum BETWEEN (@PageNum - 1) * (@PageSize + 1) 
                              AND @PageNum * @PageSize

end

2

Il più vicino che potessi fare è

select * FROM( SELECT *, ROW_NUMBER() over (ORDER BY ID ) as ct from [db].[dbo].[table] ) sub where ct > fromNumber  and ct <= toNumber

Che immagino simile select * from [db].[dbo].[table] LIMIT 0, 10


2
-- @RowsPerPage  can be a fixed number and @PageNumber number can be passed 
DECLARE @RowsPerPage INT = 10, @PageNumber INT = 2

SELECT *

FROM MemberEmployeeData

ORDER BY EmployeeNumber

OFFSET @PageNumber*@RowsPerPage ROWS

FETCH NEXT 10 ROWS ONLY

Funziona con Microsoft SQL Server 13.x Grazie mille.
Shubham Arya

1
select top (@TakeCount) * --FETCH NEXT
from(
    Select  ROW_NUMBER() OVER (order by StartDate) AS rowid,*
    From YourTable
)A
where Rowid>@SkipCount --OFFSET

1
@nombre_row :nombre ligne par page  
@page:numero de la page

//--------------code sql---------------

declare  @page int,@nombre_row int;
    set @page='2';
    set @nombre_row=5;
    SELECT  *
FROM    ( SELECT    ROW_NUMBER() OVER ( ORDER BY etudiant_ID ) AS RowNum, *
      FROM      etudiant

    ) AS RowConstrainedResult
WHERE   RowNum >= ((@page-1)*@nombre_row)+1
    AND RowNum < ((@page)*@nombre_row)+1
ORDER BY RowNum

1

Poiché nessuno ha ancora fornito questo codice:

SELECT TOP @limit f1, f2, f3...
FROM t1
WHERE c1 = v1, c2 > v2...
AND
    t1.id NOT IN
        (SELECT TOP @offset id
         FROM t1
         WHERE c1 = v1, c2 > v2...
         ORDER BY o1, o2...)
ORDER BY o1, o2...

Punti importanti:

  • ORDER BY deve essere identico
  • @limit può essere sostituito con il numero di risultati da recuperare,
  • @offset è il numero di risultati da saltare
  • Si prega di confrontare le prestazioni con le soluzioni precedenti in quanto potrebbero essere più efficienti
  • questa soluzione duplica whereeorder by clausole e fornirà risultati errati se non sono sincronizzati
  • d'altra parte order byc'è esplicitamente se questo è ciò che è necessario

1

In particolare per SQL-SERVER puoi ottenerlo in molti modi diversi. Per un vero esempio abbiamo preso la tabella dei clienti qui.

Esempio 1: con "SET ROWCOUNT"

SET ROWCOUNT 10
SELECT CustomerID, CompanyName from Customers
ORDER BY CompanyName

Per restituire tutte le righe, impostare ROWCOUNT su 0

SET ROWCOUNT 0  
SELECT CustomerID, CompanyName from Customers
    ORDER BY CompanyName

Esempio 2: con "ROW_NUMBER e OVER"

With Cust AS
( SELECT CustomerID, CompanyName,
ROW_NUMBER() OVER (order by CompanyName) as RowNumber 
FROM Customers )
select *
from Cust
Where RowNumber Between 0 and 10

Esempio 3: con "OFFSET e FETCH", ma con questo "ORDER BY" è obbligatorio

SELECT CustomerID, CompanyName FROM Customers
ORDER BY CompanyName
OFFSET 0 ROWS
FETCH NEXT 10 ROWS ONLY

Spero che questo ti aiuti.



-1

Da allora, ho testato più volte questo script più utile di 1 milione di record ogni pagina 100 record con l'impaginazione funzionano più velocemente il mio PC esegue questo script 0 sec mentre il confronto con mysql ha il proprio limite e lo scostamento di circa 4,5 sec per ottenere il risultato.

Qualcuno potrebbe perdere la comprensione di Row_Number () ordinare sempre per campo specifico. Nel caso in cui abbiamo bisogno di definire solo la riga in sequenza dovrebbe usare:

ROW_NUMBER () OVER (ORDER BY (SELECT NULL))

SELECT TOP {LIMIT} * FROM (
      SELECT TOP {LIMIT} + {OFFSET} ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) AS ROW_NO,*
      FROM  {TABLE_NAME}
) XX WHERE ROW_NO > {OFFSET}

Spiegare:

  • {LIMIT}: numero di record per ogni pagina
  • {OFFSET}: numero di record da saltare

2
Sebbene questo codice possa risolvere la domanda, inclusa una spiegazione di come e perché questo risolva il problema, contribuirebbe davvero a migliorare la qualità del tuo post e probabilmente a dare più voti positivi. Ricorda che stai rispondendo alla domanda per i lettori in futuro, non solo per la persona che chiede ora. Si prega di modificare la risposta per aggiungere spiegazioni e dare un'indicazione di ciò si applicano le limitazioni e le assunzioni.
Brian,
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.