Questo è un po 'ampio, ma penso di capire la vera domanda e risponderò di conseguenza. Sto solo per parlare del rocchetto tabella vs indice però. Non credo sia corretto vedere lì come una scelta tra spool di tabella e di indice. Come sapete, è possibile in una singola sottostruttura ottenere uno spool di indice, uno spool della tabella o entrambi uno spool dell'indice e uno spool della tabella. Credo che sia generalmente corretto affermare che si ottiene uno spool di indice nelle seguenti condizioni:
- Query Optimizer ha un motivo per trasformare un join in un applicare
- Query Optimizer esegue effettivamente la trasformazione per applicare
- Query Optimizer utilizza la regola per aggiungere uno spool di indice (almeno lo spool di indice deve essere sicuro da usare)
- Il piano con lo spool di indice è selezionato
Puoi vedere la maggior parte di questi con semplici demo. Inizia creando un paio di heap:
DROP TABLE IF EXISTS dbo.X_10000_VARCHAR_901;
CREATE TABLE dbo.X_10000_VARCHAR_901 (ID VARCHAR(901) NOT NULL);
INSERT INTO dbo.X_10000_VARCHAR_901 WITH (TABLOCK)
SELECT TOP (10000) ROW_NUMBER() OVER (ORDER BY (SELECT NULL))
FROM master..spt_values t1
CROSS JOIN master..spt_values t2;
DROP TABLE IF EXISTS dbo.X_10000_VARCHAR_800;
CREATE TABLE dbo.X_10000_VARCHAR_800 (ID VARCHAR(800) NOT NULL);
INSERT INTO dbo.X_10000_VARCHAR_800 WITH (TABLOCK)
SELECT TOP (10000) ROW_NUMBER() OVER (ORDER BY (SELECT NULL))
FROM master..spt_values t1
CROSS JOIN master..spt_values t2;
Per la prima query, non c'è nulla su cui cercare:
SELECT *
FROM dbo.X_10000_VARCHAR_901 a
CROSS JOIN dbo.X_10000_VARCHAR_901 b
OPTION (MAXDOP 1);
Quindi non c'è motivo per cui l'ottimizzatore trasformi il join in un'applicazione. Si finisce con un rocchetto di tabella per motivi di costo. Quindi questa query fallisce il primo test.
Per la query successiva, è lecito aspettarsi che l'ottimizzatore abbia un motivo per considerare un'applicazione:
SELECT *
FROM dbo.X_10000_VARCHAR_901 a
INNER JOIN dbo.X_10000_VARCHAR_901 b ON a.ID = b.ID
OPTION (LOOP JOIN, MAXDOP 1);
Ma non è pensato per essere:
Questa query non supera il secondo test. Una spiegazione completa è qui . Citando la parte più rilevante:
L'ottimizzatore non considera la costruzione di un indice al volo per consentire un'applicazione; piuttosto la sequenza di eventi è di solito il contrario: trasformarsi in applicare perché esiste un buon indice.
Posso riscrivere la query per incoraggiare l'ottimizzatore a considerare un'applicazione:
SELECT *
FROM dbo.X_10000_VARCHAR_901 a
INNER JOIN dbo.X_10000_VARCHAR_901 b ON a.ID >= b.ID AND a.ID <= b.ID
OPTION (MAXDOP 1);
Ma non c'è ancora spool di indice:
Questa query non supera il terzo test. In SQL Server 2014 c'era un limite di lunghezza della chiave di indice di 900 byte. Questo è stato esteso in SQL Server 2016 ma solo per indici non cluster. L'indice per uno spool è un indice cluster quindi il limite rimane a 900 byte . In ogni caso, la regola di spooling dell'indice non può essere applicata perché potrebbe causare un errore durante l'esecuzione della query.
Ridurre la lunghezza del tipo di dati a 800 fornisce infine un piano con uno spool di indice:
Il piano di spooling dell'indice, non a caso, è molto più economico di un piano senza spool: 89.7603 unità contro 598.832 unità. Puoi vedere la differenza con il QUERYRULEOFF BuildSpool
suggerimento per la query non documentata :
Questa non è una risposta completa, ma si spera sia qualcosa di quello che stavi cercando.