Considera la seguente query che inserisce le righe da una tabella di origine solo se non sono già nella tabella di destinazione:
INSERT INTO dbo.HALLOWEEN_IS_COMING_EARLY_THIS_YEAR WITH (TABLOCK)
SELECT maybe_new_rows.ID
FROM dbo.A_HEAP_OF_MOSTLY_NEW_ROWS maybe_new_rows
WHERE NOT EXISTS (
SELECT 1
FROM dbo.HALLOWEEN_IS_COMING_EARLY_THIS_YEAR halloween
WHERE maybe_new_rows.ID = halloween.ID
)
OPTION (MAXDOP 1, QUERYTRACEON 7470);
Una possibile forma del piano include un join unione e una bobina desiderosa. L'operatore desideroso di spool è presente per risolvere il problema di Halloween :
Sulla mia macchina, il codice sopra riportato viene eseguito in circa 6900 ms. Il codice di riproduzione per creare le tabelle è incluso nella parte inferiore della domanda. Se non sono soddisfatto delle prestazioni, potrei provare a caricare le righe da inserire in una tabella temporanea invece di fare affidamento sul rocchetto desideroso. Ecco una possibile implementazione:
DROP TABLE IF EXISTS #CONSULTANT_RECOMMENDED_TEMP_TABLE;
CREATE TABLE #CONSULTANT_RECOMMENDED_TEMP_TABLE (
ID BIGINT,
PRIMARY KEY (ID)
);
INSERT INTO #CONSULTANT_RECOMMENDED_TEMP_TABLE WITH (TABLOCK)
SELECT maybe_new_rows.ID
FROM dbo.A_HEAP_OF_MOSTLY_NEW_ROWS maybe_new_rows
WHERE NOT EXISTS (
SELECT 1
FROM dbo.HALLOWEEN_IS_COMING_EARLY_THIS_YEAR halloween
WHERE maybe_new_rows.ID = halloween.ID
)
OPTION (MAXDOP 1, QUERYTRACEON 7470);
INSERT INTO dbo.HALLOWEEN_IS_COMING_EARLY_THIS_YEAR WITH (TABLOCK)
SELECT new_rows.ID
FROM #CONSULTANT_RECOMMENDED_TEMP_TABLE new_rows
OPTION (MAXDOP 1);
Il nuovo codice viene eseguito in circa 4400 ms. Sono in grado di ottenere piani reali e utilizzare Actual Time Statistics ™ per esaminare dove viene trascorso il tempo a livello di operatore. Si noti che la richiesta di un piano effettivo aggiunge un notevole sovraccarico per queste query in modo che i totali non corrispondano ai risultati precedenti.
╔═════════════╦═════════════╦══════════════╗
║ operator ║ first query ║ second query ║
╠═════════════╬═════════════╬══════════════╣
║ big scan ║ 1771 ║ 1744 ║
║ little scan ║ 163 ║ 166 ║
║ sort ║ 531 ║ 530 ║
║ merge join ║ 709 ║ 669 ║
║ spool ║ 3202 ║ N/A ║
║ temp insert ║ N/A ║ 422 ║
║ temp scan ║ N/A ║ 187 ║
║ insert ║ 3122 ║ 1545 ║
╚═════════════╩═════════════╩══════════════╝
Il piano di query con lo spooler desideroso sembra impiegare molto più tempo sugli operatori di inserimento e spool rispetto al piano che utilizza la tabella temporanea.
Perché il piano con la tabella temporanea è più efficiente? Una bobina desiderosa non è comunque solo una tabella temporanea interna? Credo di cercare risposte incentrate sugli interni. Sono in grado di vedere come le pile di chiamate sono diverse ma non riesco a capire il quadro generale.
Sono su SQL Server 2017 CU 11 nel caso qualcuno volesse saperlo. Ecco il codice per popolare le tabelle utilizzate nelle query precedenti:
DROP TABLE IF EXISTS dbo.HALLOWEEN_IS_COMING_EARLY_THIS_YEAR;
CREATE TABLE dbo.HALLOWEEN_IS_COMING_EARLY_THIS_YEAR (
ID BIGINT NOT NULL,
PRIMARY KEY (ID)
);
INSERT INTO dbo.HALLOWEEN_IS_COMING_EARLY_THIS_YEAR WITH (TABLOCK)
SELECT TOP (20000000) ROW_NUMBER() OVER (ORDER BY (SELECT NULL))
FROM master..spt_values t1
CROSS JOIN master..spt_values t2
CROSS JOIN master..spt_values t3
OPTION (MAXDOP 1);
DROP TABLE IF EXISTS dbo.A_HEAP_OF_MOSTLY_NEW_ROWS;
CREATE TABLE dbo.A_HEAP_OF_MOSTLY_NEW_ROWS (
ID BIGINT NOT NULL
);
INSERT INTO dbo.A_HEAP_OF_MOSTLY_NEW_ROWS WITH (TABLOCK)
SELECT TOP (1900000) 19999999 + ROW_NUMBER() OVER (ORDER BY (SELECT NULL))
FROM master..spt_values t1
CROSS JOIN master..spt_values t2;