Brutto. Ecco come appaiono i dati non ordinati. Semplifichiamo i dati per gli occhi ordinandoli. Ed è a questo che serve SQL ORDER BY. Utilizzare una o più colonne o espressioni come base per ordinare i dati. Quindi, aggiungi ASC o DESC per ordinare crescente o decrescente.
La sintassi SQL ORDER BY:
ORDER BY <order_by_expression> [ASC | DESC]
L'espressione ORDER BY può essere semplice come un elenco di colonne o espressioni. Può anche essere condizionale utilizzando un blocco CASE WHEN.
È molto flessibile.
Puoi anche utilizzare il paging tramite OFFSET e FETCH. Specifica il numero di righe da saltare e le righe da mostrare.
Ma ecco la cattiva notizia.
L'aggiunta di ORDER BY alle tue query può rallentarle. E alcuni altri avvertimenti possono rendere ORDER BY "non funzionante". Non puoi semplicemente usarli ogni volta che vuoi, poiché potrebbero esserci delle sanzioni. Allora, cosa facciamo?
In questo articolo, esamineremo le cose da fare e da non fare nell'utilizzo di ORDER BY. Ogni articolo affronterà un problema e seguirà una soluzione.
Pronto?
Cose da fare in SQL ORDINA PER
1. Indicizza le colonne SQL ORDER BY
Gli indici riguardano ricerche rapide. E averne uno nelle colonne che usi nella clausola ORDER BY può velocizzare la tua query.
Iniziamo a usare ORDER BY in una colonna senza indice. Utilizzeremo AdventureWorks database di esempio. Prima di eseguire la query seguente, disabilita IX_SalesOrderDetail_ProductID indice in SalesOrderDetail tavolo. Quindi, premi Ctrl-M ed eseguilo.
-- Get order details by product and sort them by ProductID
USE AdventureWorks
GO
SET STATISTICS IO ON
GO
SELECT
ProductID
,OrderQty
,UnitPrice
,LineTotal
FROM Sales.SalesOrderDetail
ORDER BY ProductID
SET STATISTICS IO OFF
GO
ANALISI
Il codice precedente genererà le statistiche di I/O nella scheda Messaggi di SQL Server Management Studio. Vedrai il piano di esecuzione in un'altra scheda.
SENZA UN INDICE
Per prima cosa, prendiamo le letture logiche da STATISTICS IO. Dai un'occhiata alla Figura 1.
Figura 1 . Letture logiche utilizzando ORDER BY di una colonna non indicizzata. (Formattato utilizzando statisticsparser.com )
Senza l'indice, la query utilizzava 1.313 letture logiche. E quel WorkTable ? Significa che SQL Server ha utilizzato TempDB per elaborare l'ordinamento.
Ma cosa è successo dietro le quinte? Esaminiamo il piano di esecuzione nella Figura 2.
Figura 2 . Piano di esecuzione di una query utilizzando ORDER BY di una colonna non indicizzata.
Hai visto quell'operatore Parallelism (Gather Streams)? Significa che SQL Server ha utilizzato più di 1 processore per elaborare questa query. La query era abbastanza pesante da richiedere più CPU.
E se SQL Server utilizzasse TempDB e più processori? Non va bene per una semplice query.
CON UN INDICE
Come andrà se l'indice viene riattivato? Scopriamolo. Ricostruisci l'indice IX_SalesOrderDetail_ProductID . Quindi, riesegui la query precedente.
Controllare le nuove letture logiche nella Figura 3.
Figura 3 . Nuove letture logiche dopo la ricostruzione dell'indice.
Questo è molto meglio. Abbiamo ridotto di quasi la metà il numero di letture logiche. Ciò significa che l'indice ha fatto consumare meno risorse. E il Tabella di lavoro ? È andato! Non è necessario utilizzare TempDB .
E il piano di esecuzione? Vedi figura 4.
Figura 4 . Il nuovo piano di esecuzione è più semplice quando l'indice è stato ricostruito.
Vedere? Il piano è più semplice. Non c'è bisogno di CPU aggiuntive per ordinare le stesse 121.317 righe.
Quindi, la linea di fondo è:Assicurati che le colonne che utilizzi per ORDER BY siano indicizzate .
MA E SE L'AGGIUNTA DI UN INDICE HA UN IMPATTO SULLE PRESTAZIONI DI SCRITTURA?
Bella domanda.
Se questo è il problema, puoi scaricare una parte della tabella di origine in una tabella temporanea o in una tabella ottimizzata per la memoria . Quindi, indicizza quella tabella. Usa lo stesso se sono coinvolte più tabelle. Quindi, valuta le prestazioni della query dell'opzione scelta. L'opzione più veloce sarà la vincitrice.
2. Limita i risultati con WHERE e OFFSET/FETCH
Usiamo una query diversa. Supponiamo che tu debba visualizzare le informazioni sul prodotto con le immagini in un'app. Le immagini possono rendere le query ancora più pesanti. Quindi, non controlleremo solo le letture logiche, ma anche le letture logiche lob.
Ecco il codice.
SET STATISTICS IO ON
GO
SELECT
a.ProductID
,a.Name AS ProductName
,a.ListPrice
,a.Color
,b.Name AS ProductSubcategory
,d.ThumbNailPhoto
,d.LargePhoto
FROM Production.Product a
INNER JOIN Production.ProductSubcategory b ON a.ProductSubcategoryID = b.ProductSubcategoryID
INNER JOIN Production.ProductProductPhoto c ON a.ProductID = c.ProductID
INNER JOIN Production.ProductPhoto d ON c.ProductPhotoID = d.ProductPhotoID
WHERE b.ProductCategoryID = 1 -- Bikes
ORDER BY ProductSubcategory, ProductName, a.Color
SET STATISTICS IO OFF
GO
Questo produrrà 97 biciclette con immagini. Sono molto difficili da navigare su un dispositivo mobile.
ANALISI
UTILIZZO DELLA CONDIZIONE MINIMA DOVE SENZA OFFSET/FETCH
Ecco quante letture logiche sono necessarie per recuperare 97 prodotti con immagini. Vedi figura 5.
Figura 5 . Le letture logiche e le letture logiche lob quando si utilizza ORDER BY senza OFFSET/FETCH e con una condizione WHERE minima . (Nota:Statisticsparser.com non ha mostrato le letture logiche lob. Lo screenshot viene modificato in base al risultato in SSMS)
667 letture logiche lob sono apparse a causa del recupero di immagini in 2 colonne. Nel frattempo, per il resto sono state utilizzate 590 letture logiche.
Ecco il piano di esecuzione nella Figura 6 in modo da poterlo confrontare in seguito con il piano migliore.
Figura 6 . Piano di esecuzione utilizzando ORDER BY senza OFFSET/FETCH e con una condizione WHERE minima.
Non c'è molto altro da dire finché non vedremo l'altro piano di esecuzione.
UTILIZZO AGGIUNTIVO WHERE CONDITION E OFFSET/FETCH IN ORDINE BY
Ora, regoliamo la query per assicurarci che vengano restituiti dati minimi. Ecco cosa faremo:
- Aggiungi una condizione nella sottocategoria del prodotto. Nell'app di chiamata, possiamo immaginare di lasciare che anche l'utente scelga la sottocategoria.
- Quindi, rimuovi la sottocategoria del prodotto nell'elenco di colonne SELECT e nell'elenco di colonne ORDER BY.
- Infine, aggiungi OFFSET/FETCH in ORDER BY. Solo 10 prodotti verranno restituiti e visualizzati nell'app di chiamata.
Ecco il codice modificato.
DECLARE @pageNumber TINYINT = 1
DECLARE @noOfRows TINYINT = 10 -- each page will display 10 products at a time
SELECT
a.ProductID
,a.Name AS ProductName
,a.ListPrice
,a.Color
,d.ThumbNailPhoto
FROM Production.Product a
INNER JOIN Production.ProductSubcategory b ON a.ProductSubcategoryID = b.ProductSubcategoryID
INNER JOIN Production.ProductProductPhoto c ON a.ProductID = c.ProductID
INNER JOIN Production.ProductPhoto d ON c.ProductPhotoID = d.ProductPhotoID
WHERE b.ProductCategoryID = 1 -- Bikes
AND a.ProductSubcategoryID = 2 -- Road Bikes
ORDER BY ProductName, a.Color
OFFSET (@pageNumber-1)*@noOfRows ROWS FETCH NEXT @noOfRows ROWS ONLY
Questo codice migliorerà ulteriormente se lo trasformi in una stored procedure. Avrà anche parametri come il numero di pagina e il numero di righe. Il numero di pagina indica la pagina che l'utente sta attualmente visualizzando. Migliora ulteriormente questo aspetto rendendo flessibile il numero di righe in base alla risoluzione dello schermo. Ma questa è un'altra storia.
Ora, diamo un'occhiata alle letture logiche nella Figura 7.
Figura 7 . Meno letture logiche dopo aver semplificato la query. OFFSET/FETCH viene utilizzato anche in ORDER BY.
Quindi, confrontare la Figura 7 con la Figura 5. Le letture logiche lob sono sparite. Inoltre, le letture logiche hanno una notevole diminuzione poiché anche il set di risultati è stato ridotto da 97 a 10.
Ma cosa ha fatto SQL Server dietro le quinte? Controlla il piano di esecuzione nella Figura 8.
Figura 8 . Un piano di esecuzione più semplice dopo aver semplificato la query e aggiunto OFFSET/FETCH in ORDER BY.
Quindi, confronta la Figura 8 con la Figura 6. Senza esaminare ciascun operatore, possiamo vedere che questo nuovo piano è più semplice del precedente.
La lezione? Semplifica la tua richiesta. Usa OFFSET/FETCH quando possibile.
Da non fare in SQL ORDINA PER
Abbiamo finito con quello che dobbiamo fare quando utilizziamo ORDER BY. Questa volta, concentriamoci su ciò che dovremmo evitare.
3. Non utilizzare ORDER BY durante l'ordinamento in base alla chiave di indice cluster
Perché è inutile.
Mostriamolo con un esempio.
SET STATISTICS IO ON
GO
-- Using ORDER BY with BusinessEntityID - the primary key
SELECT TOP 100 * FROM Person.Person
ORDER BY BusinessEntityID;
-- Without using ORDER BY at all
SELECT TOP 100 * FROM Person.Person;
SET STATISTICS IO OFF
GO
Quindi, controlliamo le letture logiche di entrambe le istruzioni SELECT nella Figura 9.
Figura 9 . 2 query sulla tabella Person mostrano le stesse letture logiche. Uno è con ORDER BY, un altro senza.
Entrambi hanno 17 letture logiche. Ciò è logico a causa delle stesse 100 righe restituite. Ma hanno lo stesso piano? Dai un'occhiata alla Figura 10.
Figura 10 . Lo stesso piano indipendentemente dal fatto che ORDER BY venga utilizzato o meno durante l'ordinamento in base alla chiave dell'indice cluster.
Osserva gli stessi operatori e lo stesso costo della query.
Ma perché? Quando si indicizza una o più colonne in un indice cluster, la tabella verrà ordinata fisicamente dalla chiave dell'indice cluster. Quindi, anche se non ordini in base a quella chiave, il risultato sarà comunque ordinato.
Linea di fondo? Perdona te stesso non utilizzando la chiave dell'indice cluster in casi simili utilizzando ORDER BY . Risparmia energia con meno sequenze di tasti.
4. Non utilizzare ORDER BY quando una colonna di stringhe contiene numeri
Se ordini in base a una colonna di stringa contenente numeri, non aspettarti l'ordinamento come i tipi di numeri reali. Altrimenti, ti aspetta una grande sorpresa.
Ecco un esempio.
SELECT
NationalIDNumber
,JobTitle
,HireDate
FROM HumanResources.Employee
ORDER BY NationalIDNumber;
Controllare l'output nella Figura 11.
Figura 11 . Ordinamento di una colonna di stringa contenente numeri. Il valore numerico non viene seguito.
Nella Figura 11 viene seguito l'ordinamento lessicografico. Quindi, per risolvere questo problema, usa un CAST su un numero intero.
SELECT
NationalIDNumber
,JobTitle
,HireDate
FROM HumanResources.Employee
ORDER BY CAST(NationalIDNumber AS INT)
Dai un'occhiata alla Figura 12 per l'output fisso.
Figura 12 . CAST to INT ha corretto l'ordinamento di una colonna di stringhe contenente numeri.
Quindi, iinvece di ORDER BY
5. Non utilizzare SELECT INTO #TempTable con ORDER BY
L'ordinamento desiderato non sarà garantito nella tabella temporanea di destinazione. Consulta la documentazione ufficiale .
Prendiamo un codice modificato dall'esempio precedente.
SELECT
NationalIDNumber
,JobTitle
,HireDate
INTO #temp
FROM HumanResources.Employee
ORDER BY CAST(NationalIDNumber AS INT);
SELECT * FROM #temp;
L'unica differenza rispetto all'esempio precedente è la clausola INTO. L'output sarà lo stesso della Figura 11. Torniamo al quadrato 1 anche se CAST la colonna su INT.
È necessario creare una tabella temporanea utilizzando CREATE TABLE. Ma includi una colonna di identità aggiuntiva e rendila una chiave primaria. Quindi, INSERT nella tabella temporanea.
Ecco il codice fisso.
CREATE TABLE #temp2
(
id INT IDENTITY(1,1) NOT NULL PRIMARY KEY,
NationalIDNumber NVARCHAR(15) NOT NULL,
JobTitle NVARCHAR(50) NOT NULL,
HireDate DATE NOT NULL
)
GO
INSERT INTO #temp2
(NationalIDNumber, JobTitle, HireDate)
SELECT
NationalIDNumber
,JobTitle
,HireDate
FROM HumanResources.Employee
ORDER BY CAST(NationalIDNumber AS INT);
SELECT
NationalIDNumber
,JobTitle
,HireDate
FROM #Temp2;
E l'output sarà lo stesso della Figura 12. Funziona!
Da asporto nell'uso di SQL ORDINA PER
Abbiamo coperto le insidie comuni nell'utilizzo di SQL ORDER BY. Ecco un riassunto:
Cose da fare :
- Indicizza le colonne ORDER BY
- Limita i risultati con WHERE e OFFSET/FETCH,
Non fare :
- Non utilizzare ORDER BY quando esegui l'ordinamento in base alla chiave dell'indice cluster,
- Non utilizzare ORDER BY quando una colonna di stringa contiene numeri. Invece, CAST prima la colonna della stringa su INT.
- Non utilizzare SELECT INTO #TempTable con ORDER BY. Invece, crea prima la tabella temporanea con una colonna identità aggiuntiva.
Quali sono i tuoi suggerimenti e trucchi nell'utilizzo di ORDER BY? Fatecelo sapere nella sezione Commenti qui sotto. E se ti piace questo post, condividilo sulle tue piattaforme di social media preferite.