Come funziona il paging con ROW_NUMBER in SQL Server?


13

Ho una Employeetabella che ha un milione di record. Ho seguito SQL per i dati di paginazione in un'applicazione web. Funziona benissimo. Tuttavia ciò che vedo come un problema è: la tabella derivata tblEmployeeseleziona tutti i record nella Employeetabella (per creare i MyRowNumbervalori).

Penso che questo provochi la selezione di tutti i record nella Employeetabella.

Funziona davvero così? Oppure SQL Server è ottimizzato per selezionare solo i 5 record dalla Employeetabella originale ?

DECLARE @Index INT;
DECLARE @PageSize INT;

SET @Index = 3;
SET @PageSize = 5;

SELECT *  FROM
  (SELECT  ROW_NUMBER() OVER (ORDER BY EmpID asc) as MyRowNumber,*
  FROM Employee) tblEmployee
WHERE MyRowNumber BETWEEN ( ((@Index - 1) * @PageSize )+ 1) AND @Index*@PageSize 

Risposte:


17

Un'alternativa al test potrebbe essere:

;WITH x AS (SELECT EmpID, k = ROW_NUMBER() OVER (ORDER BY EmpID) FROM dbo.Emp)
SELECT e.columns
FROM x INNER JOIN dbo.Emp AS e
ON x.EmpID = e.EmpID
WHERE x.k BETWEEN (((@Index - 1) * @PageSize) + 1) AND @Index * @PageSize
ORDER BY ...;

Sì, hai colpito la tabella due volte, ma nel CTE in cui esegui la scansione dell'intera tabella stai solo prendendo la chiave, non TUTTI i dati. Ma dovresti davvero guardare questo articolo:

http://www.sqlservercentral.com/articles/T-SQL/66030/

E la discussione di follow-up:

http://www.sqlservercentral.com/Forums/Topic672980-329-1.aspx

Ovviamente in SQL Server 2012 è possibile utilizzare la sintassi new OFFSET/ FETCH NEXT:

;WITH x AS 
(
  SELECT EmpID FROM dbo.Emp
    ORDER BY EmpID
    OFFSET  @PageSize * (@Index - 1) ROWS
    FETCH NEXT @PageSize ROWS ONLY
)
SELECT e.columns
FROM x INNER JOIN dbo.Emp AS e
ON x.EmpID = e.EmpID
ORDER BY ...; 

Va notato tuttavia che OFFSET / FETCH NEXT non offrono alcun vantaggio in termini di prestazioni rispetto al metodo CTE
Akash

2
@Akash l'hai provato a fondo? Ho osservato alcune differenze di piano ma non ho menzionato in modo specifico le prestazioni perché non ho eseguito test approfonditi. Anche se le prestazioni sono le stesse, la sintassi è leggermente meno complessa. Ne ho fatto un blog qui: sqlblog.com/blogs/aaron_bertrand/archive/2010/11/10/…
Aaron Bertrand

1
Ah, hai ragione, c'è una differenza di prestazioni. Avevo letto questo: blogs.technet.com/b/dataplatforminsider/archive/2011/11/01/… dove non menziona alcuna differenza, ma ho appena visto channel9.msdn.com/posts/SQL11UPD03-REC-02 dove mostra molta differenza .. (sebbene nella sottofase audio la differenza di prestazioni)
Akash

2

Anche se potresti non conoscere il meccanismo alla base, puoi testarlo tu stesso confrontando le prestazioni della tua query con: selezionare * da Dipendente.

Le versioni più recenti di SQL Server fanno un ottimo lavoro di ottimizzazione, ma possono dipendere da diversi fattori.

Le prestazioni della tua funzione ROW_NUMBER saranno determinate dalla clausola Order By. Nel tuo esempio, la maggior parte immaginerebbe che EmpID sia la chiave primaria.

Ci sono alcune clausole in cui le clausole sono così complesse e / o scarsamente codificate o indicizzate, potrebbe essere meglio restituire l'intero set di dati (è raro e può essere corretto). L'uso di FRA ha dei problemi.

Prima di assumere che sarebbe meglio restituire tutte le righe alla tua applicazione e lasciarlo capire, dovresti lavorare sull'ottimizzazione della tua query. Controlla le stime. Chiedi a Query Analyzer. Prova alcune alternative.


2

So che la domanda riguarda row_number () ma voglio aggiungere una nuova funzionalità di sql server 2012. In sql server 2012 è stata introdotta la nuova funzione OFFSET Recupera successiva ed è molto veloce di row_number (). L'ho usato e mi dà un buon risultato, spero che anche voi ragazzi riempiate la stessa esperienza.

Ho trovato un esempio su http://blogfornet.com/2013/06/sql-server-2012-offset-use/

che è utile. Spero che possa esserti utile anche per implementare nuove funzionalità ....


-2

Non credo che valuti per restituire tutte le righe nella tabella originale. Il server SQL ottimizza. Altrimenti ci vorrà un sacco di tempo per selezionare un milione di voci. Attualmente sto usando questo ed è molto più veloce rispetto alla selezione di tutte le righe. Quindi, sicuramente non ottiene tutte le righe. È comunque più lento del semplice recupero delle prime cinque righe, probabilmente a causa del tempo impiegato nell'ordinamento


-2
DECLARE @PageIndex int;
DECLARE @PageSize int;
SET @PageIndex = 4;
SET @PageSize = 5;
;With ranked AS   --- Or you can make it a view
(
   SELECT ROW_NUMBER() OVER(ORDER BY IdentityId) AS RowNum,  *
   FROM logeventnew
)
SELECT *   --Your fields here
FROM Ranked
WHERE RowNum BETWEEN ((@PageIndex - 1) * @PageSize + 1)
    AND (@PageIndex * @PageSize)
ORDER BY IdentityId

4
Potresti espandere la tua risposta? La domanda riguardava il modo in cui il paging funziona internamente a SQL Server, ovvero cosa fa il motore di database per soddisfare la richiesta. Sfortunatamente, fin d'ora, la tua risposta non affronta il problema reale.
Mr.Brownstone,
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.