vorrei provare qualcosa di simile a questo:
WITH CTEOrder AS (
SELECT
OrderID,
ROW_NUMBER() OVER (ORDER BY OrderID ASC) AS ROW_ID
FROM Orders
)
, CTECustomerName AS (
SELECT
OrderID,
ROW_NUMBER() OVER (ORDER BY Surname ASC) AS ROW_ID
FROM Orders
)
, CTECombined AS
(
SELECT 'OrderID' OrderByType, OrderID, Row_ID
FROM CTEOrder
WHERE Row_id BETWEEN @RowStart AND @RowStart + @RowCount -1
UNION
SELECT 'CustomerName' OrderByType, OrderID, Row_ID
FROM CTECustomerName
WHERE row_id BETWEEN @RowStart AND @RowStart + @RowCount -1
)
SELECT Orders.* FROM CTECombined
INNER JOIN Orders ON CTECombined.OrderID = Orders.OrderID
WHERE ROW_ID BETWEEN @RowStart AND @RowStart + @RowCount -1;
AND OrderByType = @SortCol
ho provato questo con una delle mie tabelle, che ha app . 4 milioni di dischi. Ovviamente, ha nomi di campo diversi, quindi mi scuso se non ho "tradotto" correttamente nella risposta e SQL non funziona per te. Tuttavia, l'idea dovrebbe essere ovvia.
Con il codice nella tua domanda, ottengo 200000 letture logiche e 6068 ms CPU sulla mia tabella e con quanto sopra ottengo 1422 letture logiche e 78 ms CPU.
non ho la cache a filo o altre cose necessarie per un vero e proprio punto di riferimento, ma l'ho fatto provare con diverse pagine, pagesizes, ecc ei miei risultati sono stati coerenti fin dall'inizio.
Se si dispone di query con molti campi diversi che si desidera ordinare, questa soluzione potrebbe non essere sufficientemente scalabile dal momento che sarà necessario espandere il numero di CTE, ma è possibile farlo in codice se si sta costruendo SQL comunque - e per l'esempio con l'ordinamento su due campi diversi funziona come un incantesimo per me.
EDIT: Vieni a pensarci bene, probabilmente non avrà bisogno di CTE individuali per ogni OrderBy colonne, probabilmente sarete in grado di avere un solo dove si fa sia ROW_NUMBER()
e UNION
nella stessa CTE. Il principio è lo stesso, e penserei che l'ottimizzatore finirebbe per fare lo stesso, ma non l'ho ancora analizzato. Aggiornerò la risposta se e quando avrò il tempo di verificarlo.
MODIFICA 2: Come previsto, è possibile eseguire UNION
all'interno di un CTE. Non ho intenzione di aggiornare il codice, ma fornirò alcuni benchmark sul codice così com'è. Ho fatto un pageize di 10000 righe per vedere se ciò ha fatto una grande differenza, ma non è così. (Le due esecuzioni di cusinar9 e il mio codice rispettivamente sono state eseguite in modo equivalente, quindi l'avvio a freddo aveva parametri identici per entrambe le versioni del codice e la seconda esecuzione aveva parametri diversi, ma lo stesso per le due versioni del codice):
codice di cusimar9, avviamento a freddo:
Table 'TestTable'. Scan count 10009, logical reads 43080, physical reads 189, read-ahead reads 12915, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table 'Worktable'. Scan count 0, logical reads 0, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
SQL Server Execution Times: CPU time = 3037 ms, elapsed time = 2206 ms.
codice di cusimar9, 2 ° periodo, diversi parametri:
Table 'TestTable'. Scan count 10009, logical reads 43096, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table 'Worktable'. Scan count 0, logical reads 0, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
SQL Server Execution Times: CPU time = 4132 ms, elapsed time = 1012 ms.
Il mio suggerimento, avviamento a freddo:
Table 'TestTable'. Scan count 10001, logical reads 31963, physical reads 12, read-ahead reads 6984, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table 'Worktable'. Scan count 0, logical reads 0, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
SQL Server Execution Times: CPU time = 218 ms, elapsed time = 1410 ms.
Il mio suggerimento, 2 ° periodo:
Table 'TestTable'. Scan count 10001, logical reads 31963, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table 'Worktable'. Scan count 0, logical reads 0, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
SQL Server Execution Times: CPU time = 218 ms, elapsed time = 358 ms.
Edit 3: Vedendo il codice scritto, ho notato questa costruzione:
PagedCTE AS (
SELECT (SELECT Max(ROW_ID) FROM OrderByCTE) AS TOTAL_ROWS, OrderID
FROM OrderByCTE
WHERE
OrderByCTE.SortCol = @SortCol AND OrderByCTE.SortDir = @SortDir AND
OrderByCTE.ROW_ID BETWEEN @RowStart AND @RowStart + @RowCount -1
)
Non sono sicuro al 100% circa lo scopo di questo, ma suppongo che tu voglia restituire il totale delle righe in modo che tu possa calcolare (e mostrare all'utente) quanto siamo lontani nel recordset. Quindi, perché non ottenere quel numero al di fuori di tutto il rumore ORDER BY
? Per mantenere lo stile attuale, perché non creare un CTE con un SELECT COUNT(*)
? E poi partecipa alla selezione finale?
SQL Server 2012 offre 'Offset/FETCH'. Ma è necessario decidere quale valore apprezzi di più: prestazioni o manutenibilità. Devi sceglierne uno. –
E perché stai facendo un 'JOIN' alla tabella' Orders' sull'ultimo 'SELECT' ?, non è necessario – Lamak
Lamak il join indietro sulla tabella Orders è un miglioramento delle prestazioni, il CTE restituisce solo le colonne minime richieste – cusimar9