Sqlserver
 sql >> Database >  >> RDS >> Sqlserver

Prestazioni SQL:DOVE vs DOVE(ROW_NUMBER)

Come altri hanno già sottolineato, le query restituiscono risultati diversi e stanno confrontando mele con arance.

Ma la domanda di fondo rimane:che è più veloce:paging guidato da keyset o paging guidato da numero di riga?

Cercapersone Keyset

Il paging guidato dal keyset si basa sul ricordo dei tasti superiore e inferiore dell'ultima pagina visualizzata e sulla richiesta del set di righe successivo o precedente, in base al keyset superiore/ultimo:

Pagina successiva:

select top (<pagesize>) ...
from <table>
where key > @last_key_on_current_page
order by key;

Pagina precedente:

select top (<pagesize>)
from <table>
where key < @first_key_on_current_page
order by key desc;

Questo approccio presenta due vantaggi principali rispetto all'approccio ROW_NUMBER o rispetto all'equivalente approccio LIMIT di MySQL:

  • è corretto :a differenza dell'approccio basato sul numero di riga, gestisce correttamente le nuove voci e le voci eliminate. L'ultima riga di Pagina 4 non viene visualizzata come prima riga di Pagina 5 solo perché la riga 23 di Pagina 2 è stata eliminata nel frattempo. Né le righe svaniscono misteriosamente tra le pagine. Queste anomalie sono comuni con l'approccio basato su numero_riga, ma la soluzione basata su set di chiavi fa un lavoro molto migliore nell'evitarle.
  • è veloce :tutte le operazioni possono essere risolte con un posizionamento veloce della riga seguito da una scansione del range nella direzione desiderata

Tuttavia, questo approccio è difficile da implementare, di difficile comprensione per il programmatore medio e non supportato dagli strumenti.

Numero di riga guidato

Questo è l'approccio comune introdotto con le query Linq:

select ...
from (
  select ..., row_number() over (...) as rn
  from table)
where rn between @firstRow and @lastRow;

(o una query simile utilizzando TOP)Questo approccio è facile da implementare ed è supportato da strumenti (nello specifico dagli operatori Linq .Limit e .Take). Ma questo approccio è garantito per scansionare l'indice per contare le righe. Questo approccio di solito funziona molto velocemente per la pagina 1 e rallenta gradualmente man mano che si passa a numeri di pagina sempre più alti.

Come bonus, con questa soluzione è molto semplice modificare l'ordinamento (basta cambiare la clausola OVER).

Nel complesso, data la facilità delle soluzioni basate su ROW_NUMBER(), il supporto che hanno da Linq, la semplicità di utilizzare ordini arbitrari per set di dati moderati le soluzioni basate su ROW_NUMBER sono adeguate. Per set di dati grandi e molto grandi, ROW_NUMBER() può causare seri problemi di prestazioni.

Un'altra cosa da considerare è che spesso c'è un modello definito di accesso. Spesso le prime pagine sono calde e le pagine successive alla 10 non vengono praticamente mai visualizzate (ad es. i post più recenti). In questo caso, la penalità che si verifica con ROW_NUMBER() per la visita delle ultime pagine (pagine di visualizzazione per le quali è necessario contare un numero elevato di righe per ottenere la riga del risultato iniziale) potrebbe essere ben ignorata.

E infine, l'impaginazione del keyset è ottima per la navigazione nel dizionario, che ROW_NUMBER() non può ospitare facilmente. La navigazione nel dizionario è dove invece di utilizzare il numero di pagina, gli utenti possono navigare verso determinati ancoraggi, come le lettere dell'alfabeto. Un tipico esempio è un contatto Rolodex come barra laterale, fai clic su M e vai al nome del primo cliente che inizia con M.