Offset di riga in SQL Server


133

Esiste un modo in SQL Server per ottenere i risultati a partire da un determinato offset? Ad esempio, in un altro tipo di database SQL, è possibile fare:

SELECT * FROM MyTable OFFSET 50 LIMIT 25

per ottenere risultati 51-75. Questo costrutto non sembra esistere in SQL Server.

Come posso farlo senza caricare tutte le righe che non mi interessano? Grazie!


Puoi usare offset e recuperare la prossima istruzione. youtu.be/EqHkAiiBwPc
Amresh Kumar Singh

Risposte:


152

Eviterei di usare SELECT *. Specifica le colonne che desideri effettivamente, anche se potrebbero essere tutte.

SQL Server 2005+

SELECT col1, col2 
FROM (
    SELECT col1, col2, ROW_NUMBER() OVER (ORDER BY ID) AS RowNum
    FROM MyTable
) AS MyDerivedTable
WHERE MyDerivedTable.RowNum BETWEEN @startRow AND @endRow

SQL Server 2000

Paging efficiente tramite set di risultati di grandi dimensioni in SQL Server 2000

Un metodo più efficiente per il paging tramite set di risultati di grandi dimensioni


6
Perché suggerisci di evitare SELEZIONA anche se stai selezionando tutte le colonne?
Adam Ness,

12
Sono sicuro che ha usato "*" perché era più semplice da digitare e ha ottenuto il punto migliore di "col1, col2, ... colN"
gillonba

9
Per quanto riguarda il motivo per cui non usarlo, SELECT *significa che se la struttura della tabella cambia, la query viene comunque eseguita, ma fornisce risultati diversi. Se viene aggiunta una colonna, questo potrebbe essere utile (anche se devi comunque usarlo per nome da qualche parte); se una colonna viene eliminata o rinominata, è meglio che il tuo SQL si interrompa visibilmente rispetto al codice che si comporta in modo strano perché una variabile non è inizializzata.
IMSoP

5
selezionare tutti i dati della tabella e tagliare? se hanno 5000000000 righe? selezionare 5000000000 righe e tagliare per ogni query? non è efficace per CPU e memoria del server.
e-info128,

3
Si noti che il 2012+ ha implementato in modo molto migliore. Vedi risposta di + Martin Smith
meridio

100

Se elaborerai tutte le pagine in ordine, ricordati semplicemente di ricordare l'ultimo valore chiave visualizzato nella pagina precedente e l'utilizzo TOP (25) ... WHERE Key > @last_key ORDER BY Keypuò essere il metodo con le migliori prestazioni se esistono indici adeguati per consentire una ricerca efficiente - o un cursore API se non lo fanno .

Per selezionare una pagina arbitrale, la migliore soluzione per SQL Server 2005 - 2008 R2 è probabilmente ROW_NUMBEReBETWEEN

Per SQL Server 2012+ è possibile utilizzare la clausola ORDER BY avanzata per questa esigenza.

SELECT  *
FROM     MyTable 
ORDER BY OrderingColumn ASC 
OFFSET  50 ROWS 
FETCH NEXT 25 ROWS ONLY 

Anche se resta da vedere come pure l'esecuzione di questa opzione sarà .


2
È ora disponibile in SQL Server Compact 4.0 -> msdn.microsoft.com/en-us/library/gg699618(v=sql.110).aspx
Bart Verkoeijen

13
È ora che lo aggiungano a tSQL
JohnFx il

3
Solo per SQL Server 2012 :(
e-info128

22

Questo è un modo (SQL2000)

SELECT * FROM
(
    SELECT TOP (@pageSize) * FROM
    (
        SELECT TOP (@pageNumber * @pageSize) *
        FROM tableName 
        ORDER BY columnName ASC
    ) AS t1 
    ORDER BY columnName DESC
) AS t2 
ORDER BY columnName ASC

e questo è un altro modo (SQL 2005)

;WITH results AS (
    SELECT 
        rowNo = ROW_NUMBER() OVER( ORDER BY columnName ASC )
        , *
    FROM tableName 
) 
SELECT * 
FROM results
WHERE rowNo between (@pageNumber-1)*@pageSize+1 and @pageNumber*@pageSize

Giusto per chiarire il primo ... (@pageSize) è un segnaposto qui per il valore reale. Dovrai fare 'TOP 25' in particolare; SQL Server 2000 non supporta le variabili in una clausola TOP. Questo lo rende un problema che coinvolge SQL dinamico.
Cowan,

5
Tale soluzione per SQL2000 non funziona per l'ultima pagina nel set di risultati, a meno che il numero totale di righe non sia un multiplo della dimensione della pagina.
Bill Karwin,

10

Puoi usare la ROW_NUMBER()funzione per ottenere ciò che vuoi:

SELECT *
FROM (SELECT ROW_NUMBER() OVER(ORDER BY id) RowNr, id FROM tbl) t
WHERE RowNr BETWEEN 10 AND 20

7

C'è OFFSET .. FETCHin SQL Server 2012, ma sarà necessario specificare una ORDER BYcolonna.

Se davvero non hai alcuna colonna esplicita che potresti passare come ORDER BYcolonna (come altri hanno suggerito), puoi usare questo trucco:

SELECT * FROM MyTable 
ORDER BY @@VERSION 
OFFSET 50 ROWS FETCH NEXT 25 ROWS ONLY

... o

SELECT * FROM MyTable 
ORDER BY (SELECT 0)
OFFSET 50 ROWS FETCH NEXT 25 ROWS ONLY

Lo stiamo usando in jOOQ quando gli utenti non specificano esplicitamente un ordine. Ciò produrrà quindi un ordinamento piuttosto casuale senza costi aggiuntivi.


6

Per le tabelle con più e grandi colonne di dati, preferisco:

SELECT 
  tablename.col1,
  tablename.col2,
  tablename.col3,
  ...
FROM
(
  (
    SELECT
      col1
    FROM 
    (
      SELECT col1, ROW_NUMBER() OVER (ORDER BY col1 ASC) AS RowNum
      FROM tablename
      WHERE ([CONDITION])
    )
    AS T1 WHERE T1.RowNum BETWEEN [OFFSET] AND [OFFSET + LIMIT]
  )
  AS T2 INNER JOIN tablename ON T2.col1=tablename.col1
);

-

[CONDITION] can contain any WHERE clause for searching.
[OFFSET] specifies the start,
[LIMIT] the maximum results.

Ha prestazioni molto migliori su tabelle con dati di grandi dimensioni come BLOB, perché la funzione ROW_NUMBER deve solo guardare attraverso una colonna e solo le righe corrispondenti vengono restituite con tutte le colonne.


5

Vedi la mia selezione per impaginatore

SELECT TOP @limit * FROM (
   SELECT ROW_NUMBER() OVER (ORDER BY colunx ASC) offset, * FROM (

     -- YOU SELECT HERE
     SELECT * FROM mytable


   ) myquery
) paginator
WHERE offset > @offset

Questo risolve l'impaginazione;)


3
SELECT TOP 75 * FROM MyTable
EXCEPT 
SELECT TOP 50 * FROM MyTable

Le prestazioni non sembrano ottimali poiché la query viene quindi inutilmente eseguita due volte. Soprattutto quando l'utente accede alle pagine più in alto, la query per eliminare le righe, ovvero la parte sotto EXCEPT, richiederà sempre più tempo.
Vanval,

2

A seconda della versione non puoi farlo direttamente, ma potresti fare qualcosa di simile

select top 25 *
from ( 
  select top 75 *
  from   table 
  order by field asc
) a 
order by field desc 

dove "campo" è la chiave.


4
Tale soluzione per SQL2000 non funziona per l'ultima pagina nel set di risultati, a meno che il numero totale di righe non sia un multiplo della dimensione della pagina.
Bill Karwin,

2

Di seguito verranno visualizzati 25 record esclusi i primi 50 record di lavori in SQL Server 2012.

SELECT * FROM MyTable ORDER BY ID OFFSET 50 ROWS FETCH NEXT 25 ROWS ONLY;

è possibile sostituire l'ID come requisito


Per favore, aggiungi questo è possibile in SQL SERVER 2012
Usman Younas il

2

Dovresti stare attento quando usi l' ROW_NUMBER() OVER (ORDER BY)istruzione poiché le prestazioni sono piuttosto scarse. Lo stesso vale per l'uso delle espressioni comuni delle tabelle con ROW_NUMBER()questo è ancora peggio. Sto usando il seguente frammento che ha dimostrato di essere leggermente più veloce rispetto all'utilizzo di una variabile di tabella con un'identità per fornire il numero di pagina.

DECLARE @Offset INT = 120000
DECLARE @Limit INT = 10

DECLARE @ROWCOUNT INT = @Offset+@Limit
SET ROWCOUNT @ROWCOUNT

SELECT * FROM MyTable INTO #ResultSet
WHERE MyTable.Type = 1

SELECT * FROM
(
    SELECT *, ROW_NUMBER() OVER(ORDER BY SortConst ASC) As RowNumber FROM
    (
        SELECT *, 1 As SortConst FROM #ResultSet
    ) AS ResultSet
) AS Page
WHERE RowNumber BETWEEN @Offset AND @ROWCOUNT

DROP TABLE #ResultSet

Questo restituirà 11 righe, non 10.
Aaron Bertrand,

1

Uso questa tecnica per l'impaginazione. Non recupero tutte le righe. Ad esempio, se la mia pagina deve visualizzare le prime 100 righe, prendo solo le 100 con la clausola where. L'output di SQL dovrebbe avere una chiave univoca.

La tabella presenta quanto segue:

ID, KeyId, Rank

Lo stesso grado verrà assegnato per più di un KeyId.

SQL è select top 2 * from Table1 where Rank >= @Rank and ID > @Id

Per la prima volta passo 0 per entrambi. La seconda volta passa 1 e 14. 3a volta passa 2 e 6 ....

Il valore del decimo record Rank & Id viene passato al successivo

11  21  1
14  22  1
7   11  1
6   19  2
12  31  2
13  18  2

Ciò avrà il minimo stress sul sistema


1

In SqlServer2005 è possibile effettuare le seguenti operazioni:

DECLARE @Limit INT
DECLARE @Offset INT
SET @Offset = 120000
SET @Limit = 10

SELECT 
    * 
FROM
(
   SELECT 
       row_number() 
   OVER 
      (ORDER BY column) AS rownum, column2, column3, .... columnX
   FROM   
     table
) AS A
WHERE 
 A.rownum BETWEEN (@Offset) AND (@Offset + @Limit-1) 

Non dovrebbe essere @Offset + @Limit - 1? Se @Limit è 10, verranno restituite 11 righe.
Aaron Bertrand,

1

Il modo migliore per farlo senza perdere tempo per ordinare i record è in questo modo:

select 0 as tmp,Column1 from Table1 Order by tmp OFFSET 5000000 ROWS FETCH NEXT 50 ROWS ONLY

ci vuole meno di un secondo!
migliore soluzione per tavoli di grandi dimensioni.


0

Sto cercando questa risposta da un po 'di tempo (per query generiche) e ho scoperto un altro modo di farlo su SQL Server 2000+ utilizzando ROWCOUNT e cursori e senza TOP o alcuna tabella temporanea.

Usando SET ROWCOUNT [OFFSET+LIMIT]puoi limitare i risultati, e con i cursori, vai direttamente alla riga che desideri, quindi esegui il ciclo 'fino alla fine.

Quindi la tua query sarebbe così:

SET ROWCOUNT 75 -- (50 + 25)
DECLARE MyCursor SCROLL CURSOR FOR SELECT * FROM pessoas
OPEN MyCursor
FETCH ABSOLUTE 50 FROM MyCursor -- OFFSET
WHILE @@FETCH_STATUS = 0 BEGIN
    FETCH next FROM MyCursor
END
CLOSE MyCursor
DEALLOCATE MyCursor
SET ROWCOUNT 0

Odierei vedere lo spettacolo quando arriverai alla fine del tavolo ...
Aaron Bertrand,

0

Con SQL Server 2012 (11.x) e versioni successive e il database SQL di Azure, puoi anche avere "fetch_row_count_expression", puoi anche avere la clausola ORDER BY insieme a questa.

USE AdventureWorks2012;  
GO  
-- Specifying variables for OFFSET and FETCH values    
DECLARE @skip int = 0  , @take int = 8;  
SELECT DepartmentID, Name, GroupName  
FROM HumanResources.Department  
ORDER BY DepartmentID ASC   
    OFFSET @skip ROWS   
    FETCH NEXT @take ROWS ONLY; 

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

Nota OFFSET Specifica il numero di righe da saltare prima che inizi a restituire le righe dall'espressione della query. NON è il numero della riga iniziale. Quindi, deve essere 0 per includere il primo record.

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.