Perché questa query non utilizza uno spool di indice?


23

Sto ponendo questa domanda al fine di comprendere meglio il comportamento dell'ottimizzatore e comprendere i limiti relativi agli spool degli indici. Supponiamo che io metta numeri interi da 1 a 10000 in un heap:

CREATE TABLE X_10000 (ID INT NOT NULL);
truncate table X_10000;

INSERT INTO X_10000 WITH (TABLOCK)
SELECT TOP 10000 ROW_NUMBER() OVER (ORDER BY (SELECT NULL))
FROM master..spt_values t1
CROSS JOIN master..spt_values t2;

E forzare un loop nidificato con MAXDOP 1:

SELECT *
FROM X_10000 a
INNER JOIN X_10000 b ON a.ID = b.ID
OPTION (LOOP JOIN, MAXDOP 1);

Questa è un'azione piuttosto ostile da intraprendere verso SQL Server. I join di loop nidificati spesso non sono una buona scelta quando entrambe le tabelle non hanno indici rilevanti. Ecco il piano:

query errata

La query impiega 13 secondi sulla mia macchina con 100000000 righe recuperate dallo spool della tabella. Tuttavia, non vedo perché la query debba essere lenta. Query Optimizer ha la capacità di creare indici al volo tramite spool di indice . Questa query sembra essere un candidato perfetto per uno spool di indice.

La query seguente restituisce gli stessi risultati della prima, ha uno spool di indice e termina in meno di un secondo:

SELECT *
FROM X_10000 a
CROSS APPLY (SELECT TOP (9223372036854775807) b.ID FROM X_10000 b WHERE a.ID = b.ID) ca
OPTION (LOOP JOIN, MAXDOP 1);

soluzione alternativa 1

Questa query ha anche uno spool di indice e termina in meno di un secondo:

SELECT *
FROM X_10000 a
INNER JOIN X_10000 b ON a.ID >= b.ID AND a.ID <= b.ID
OPTION (LOOP JOIN, MAXDOP 1);

soluzione alternativa 2

Perché la query originale non ha uno spool di indice? Esiste un insieme di suggerimenti documentati o non documentati o flag di traccia che gli daranno uno spool di indice? Ho trovato questa domanda correlata , ma non risponde completamente alla mia domanda e non riesco a far funzionare il misterioso flag di traccia per questa query.

Risposte:


20

Come sapete, la ricerca dell'ottimizzatore non è esaustiva. Prova cose che hanno senso nel contesto e che spesso pagano dividendi su domande reali. Costringere un loop loop tra due tabelle heap non indicizzate a colonna singola non è un tale scenario. Detto questo, ecco alcuni dettagli:

A SQL Server piace trasformare presto i join, perché conosce più trucchi con i join. Successivamente, potrebbe esplorare la conversione del join in un'applicazione. La differenza tra i due parametri correlati (riferimenti esterni). Si applica quando c'è un indice adatto sul lato interno. Il tuo esempio non ha indici, quindi l'ottimizzatore non è persuaso a esplorare la traduzione per applicare.

Un join semplice (non applicabile) ha il predicato di join sull'operatore di join anziché i riferimenti esterni. L'ottimizzazione dello spool per una non applicazione è in genere uno spool della tabella lazy, poiché non esiste predicato sul lato interno, ma solo sul join.

L'ottimizzatore non considera la costruzione di un indice al volo per consentire un'applicazione; piuttosto la sequenza di eventi è generalmente il contrario: trasformarsi in applicare perché esiste un buon indice.

A volte è possibile incoraggiare un'applicazione o un join utilizzando la APPLYsintassi nella query. Il flag di traccia non documentato 9114 può aiutare in questo dissuadendo l'ottimizzatore dalla traduzione di un'applicazione logica in un join frontale. Per esempio:

SELECT * 
FROM dbo.X_1000 AS a
CROSS APPLY (SELECT * FROM dbo.X_1000 AS b WHERE b.ID = a.ID) AS b
OPTION (QUERYTRACEON 9114);

Piano di spool

Uno spool di indice è preferito per applicare poiché il riferimento esterno indica che la selezione viene applicata sul lato interno del join. Lo vedrai spesso tramite SelToIndexOnTheFlyma esistono altri percorsi. Vedi il mio articolo The Eager Index Spool e The Optimizer .

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.