In questo articolo vi racconto un estratto del talk “Ordering Woes” al SqlBits 2019 di Itzik Ben-Gan. Piccolo spoiler: vedremo una casistica particolare dove l’aggiunta di un Order By migliora le performance di una query, contro ogni pronostico aggiungerei. Vi consiglio di leggere questo articolo, è sicuramente molto utile a fine didattico anche perché la query proposta non è affatto artificiosa, ci dà idea inoltre del talento di Ben-Gan nel tuning delle performance di una query SQL. Tuttavia ci deve essere ben chiaro che nel restante 99,9% dei casi l’aggiunta di un ordinamento non necessario ci porterà a importanti peggioramenti delle performance.
Costruiamo il caso d’esempio
Creiamo nel nostro Database d’esempio una tabella Ordini con due colonne di circa diecimila righe. La prima avrà la proprietà Identity e sarà chiave primaria. Popoliamo inizialmente la tabella costruendo un progressivo anche per la colonna IdCliente per circa 10000 righe.
CREATE TABLE Ordini(
IdOrdine INT IDENTITY(1,1) PRIMARY KEY,
IdCliente INT)
INSERT INTO Ordini(IdCliente)
SELECT TOP 10000
ROW_NUMBER() OVER(ORDER BY (SELECT NULL))
AS IdCliente
FROM sys.objects AS a
CROSS JOIN sys.objects AS b;
A questo punto facciamo in modo da avere per ogni cliente quattro ordini. Per far questo lanciamo due volte questa query
INSERT INTO Ordini(IdCliente)
SELECT IdCliente
FROM Ordini;
Verifichiamo il contenuto della tabella eseguendo una semplice
SELECT *
FROM Ordini
WHERE IdCliente = 1
Se abbiamo seguito alla lettera le istruzioni avremo quattro righe. Creiamo ora un indice non cluster sulla colonna IdCliente.
CREATE NONCLUSTERED INDEX IX_IdCliente
ON Ordini(IdCliente)
Nel prossimo paragrafo analizzeremo la query per estrarre i due ordini con Id maggiore per ogni cliente
Scriviamo la query e miglioriamo le performance
Possiamo risolvere l’esercizio utilizzando le window function.
WITH CTE AS
(SELECT *,
RANK() OVER(PARTITION BY IdCliente
ORDER BY IdOrdine DESC) AS Rango
FROM Ordini)
SELECT *
FROM CTE
WHERE Rango <= 2;
Analizzando il piano d’esecuzione notiamo:
- un costosissimo operatore Sort
- il mancato utilizzo dell’indice sulla colonna IdCliente

Riscriviamo la query aggiungendo in fondo l’ORDER BY descrescente per IdCliente. Solo se l’ordinamento è coerente con quello all’interno della Window Function, SQL Server sceglierà un piano d’esecuzione che utilizzi l’indice sulla colonna IdCliente, con un importante miglioramento delle performance per la prima parte della query.
WITH CTE AS
(SELECT *,
RANK() OVER(PARTITION BY IdCliente
ORDER BY IdOrdine DESC) AS Rango
FROM Ordini)
SELECT *
FROM CTE
WHERE Rango <= 2
ORDER BY IdCliente DESC;
Vediamo infatti che sparisce il costosissimo operatore Sort.

Continua a imparare
- Conosci il fenomeno del Parameter sniffing?
- Analizziamo i possibili impatti negativi degli indici
- Il mio video-corso sul tuning delle query e il T-SQL avanzato https://www.yimp.it/corso-t-sql-avanzato/