Qual è il modo migliore per impaginare i risultati in SQL Server


475

Qual è il modo migliore (per quanto riguarda le prestazioni) di impaginare i risultati in SQL Server 2000, 2005, 2008, 2012 se si desidera ottenere anche il numero totale di risultati (prima dell'impaginazione)?


26
Mi sono sempre chiesto perché non supportassero semplicemente la specifica di un offset come parte di TOP (come il supporto MySQL / Posgresql con LIMIT / OFFSET). Ad esempio, potrebbero avere solo la sintassi "SELECT TOP x, y ...." dove x = numero di righe, y = offset iniziale. Sarebbe anche compatibile con le versioni precedenti.
Gregregac,

3
ehi, anche io ... l'implementazione della paginazione del 2005 di sql è davvero così
imbarazzante

6
@gregmac - Sql Server 2012 non ha limite / offset ora.
OO

2
La soluzione accettata non mostra come sia il modo migliore (per quanto riguarda le prestazioni). Qualche dato di backup su set di dati di grandi dimensioni?
OO,

3
@OO: Un buon benchmark può essere trovato qui: 4guysfromrolla.com/webtech/042606-1.shtml . Tuttavia, il metodo seek supererà qualsiasi impaginazione basata su offset.
Lukas Eder,

Risposte:


465

Ottenere il numero totale di risultati e impaginare sono due diverse operazioni. Per il bene di questo esempio, supponiamo che la query con cui hai a che fare sia

SELECT * FROM Orders WHERE OrderDate >= '1980-01-01' ORDER BY OrderDate

In questo caso, determineresti il ​​numero totale di risultati usando:

SELECT COUNT(*) FROM Orders WHERE OrderDate >= '1980-01-01'

... che può sembrare inefficiente, ma in realtà è piuttosto performante, supponendo che tutti gli indici ecc. siano impostati correttamente.

Successivamente, per ottenere risultati effettivi in ​​modo impaginato, la seguente query sarebbe più efficiente:

SELECT  *
FROM    ( SELECT    ROW_NUMBER() OVER ( ORDER BY OrderDate ) AS RowNum, *
          FROM      Orders
          WHERE     OrderDate >= '1980-01-01'
        ) AS RowConstrainedResult
WHERE   RowNum >= 1
    AND RowNum < 20
ORDER BY RowNum

Ciò restituirà le righe 1-19 della query originale. La cosa interessante qui, specialmente per le app Web, è che non è necessario mantenere alcuno stato, tranne i numeri di riga da restituire.


37
Solo per notare che ROW_NUMBER () non esiste in SQL Server 2000
John Hunter il

6
restituisce tutte le righe dalla query interna e quindi filtra in base alla query esterna? ad esempio: la query interna restituisce 100.000 e la query esterna restituisce solo 20.
SoftwareGeek

2
@SoftwareGeek: pensalo come la subquery (query interna) che restituisce un flusso, che viene quindi letto fino a quando la clausola WHERE esterna è soddisfatta. Come possono essere coinvolte le righe con ciò, dipende interamente dalla query, ma l'ottimizzatore generalmente fa un ottimo lavoro nel minimizzare quel numero. L'utilizzo del visualizzatore del piano di esecuzione grafico in SQL Server Management Studio (utilizzare Query / Includi piano di esecuzione effettivo) è molto istruttivo al riguardo.
mdb,

2
ok, cosa succede se vieni pubblicato in inner select (come quando hai un join interno) come usi distinto perché RowNumber è diverso e non funziona
user217648

10
Microsoft ha aggiunto una nuova funzionalità a SQL 2012 che rende l'impaginazione simile a MySQL. Segui questo link per sapere come. È un articolo interessante: dbadiaries.com/…
Arash,

513

Infine, è stato rilasciato Microsoft SQL Server 2012 , mi piace molto la sua semplicità per una paginazione, non è necessario utilizzare query complesse come risposta qui.

Per ottenere le prossime 10 righe basta eseguire questa query:

SELECT * FROM TableName ORDER BY id OFFSET 10 ROWS FETCH NEXT 10 ROWS ONLY;

https://docs.microsoft.com/en-us/sql/t-sql/queries/select-order-by-clause-transact-sql#using-offset-and-fetch-to-limit-the-rows- tornato

Punti chiave da considerare quando lo si utilizza:

  • ORDER BYè obbligatorio utilizzare la OFFSET ... FETCHclausola.
  • OFFSETla clausola è obbligatoria con FETCH. Non puoi usareORDER BY ... FETCH .
  • TOPnon può essere combinato con OFFSETe FETCHnella stessa espressione di query.

12
Sto ancora aspettando il LISTAGG()/ GROUP_CONCAT().
Pezzi di pancetta dal

1
@BaconBits Vedere questa risposta per un modo subdolo di farlo con FOR XML: stackoverflow.com/a/273330/429949
Richard Marskell - Drackir

1
@ RichardMarskell-Drackir Ci sono molti problemi con FOR XML PATH (''). Innanzitutto, sostituisce i caratteri di controllo XML con codici entità XML. Spero non si hanno <, >o &nei vostri dati! In secondo luogo, FOR XML PATH ('')utilizzato in questo modo è in realtà una sintassi non documentata. Dovresti specificare una colonna con nome o un nome di elemento alternativo. Non fare nessuno dei due non è nel documento, il che significa che il comportamento non è affidabile. Terzo, più accettiamo la FOR XML PATH ('')sintassi non funzionante, meno è probabile che la SM fornisca effettivamente una funzione reale LISTAGG() [ OVER() ] come era necessario.
Bacon Bits

4
peccato che il perf sia così male mssqlgirl.com/…
Jon

5
@Jon, quel post di blog collegato non è rappresentativo, nel senso che fa confronti in base alla restituzione del risultato della pagina cercando i valori della colonna id.
Noel Abrahams,

103

Incredibilmente, nessun'altra risposta ha menzionato il modo più veloce per eseguire l'impaginazione in tutte le versioni di SQL Server. Gli offset possono essere terribilmente lenti per i numeri di pagina di grandi dimensioni, come viene benchmarkato qui . Esiste un modo completamente diverso e molto più veloce per eseguire l'impaginazione in SQL. Questo è spesso chiamato "metodo di ricerca" o "impaginazione keyset" come descritto in questo post di blog qui .

SELECT TOP 10 first_name, last_name, score, COUNT(*) OVER()
FROM players
WHERE (score < @previousScore)
   OR (score = @previousScore AND player_id < @previousPlayerId)
ORDER BY score DESC, player_id DESC

Il "cercare predicato"

I valori @previousScoree @previousPlayerIdsono i rispettivi valori dell'ultimo record della pagina precedente. Ciò ti consente di recuperare la pagina "successiva". Se la ORDER BYdirezione è ASC, basta usare >invece.

Con il metodo sopra, non è possibile passare immediatamente alla pagina 4 senza aver prima recuperato i precedenti 40 record. Ma spesso, non vuoi saltare così lontano comunque. Invece, ottieni una query molto più veloce che potrebbe essere in grado di recuperare i dati in tempo costante, a seconda dell'indicizzazione. Inoltre, le tue pagine rimangono "stabili", indipendentemente dal fatto che i dati sottostanti cambino (ad esempio a pagina 1, mentre sei a pagina 4).

Questo è il modo migliore per implementare l'impaginazione durante il caricamento pigro di più dati in applicazioni Web, ad esempio.

Nota, il "metodo seek" è anche chiamato impaginazione keyset .

Record totali prima dell'impaginazione

La COUNT(*) OVER()funzione finestra ti aiuterà a contare il numero di record totali "prima dell'impaginazione". Se si utilizza SQL Server 2000, sarà necessario ricorrere a due query per COUNT(*).


2
@ user960567: in termini di prestazioni, il paging del keyset batterà sempre il paging dell'offset, indipendentemente dal fatto che si implementa il paging dell'offset con lo standard SQL OFFSET .. FETCHo con ROW_NUMBER()trucchi precedenti .
Lukas Eder,

21
Ho tre problemi con il metodo seek. [1] Un utente non può saltare alla pagina. [2] presuppone chiavi sequenziali, cioè se qualcuno elimina circa 3 righe, allora ottengo una pagina di 7 elementi invece di 10. RowNumbermi dà 10 elementi coerenti per pagina. [3] non funziona con le griglie esistenti che presuppongonopagenumber e pagesize.
Rebecca,

7
@Junto: il paging del keyset non è appropriato per tutti i casi. Non è sicuramente per le reti dati. Ma è perfetto per scenari come lo scorrimento infinito della pagina dei feed di Facebook. Non importa se vengono aggiunti nuovi post nella parte superiore, i post dei feed successivi verranno aggiunti correttamente nella parte inferiore mentre si scorre verso il basso. Esempio di utilizzo perfetto per questo ... Una cosa del genere sarebbe molto più difficile da implementare usando il limite di offset / recupero usando solo numeri.
Robert Koritnik,

4
Sono d'accordo con Junto. Questo metodo esclude completamente un client che ha un'interfaccia utente di paginazione piuttosto standard di "Precedente 1 2 3 (4) 5 6 Successivo" in cui gli utenti possono saltare avanti. Questo non è esattamente un caso limite nella mia esperienza ...
AaronHS,

3
Articolo di impaginazione di chiavi qui
Stphane

31

Da SQL Server 2012, possiamo utilizzare OFFSETe la FETCH NEXTclausola per ottenere l'impaginazione.

Prova questo, per SQL Server:

In SQL Server 2012 è stata aggiunta una nuova funzionalità nella clausola ORDER BY, per interrogare l'ottimizzazione di un set di dati, semplificando il lavoro con il paging dei dati per chiunque scriva in T-SQL e per l'intero piano di esecuzione in SQL Server.

Sotto lo script T-SQL con la stessa logica utilizzata nell'esempio precedente.

--CREATING A PAGING WITH OFFSET and FETCH clauses IN "SQL SERVER 2012"
DECLARE @PageNumber AS INT, @RowspPage AS INT
SET @PageNumber = 2
SET @RowspPage = 10 
SELECT ID_EXAMPLE, NM_EXAMPLE, DT_CREATE
FROM TB_EXAMPLE
ORDER BY ID_EXAMPLE
OFFSET ((@PageNumber - 1) * @RowspPage) ROWS
FETCH NEXT @RowspPage ROWS ONLY;

TechNet: paginazione di una query con SQL Server


risposta più accurata in questo processo
Vikrant,

17

MSDN: ROW_NUMBER (Transact-SQL)

Restituisce il numero sequenziale di una riga all'interno di una partizione di un set di risultati, a partire da 1 per la prima riga in ciascuna partizione.

L'esempio seguente restituisce le righe con i numeri da 50 a 60 inclusi nell'ordine del OrderDate.

WITH OrderedOrders AS
(
    SELECT
        ROW_NUMBER() OVER(ORDER BY FirstName DESC) AS RowNumber, 
        FirstName, LastName, ROUND(SalesYTD,2,1) AS "Sales YTD"
    FROM [dbo].[vSalesPerson]
) 
SELECT RowNumber, 
    FirstName, LastName, Sales YTD 
FROM OrderedOrders 
WHERE RowNumber > 50 AND RowNumber < 60;
  RowNumber FirstName    LastName               SalesYTD
  --- -----------  ---------------------- -----------------
  1   Linda        Mitchell               4251368.54
  2   Jae          Pak                    4116871.22
  3   Michael      Blythe                 3763178.17
  4   Jillian      Carson                 3189418.36
  5   Ranjit       Varkey Chudukatil      3121616.32
  6   José         Saraiva                2604540.71
  7   Shu          Ito                    2458535.61
  8   Tsvi         Reiter                 2315185.61
  9   Rachel       Valdez                 1827066.71
  10  Tete         Mensa-Annan            1576562.19
  11  David        Campbell               1573012.93
  12  Garrett      Vargas                 1453719.46
  13  Lynn         Tsoflias               1421810.92
  14  Pamela       Ansman-Wolfe           1352577.13

15

È disponibile una buona panoramica delle diverse tecniche di paging su http://www.codeproject.com/KB/aspnet/PagingLarge.aspx

Ho usato il metodo ROWCOUNT abbastanza spesso principalmente con SQL Server 2000 (funzionerà anche con 2005 e 2008, basta misurare le prestazioni rispetto a ROW_NUMBER), è velocissimo, ma è necessario assicurarsi che le colonne ordinate abbiano (principalmente ) valori unici.


1
È interessante notare che l'articolo non menziona il metodo seek , che è in grado di eseguire il paging in tempo costante ... Ancora un buon articolo
Lukas Eder,

6

Per SQL Server 2000 è possibile simulare ROW_NUMBER () utilizzando una variabile di tabella con una colonna IDENTITY:

DECLARE @pageNo int -- 1 based
DECLARE @pageSize int
SET @pageNo = 51
SET @pageSize = 20

DECLARE @firstRecord int
DECLARE @lastRecord int
SET @firstRecord = (@pageNo - 1) * @pageSize + 1 -- 1001
SET @lastRecord = @firstRecord + @pageSize - 1   -- 1020

DECLARE @orderedKeys TABLE (
  rownum int IDENTITY NOT NULL PRIMARY KEY CLUSTERED,
  TableKey int NOT NULL
)

SET ROWCOUNT @lastRecord
INSERT INTO @orderedKeys (TableKey) SELECT ID FROM Orders WHERE OrderDate >= '1980-01-01' ORDER BY OrderDate

SET ROWCOUNT 0

SELECT t.*
FROM Orders t
  INNER JOIN @orderedKeys o ON o.TableKey = t.ID
WHERE o.rownum >= @firstRecord
ORDER BY o.rownum

Questo approccio può essere esteso alle tabelle con chiavi multi-colonna e non comporta il sovraccarico prestazionale dell'utilizzo di OR (che salta l'utilizzo dell'indice). Il rovescio della medaglia è la quantità di spazio temporaneo esaurito se il set di dati è molto grande e uno è vicino all'ultima pagina. In quel caso non ho testato le prestazioni del cursore, ma potrebbe essere migliore.

Si noti che questo approccio potrebbe essere ottimizzato per la prima pagina di dati. Inoltre, è stato utilizzato ROWCOUNT poiché TOP non accetta una variabile in SQL Server 2000.


3

Il modo migliore per il paging in sql server 2012 è utilizzare offset e recupero successivo in una procedura memorizzata. Parola chiave OFFSET : se utilizziamo l'offset con la clausola order by, la query salterà il numero di record specificati in OFFSET n Rows.

Parole chiave FETCH NEXT - Quando utilizziamo Fetch Next solo con una clausola order by, verrà restituito il numero di righe che si desidera visualizzare nel paging, senza Offset, quindi SQL genererà un errore. ecco l'esempio riportato di seguito.

create procedure sp_paging
(
 @pageno as int,
 @records as int
)
as
begin
declare @offsetcount as int
set @offsetcount=(@pageno-1)*@records
select id,bs,variable from salary order by id offset @offsetcount rows fetch Next @records rows only
end

puoi eseguirlo come segue.

exec sp_paging 2,3

2

Queste sono le mie soluzioni per il paging del risultato della query sul lato server SQL. questi approcci sono diversi tra SQL Server 2008 e 2012. Inoltre, ho aggiunto il concetto di filtraggio e ordinamento con una colonna. È molto efficiente quando si esegue il paging, il filtraggio e l'ordine in Gridview.

Prima del test, devi creare una tabella di esempio e inserire una riga in questa tabella: (Nel mondo reale devi cambiare la clausola Where considerando i campi della tabella e forse hai un po 'di join e subquery nella parte principale di select)

Create Table VLT
(
    ID int IDentity(1,1),
    Name nvarchar(50),
    Tel Varchar(20)
)
GO


Insert INTO VLT
VALUES
    ('NAME' + Convert(varchar(10),@@identity),'FAMIL' + Convert(varchar(10),@@identity))
GO 500000

In tutti questi esempi, voglio interrogare 200 righe per pagina e sto recuperando la riga per il numero di pagina 1200.

In SQL Server 2008, è possibile utilizzare il concetto CTE. Per questo motivo, ho scritto due tipi di query per SQL Server 2008+

- SQL Server 2008+

DECLARE @PageNumber Int = 1200
DECLARE @PageSize INT = 200
DECLARE @SortByField int = 1 --The field used for sort by
DECLARE @SortOrder nvarchar(255) = 'ASC' --ASC or DESC
DECLARE @FilterType nvarchar(255) = 'None' --The filter type, as defined on the client side (None/Contain/NotContain/Match/NotMatch/True/False/)
DECLARE @FilterValue nvarchar(255) = '' --The value the user gave for the filter
DECLARE @FilterColumn int = 1 --The column to wich the filter is applied, represents the column number like when we send the information.

SELECT 
  Data.ID,
  Data.Name,
  Data.Tel
FROM
  (  
    SELECT 
      ROW_NUMBER() 
        OVER( ORDER BY 
                CASE WHEN @SortByField = 1 AND @SortOrder = 'ASC'
                      THEN VLT.ID END ASC,
                CASE WHEN @SortByField = 1 AND @SortOrder = 'DESC'
                      THEN VLT.ID END DESC,
                CASE WHEN @SortByField = 2 AND @SortOrder = 'ASC'
                      THEN VLT.Name END ASC,
                CASE WHEN @SortByField = 2 AND @SortOrder = 'DESC'
                      THEN VLT.Name END ASC,
                CASE WHEN @SortByField = 3 AND @SortOrder = 'ASC'
                      THEN VLT.Tel END ASC,
                CASE WHEN @SortByField = 3 AND @SortOrder = 'DESC'
                      THEN VLT.Tel END ASC
         ) AS RowNum
      ,*  
    FROM VLT 
    WHERE
      ( -- We apply the filter logic here
        CASE
          WHEN @FilterType = 'None' THEN 1

          -- Name column filter
          WHEN @FilterType = 'Contain' AND @FilterColumn = 1
            AND ( -- In this case, when the filter value is empty, we want to show everything.
                VLT.ID LIKE '%' + @FilterValue + '%'
               OR
                @FilterValue = ''
               ) THEN 1
          WHEN @FilterType = 'NotContain' AND @FilterColumn = 1
            AND ( -- In this case, when the filter value is empty, we want to show everything.
                VLT.ID NOT LIKE '%' + @FilterValue + '%'
               OR
                @FilterValue = ''
               ) THEN 1
          WHEN @FilterType = 'Match' AND @FilterColumn = 1
            AND VLT.ID = @FilterValue THEN 1
          WHEN @FilterType = 'NotMatch' AND @FilterColumn = 1
            AND VLT.ID <> @FilterValue THEN 1               

          -- Name column filter
          WHEN @FilterType = 'Contain' AND @FilterColumn = 2
            AND ( -- In this case, when the filter value is empty, we want to show everything.
                VLT.Name LIKE '%' + @FilterValue + '%'
               OR
                @FilterValue = ''
               ) THEN 1
          WHEN @FilterType = 'NotContain' AND @FilterColumn = 2
            AND ( -- In this case, when the filter value is empty, we want to show everything.
                VLT.Name NOT LIKE '%' + @FilterValue + '%'
               OR
                @FilterValue = ''
               ) THEN 1
          WHEN @FilterType = 'Match' AND @FilterColumn = 2
            AND VLT.Name = @FilterValue THEN 1
          WHEN @FilterType = 'NotMatch' AND @FilterColumn = 2
            AND VLT.Name <> @FilterValue THEN 1         

         -- Tel column filter   
         WHEN @FilterType = 'Contain' AND @FilterColumn = 3
            AND ( -- In this case, when the filter value is empty, we want to show everything.
                VLT.Tel LIKE '%' + @FilterValue + '%'
               OR
                @FilterValue = ''
               ) THEN 1
          WHEN @FilterType = 'NotContain' AND @FilterColumn = 3
            AND ( -- In this case, when the filter value is empty, we want to show everything.
                VLT.Tel NOT LIKE '%' + @FilterValue + '%'
               OR
                @FilterValue = ''
               ) THEN 1
          WHEN @FilterType = 'Match' AND @FilterColumn = 3
            AND VLT.Tel = @FilterValue THEN 1
          WHEN @FilterType = 'NotMatch' AND @FilterColumn = 3
            AND VLT.Tel <> @FilterValue THEN 1    

        END
      ) = 1   
  ) AS Data
WHERE Data.RowNum > @PageSize * (@PageNumber - 1)
  AND Data.RowNum <= @PageSize * @PageNumber
ORDER BY Data.RowNum

GO

E seconda soluzione con CTE in SQL Server 2008+

DECLARE @PageNumber Int = 1200
DECLARE @PageSize INT = 200
DECLARE @SortByField int = 1 --The field used for sort by
DECLARE @SortOrder nvarchar(255) = 'ASC' --ASC or DESC
DECLARE @FilterType nvarchar(255) = 'None' --The filter type, as defined on the client side (None/Contain/NotContain/Match/NotMatch/True/False/)
DECLARE @FilterValue nvarchar(255) = '' --The value the user gave for the filter
DECLARE @FilterColumn int = 1 --The column to wich the filter is applied, represents the column number like when we send the information.

;WITH
  Data_CTE
  AS
  (  
    SELECT 
      ROW_NUMBER() 
        OVER( ORDER BY 
                CASE WHEN @SortByField = 1 AND @SortOrder = 'ASC'
                      THEN VLT.ID END ASC,
                CASE WHEN @SortByField = 1 AND @SortOrder = 'DESC'
                      THEN VLT.ID END DESC,
                CASE WHEN @SortByField = 2 AND @SortOrder = 'ASC'
                      THEN VLT.Name END ASC,
                CASE WHEN @SortByField = 2 AND @SortOrder = 'DESC'
                      THEN VLT.Name END ASC,
                CASE WHEN @SortByField = 3 AND @SortOrder = 'ASC'
                      THEN VLT.Tel END ASC,
                CASE WHEN @SortByField = 3 AND @SortOrder = 'DESC'
                      THEN VLT.Tel END ASC
         ) AS RowNum
      ,*  
    FROM VLT
    WHERE
      ( -- We apply the filter logic here
        CASE
          WHEN @FilterType = 'None' THEN 1

          -- Name column filter
          WHEN @FilterType = 'Contain' AND @FilterColumn = 1
            AND ( -- In this case, when the filter value is empty, we want to show everything.
                VLT.ID LIKE '%' + @FilterValue + '%'
               OR
                @FilterValue = ''
               ) THEN 1
          WHEN @FilterType = 'NotContain' AND @FilterColumn = 1
            AND ( -- In this case, when the filter value is empty, we want to show everything.
                VLT.ID NOT LIKE '%' + @FilterValue + '%'
               OR
                @FilterValue = ''
               ) THEN 1
          WHEN @FilterType = 'Match' AND @FilterColumn = 1
            AND VLT.ID = @FilterValue THEN 1
          WHEN @FilterType = 'NotMatch' AND @FilterColumn = 1
            AND VLT.ID <> @FilterValue THEN 1               

          -- Name column filter
          WHEN @FilterType = 'Contain' AND @FilterColumn = 2
            AND ( -- In this case, when the filter value is empty, we want to show everything.
                VLT.Name LIKE '%' + @FilterValue + '%'
               OR
                @FilterValue = ''
               ) THEN 1
          WHEN @FilterType = 'NotContain' AND @FilterColumn = 2
            AND ( -- In this case, when the filter value is empty, we want to show everything.
                VLT.Name NOT LIKE '%' + @FilterValue + '%'
               OR
                @FilterValue = ''
               ) THEN 1
          WHEN @FilterType = 'Match' AND @FilterColumn = 2
            AND VLT.Name = @FilterValue THEN 1
          WHEN @FilterType = 'NotMatch' AND @FilterColumn = 2
            AND VLT.Name <> @FilterValue THEN 1         

         -- Tel column filter   
         WHEN @FilterType = 'Contain' AND @FilterColumn = 3
            AND ( -- In this case, when the filter value is empty, we want to show everything.
                VLT.Tel LIKE '%' + @FilterValue + '%'
               OR
                @FilterValue = ''
               ) THEN 1
          WHEN @FilterType = 'NotContain' AND @FilterColumn = 3
            AND ( -- In this case, when the filter value is empty, we want to show everything.
                VLT.Tel NOT LIKE '%' + @FilterValue + '%'
               OR
                @FilterValue = ''
               ) THEN 1
          WHEN @FilterType = 'Match' AND @FilterColumn = 3
            AND VLT.Tel = @FilterValue THEN 1
          WHEN @FilterType = 'NotMatch' AND @FilterColumn = 3
            AND VLT.Tel <> @FilterValue THEN 1    

        END
      ) = 1     
  )

SELECT 
  Data.ID,
  Data.Name,
  Data.Tel
FROM Data_CTE AS Data
WHERE Data.RowNum > @PageSize * (@PageNumber - 1)
  AND Data.RowNum <= @PageSize * @PageNumber
ORDER BY Data.RowNum

- SQL Server 2012+

DECLARE @PageNumber Int = 1200
DECLARE @PageSize INT = 200
DECLARE @SortByField int = 1 --The field used for sort by
DECLARE @SortOrder nvarchar(255) = 'ASC' --ASC or DESC
DECLARE @FilterType nvarchar(255) = 'None' --The filter type, as defined on the client side (None/Contain/NotContain/Match/NotMatch/True/False/)
DECLARE @FilterValue nvarchar(255) = '' --The value the user gave for the filter
DECLARE @FilterColumn int = 1 --The column to wich the filter is applied, represents the column number like when we send the information.

;WITH
  Data_CTE
  AS
  (  
    SELECT 
      *  
    FROM VLT
    WHERE
      ( -- We apply the filter logic here
        CASE
          WHEN @FilterType = 'None' THEN 1

          -- Name column filter
          WHEN @FilterType = 'Contain' AND @FilterColumn = 1
            AND ( -- In this case, when the filter value is empty, we want to show everything.
                VLT.ID LIKE '%' + @FilterValue + '%'
               OR
                @FilterValue = ''
               ) THEN 1
          WHEN @FilterType = 'NotContain' AND @FilterColumn = 1
            AND ( -- In this case, when the filter value is empty, we want to show everything.
                VLT.ID NOT LIKE '%' + @FilterValue + '%'
               OR
                @FilterValue = ''
               ) THEN 1
          WHEN @FilterType = 'Match' AND @FilterColumn = 1
            AND VLT.ID = @FilterValue THEN 1
          WHEN @FilterType = 'NotMatch' AND @FilterColumn = 1
            AND VLT.ID <> @FilterValue THEN 1               

          -- Name column filter
          WHEN @FilterType = 'Contain' AND @FilterColumn = 2
            AND ( -- In this case, when the filter value is empty, we want to show everything.
                VLT.Name LIKE '%' + @FilterValue + '%'
               OR
                @FilterValue = ''
               ) THEN 1
          WHEN @FilterType = 'NotContain' AND @FilterColumn = 2
            AND ( -- In this case, when the filter value is empty, we want to show everything.
                VLT.Name NOT LIKE '%' + @FilterValue + '%'
               OR
                @FilterValue = ''
               ) THEN 1
          WHEN @FilterType = 'Match' AND @FilterColumn = 2
            AND VLT.Name = @FilterValue THEN 1
          WHEN @FilterType = 'NotMatch' AND @FilterColumn = 2
            AND VLT.Name <> @FilterValue THEN 1         

         -- Tel column filter   
         WHEN @FilterType = 'Contain' AND @FilterColumn = 3
            AND ( -- In this case, when the filter value is empty, we want to show everything.
                VLT.Tel LIKE '%' + @FilterValue + '%'
               OR
                @FilterValue = ''
               ) THEN 1
          WHEN @FilterType = 'NotContain' AND @FilterColumn = 3
            AND ( -- In this case, when the filter value is empty, we want to show everything.
                VLT.Tel NOT LIKE '%' + @FilterValue + '%'
               OR
                @FilterValue = ''
               ) THEN 1
          WHEN @FilterType = 'Match' AND @FilterColumn = 3
            AND VLT.Tel = @FilterValue THEN 1
          WHEN @FilterType = 'NotMatch' AND @FilterColumn = 3
            AND VLT.Tel <> @FilterValue THEN 1    

        END
      ) = 1         
  )

SELECT 
  Data.ID,
  Data.Name,
  Data.Tel
FROM Data_CTE AS Data
ORDER BY 
    CASE WHEN @SortByField = 1 AND @SortOrder = 'ASC'
        THEN Data.ID END ASC,
    CASE WHEN @SortByField = 1 AND @SortOrder = 'DESC'
        THEN Data.ID END DESC,
    CASE WHEN @SortByField = 2 AND @SortOrder = 'ASC'
        THEN Data.Name END ASC,
    CASE WHEN @SortByField = 2 AND @SortOrder = 'DESC'
        THEN Data.Name END ASC,
    CASE WHEN @SortByField = 3 AND @SortOrder = 'ASC'
        THEN Data.Tel END ASC,
    CASE WHEN @SortByField = 3 AND @SortOrder = 'DESC'
        THEN Data.Tel END ASC
OFFSET @PageSize * (@PageNumber - 1) ROWS FETCH NEXT @PageSize ROWS ONLY;

1

Prova questo approccio:

SELECT TOP @offset a.*
FROM (select top @limit b.*, COUNT(*) OVER() totalrows 
        from TABLENAME b order by id asc) a
ORDER BY id desc;

1

Per quanto riguarda il caso, quanto segue sembra essere facile da usare e veloce. Basta impostare il numero di pagina.

use AdventureWorks
DECLARE @RowsPerPage INT = 10, @PageNumber INT = 6;
with result as(
SELECT SalesOrderDetailID, SalesOrderID, ProductID,
ROW_NUMBER() OVER (ORDER BY SalesOrderDetailID) AS RowNum
FROM Sales.SalesOrderDetail
where 1=1
)
select SalesOrderDetailID, SalesOrderID, ProductID from result
WHERE result.RowNum BETWEEN ((@PageNumber-1)*@RowsPerPage)+1
AND @RowsPerPage*(@PageNumber)

anche senza CTE

use AdventureWorks
DECLARE @RowsPerPage INT = 10, @PageNumber INT = 6
SELECT SalesOrderDetailID, SalesOrderID, ProductID
FROM (
SELECT SalesOrderDetailID, SalesOrderID, ProductID,
ROW_NUMBER() OVER (ORDER BY SalesOrderDetailID) AS RowNum
FROM Sales.SalesOrderDetail
where 1=1
 ) AS SOD
WHERE SOD.RowNum BETWEEN ((@PageNumber-1)*@RowsPerPage)+1
AND @RowsPerPage*(@PageNumber)

1
Cosa fa dove 1 = 1 signore?
Errol Paleracio,

0

Bene, ho usato la seguente query di esempio nel mio database SQL 2000, funziona bene anche per SQL 2005. Il potere che ti dà è ordinato dinamicamente usando più colonne. Ti dico ... questo è potente :)

    ALTER PROCEDURE [dbo].[RE_ListingReports_SelectSummary] 

@CompanyID  int,
@pageNumber     int,
@pageSize   int, 
@sort       varchar(200)
AS

DECLARE @sql nvarchar(4000)
DECLARE @strPageSize nvarchar(20)
DECLARE @strSkippedRows nvarchar(20)
DECLARE @strFields nvarchar(4000)
DECLARE @strFilter nvarchar(4000)
DECLARE @sortBy nvarchar(4000)
DECLARE @strFrom nvarchar(4000)
DECLARE @strID nvarchar(100)

If(@pageNumber < 0)
  SET @pageNumber = 1
SET @strPageSize = CAST(@pageSize AS varchar(20)) 
SET @strSkippedRows = CAST(((@pageNumber - 1) * @pageSize) AS varchar(20))-- For    example if pageNumber is 5  pageSize is 10, then SkippedRows = 40.
SET @strID = 'ListingDbID'
SET @strFields = 'ListingDbID,
ListingID,  
[ExtraRoom]
'
SET @strFrom = ' vwListingSummary '

SET @strFilter = ' WHERE
        CompanyID = ' + CAST(@CompanyID As varchar(20)) 
End
SET @sortBy = ''
if(len(ltrim(rtrim(@sort))) > 0)
SET @sortBy = ' Order By ' + @sort

-- Total Rows Count

SET @sql =  'SELECT Count(' + @strID + ')  FROM ' + @strFROM + @strFilter
EXEC sp_executesql @sql

--// This technique is used in a Single Table pagination
SET @sql = 'SELECT ' + @strFields + ' FROM ' + @strFROM +
    ' WHERE ' + @strID +  ' IN ' + 
   '  (SELECT TOP ' + @strPageSize + ' ' + @strID + ' FROM ' + @strFROM + @strFilter + 
             ' AND  ' + @strID + ' NOT IN ' + '
          (SELECT TOP ' + @strSkippedRows + ' ' + @strID + ' FROM ' + @strFROM + @strFilter + @SortBy + ') ' 
   + @SortBy + ') ' + @SortBy
Print @sql 
EXEC sp_executesql @sql

La parte migliore è che sp_executesql memorizza nella cache le chiamate successive, a condizione che si passino gli stessi parametri, ovvero si generi lo stesso testo sql.


0
   CREATE view vw_sppb_part_listsource as 
    select row_number() over (partition by sppb_part.init_id order by sppb_part.sppb_part_id asc ) as idx, * from (
      select 
          part.SPPB_PART_ID
          , 0 as is_rev
          , part.part_number 
          , part.init_id 
      from t_sppb_init_part part 
      left join t_sppb_init_partrev prev on ( part.SPPB_PART_ID = prev.SPPB_PART_ID )
      where prev.SPPB_PART_ID is null 
      union 
      select 
          part.SPPB_PART_ID
          , 1 as is_rev
          , prev.part_number 
          , part.init_id 
      from t_sppb_init_part part 
      inner join t_sppb_init_partrev prev on ( part.SPPB_PART_ID = prev.SPPB_PART_ID )
    ) sppb_part

riavvierà idx quando si tratta di init_id diverso


0

Per la ROW_NUMBERtecnica, se non si dispone di una colonna di ordinamento da utilizzare, è possibile utilizzare CURRENT_TIMESTAMPquanto segue:

SELECT TOP 20 
    col1,
    col2,
    col3,
    col4
FROM (
    SELECT 
         tbl.col1 AS col1
        ,tbl.col2 AS col2
        ,tbl.col3 AS col3
        ,tbl.col4 AS col4
        ,ROW_NUMBER() OVER (
            ORDER BY CURRENT_TIMESTAMP
            ) AS sort_row
    FROM dbo.MyTable tbl
    ) AS query
WHERE query.sort_row > 10
ORDER BY query.sort_row

Questo ha funzionato bene per me per ricerche di dimensioni del tavolo fino a 700.000.

Questo recupera i record da 11 a 30.


Come buona pratica, con l'impaginazione dovresti provare a ordinare in base a un set univoco di colonne nel set di risultati poiché l'ordine non deve essere considerato garantito.
Arin Taylor,

2
Questo recupera i record dall'11 al 30.
Ardalan Shahgholi,

0
create PROCEDURE SP_Company_List (@pagesize int = -1 ,@pageindex int= 0   ) > AS BEGIN  SET NOCOUNT ON;


    select  Id , NameEn     from Company  ORDER by Id ASC  
OFFSET (@pageindex-1 )* @pagesize   ROWS FETCH NEXt @pagesize ROWS ONLY END  GO

DECLARE   @return_value int

EXEC  @return_value = [dbo].[SP_Company_List]         @pagesize = 1 ,         > @pageindex = 2

SELECT    'Return Value' = @return_value

GO

0

Questo bit ti dà la possibilità di impaginare usando SQL Server e le versioni più recenti di MySQL e trasporta il numero totale di righe in ogni riga. Utilizza la chiave pimary per contare il numero di righe univoche.

WITH T AS
(  
  SELECT TABLE_ID, ROW_NUMBER() OVER (ORDER BY TABLE_ID) AS RN
  , (SELECT COUNT(TABLE_ID) FROM TABLE) AS TOTAL 
  FROM TABLE (NOLOCK)
)

SELECT T2.FIELD1, T2.FIELD2, T2.FIELD3, T.TOTAL 
FROM TABLE T2 (NOLOCK)
INNER JOIN T ON T2.TABLE_ID=T.TABLE_ID
WHERE T.RN >= 100
AND T.RN < 200

Puoi per favore lasciare qualche commento che spieghi cosa fa il tuo codice?
Doug F,


0

Dal 2012 in poi possiamo usare OFFSET 10 ROWS FETCH NEXT 10 ROWS ONLY


-19

Non hai specificato la lingua né il driver che stai utilizzando. Quindi lo sto descrivendo in modo astratto.

  • Crea un set di risultati / set di dati scorrevoli. Ciò ha richiesto un primario nelle tabelle
  • saltare fino alla fine
  • richiedere il conteggio delle righe
  • passa all'inizio della pagina
  • scorrere le righe fino alla fine della pagina
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.