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.