sacchetto di sabbia
Mentre lavoravo su Blog Posts® di alta qualità, mi sono imbattuto in alcuni comportamenti di ottimizzazione che ho trovato davvero esasperanti . Non ho immediatamente una spiegazione, almeno non una di cui sono contento, quindi la inserisco qui nel caso in cui qualcuno si presenti.
Se vuoi seguire, puoi prendere la versione 2013 del dump di dati Stack Overflow qui . Sto usando la tabella dei commenti, con un indice aggiuntivo su di essa.
CREATE INDEX [ix_ennui] ON [dbo].[Comments] ( [UserId], [Score] DESC );
Query One
Quando eseguo una query sulla tabella in questo modo, ottengo un piano di query dispari .
WITH x
AS
(
SELECT TOP 101
c.UserId, c.Text, c.Score
FROM dbo.Comments AS c
ORDER BY c.Score DESC
)
SELECT *
FROM x
WHERE x.Score >= 500;
Il predicato SARGable su Score non viene inserito all'interno del CTE. È in un operatore di filtri molto più tardi nel piano.
Che trovo strano, dal momento che ORDER BY
è sulla stessa colonna del filtro.
Query Two
Se cambio la query, viene spinta.
WITH x
AS
(
SELECT c.UserId, c.Text, c.Score
FROM dbo.Comments AS c
)
SELECT TOP 101 *
FROM x
WHERE x.Score >= 500
ORDER BY x.Score DESC;
Anche il piano di query cambia e viene eseguito molto più velocemente, senza versamenti su disco. Entrambi producono gli stessi risultati, con il predicato alla scansione dell'indice non cluster.
Query Three
Questo è l'equivalente di scrivere la query in questo modo:
SELECT TOP 101
c.UserId, c.Text, c.Score
FROM dbo.Comments AS c
WHERE c.Score >= 500
ORDER BY c.Score DESC;
Query Four
L'uso di una tabella derivata ottiene lo stesso piano di query "errato" della query CTE iniziale
SELECT *
FROM ( SELECT TOP 101
c.UserId, c.Text, c.Score
FROM dbo.Comments AS c
ORDER BY c.Score DESC ) AS x
WHERE x.Score >= 500;
Le cose diventano ancora più strane quando ...
Cambio la query per ordinare i dati in ordine crescente e il filtro su <=
.
Per non prolungare troppo la domanda, metterò tutto insieme.
Interrogazioni
--Derived table
SELECT *
FROM ( SELECT TOP 101
c.UserId, c.Text, c.Score
FROM dbo.Comments AS c
ORDER BY c.Score ASC ) AS x
WHERE x.Score <= 500;
--TOP inside CTE
WITH x
AS
(
SELECT TOP 101
c.UserId, c.Text, c.Score
FROM dbo.Comments AS c
ORDER BY c.Score ASC
)
SELECT *
FROM x
WHERE x.Score <= 500;
--Written normally
SELECT TOP 101
c.UserId, c.Text, c.Score
FROM dbo.Comments AS c
WHERE c.Score <= 500
ORDER BY c.Score ASC;
--TOP outside CTE
WITH x
AS
(
SELECT c.UserId, c.Text, c.Score
FROM dbo.Comments AS c
)
SELECT TOP 101 *
FROM x
WHERE x.Score <= 500
ORDER BY x.Score ASC;
Piani
Si noti che nessuna di queste query sfrutta l'indice non cluster: l'unica cosa che cambia qui è la posizione dell'operatore di filtro. In nessun caso il predicato viene inviato all'indice.
Appare una domanda!
C'è una ragione per cui un predicato SARGable può essere spinto in alcuni scenari e non in altri? Le differenze tra le query ordinate in ordine decrescente sono interessanti, ma le differenze tra quelle e quelle che stanno crescendo in modo bizzarro.
Per chiunque sia interessato, ecco i piani con solo un indice su Score
: