Gli esempi nella domanda non producono esattamente gli stessi risultati (l' OFFSET
esempio ha un errore off-by-one). I moduli aggiornati di seguito risolvono il problema, rimuovono l'ordinamento aggiuntivo per il ROW_NUMBER
caso e utilizzano le variabili per rendere la soluzione più generale:
DECLARE
@PageSize bigint = 10,
@PageNumber integer = 3;
WITH Numbered AS
(
SELECT TOP ((@PageNumber + 1) * @PageSize)
o.*,
rn = ROW_NUMBER() OVER (
ORDER BY o.[object_id])
FROM #objects AS o
ORDER BY
o.[object_id]
)
SELECT
x.name,
x.[object_id],
x.principal_id,
x.[schema_id],
x.parent_object_id,
x.[type],
x.type_desc,
x.create_date,
x.modify_date,
x.is_ms_shipped,
x.is_published,
x.is_schema_published
FROM Numbered AS x
WHERE
x.rn >= @PageNumber * @PageSize
AND x.rn < ((@PageNumber + 1) * @PageSize)
ORDER BY
x.[object_id];
SELECT
o.name,
o.[object_id],
o.principal_id,
o.[schema_id],
o.parent_object_id,
o.[type],
o.type_desc,
o.create_date,
o.modify_date,
o.is_ms_shipped,
o.is_published,
o.is_schema_published
FROM #objects AS o
ORDER BY
o.[object_id]
OFFSET @PageNumber * @PageSize - 1 ROWS
FETCH NEXT @PageSize ROWS ONLY;
Il ROW_NUMBER
piano ha un costo stimato di 0,0197935 :
Il OFFSET
piano ha un costo stimato di 0,0196955 :
Si tratta di un risparmio di 0,000098 unità di costo stimato (sebbene il OFFSET
piano richiederebbe operatori extra se si desidera restituire un numero di riga per ogni riga). Il OFFSET
piano sarà ancora leggermente più economico, in generale, ma ricorda che i costi stimati sono esattamente questo: sono ancora necessari test reali. La maggior parte del costo in entrambi i piani è il costo dell'intero tipo di set di input, quindi indici utili andrebbero a beneficio di entrambe le soluzioni.
Laddove vengono utilizzati valori letterali costanti (ad esempio OFFSET 30
nell'esempio originale), l'ottimizzatore può utilizzare un ordinamento TopN anziché un ordinamento completo seguito da un valore superiore. Quando le righe necessarie per l'ordinamento TopN sono letterali costanti e <= 100 (la somma di OFFSET
e FETCH
) il motore di esecuzione può utilizzare un algoritmo di ordinamento diverso che può eseguire più velocemente dell'ordinamento TopN generalizzato. Tutti e tre i casi hanno caratteristiche prestazionali diverse nel complesso.
Per quanto riguarda il motivo per cui l'ottimizzatore non trasforma automaticamente il ROW_NUMBER
modello di sintassi da utilizzare OFFSET
, ci sono una serie di motivi:
- È quasi impossibile scrivere una trasformazione che corrisponda a tutti gli usi esistenti
- La trasformazione automatica di alcune query di paging e non altre potrebbe creare confusione
- Il
OFFSET
piano non è garantito per essere migliore in tutti i casi
Un esempio per il terzo punto precedente si verifica in cui il set di paging è piuttosto ampio. Può essere molto più efficiente cercare le chiavi necessarie utilizzando un indice non cluster e cercare manualmente l'indice cluster rispetto alla scansione dell'indice con OFFSET
o ROW_NUMBER
. Vi sono ulteriori problemi da considerare se l'applicazione di paging deve sapere quante righe o pagine sono presenti in totale. C'è un'altra buona discussione sui meriti relativi dei metodi "ricerca chiave" e "offset" qui .
Nel complesso, è probabilmente meglio che le persone prendano una decisione informata di modificare le loro query di paging da utilizzare OFFSET
, se appropriato, dopo test approfonditi.