Sqlserver
 sql >> Database >  >> RDS >> Sqlserver

Quali sono le tabelle più performanti, CTE o temporanee?

Dipende.

Innanzitutto

Che cos'è un'espressione di tabella comune?

Un CTE (non ricorsivo) viene trattato in modo molto simile ad altri costrutti che possono essere usati anche come espressioni di tabella inline in SQL Server. Tabelle derivate, viste e funzioni con valori di tabella inline. Si noti che mentre BOL afferma che un CTE "può essere considerato come un insieme di risultati temporaneo", questa è una descrizione puramente logica. Il più delle volte non è materializzato a sé stante.

Cos'è una tabella temporanea?

Questa è una raccolta di righe archiviate nelle pagine di dati in tempdb. Le pagine dati possono risiedere parzialmente o interamente in memoria. Inoltre, la tabella temporanea può essere indicizzata e avere statistiche di colonna.

Dati di prova

CREATE TABLE T(A INT IDENTITY PRIMARY KEY, B INT , F CHAR(8000) NULL);

INSERT INTO T(B)
SELECT TOP (1000000)  0 + CAST(NEWID() AS BINARY(4))
FROM master..spt_values v1,
     master..spt_values v2;

Esempio 1

WITH CTE1 AS
(
SELECT A,
       ABS(B) AS Abs_B,
       F
FROM T
)
SELECT *
FROM CTE1
WHERE A = 780

Avviso nel piano di cui sopra non vi è alcuna menzione di CTE1. Si limita ad accedere direttamente alle tabelle di base e viene trattato come

SELECT A,
       ABS(B) AS Abs_B,
       F
FROM   T
WHERE  A = 780 

Riscrivere materializzando il CTE in una tabella temporanea intermedia qui sarebbe enormemente controproducente.

Materializzando la definizione di CTE di

SELECT A,
       ABS(B) AS Abs_B,
       F
FROM T

Implicherebbe la copia di circa 8 GB di dati in una tabella temporanea, quindi c'è ancora il sovraccarico della selezione da essa.

Esempio 2

WITH CTE2
     AS (SELECT *,
                ROW_NUMBER() OVER (ORDER BY A) AS RN
         FROM   T
         WHERE  B % 100000 = 0)
SELECT *
FROM   CTE2 T1
       CROSS APPLY (SELECT TOP (1) *
                    FROM   CTE2 T2
                    WHERE  T2.A > T1.A
                    ORDER  BY T2.A) CA 

L'esempio sopra richiede circa 4 minuti sulla mia macchina.

Solo 15 righe dei 1.000.000 di valori generati casualmente corrispondono al predicato, ma la costosa scansione della tabella avviene 16 volte per individuarli.

Questo sarebbe un buon candidato per materializzare il risultato intermedio. La riscrittura della tabella temporanea equivalente ha richiesto 25 secondi.

INSERT INTO #T
SELECT *,
       ROW_NUMBER() OVER (ORDER BY A) AS RN
FROM   T
WHERE  B % 100000 = 0

SELECT *
FROM   #T T1
       CROSS APPLY (SELECT TOP (1) *
                    FROM   #T T2
                    WHERE  T2.A > T1.A
                    ORDER  BY T2.A) CA 

La materializzazione intermedia di parte di una query in una tabella temporanea può talvolta essere utile anche se viene valutata solo una volta, quando consente di ricompilare il resto della query sfruttando le statistiche sul risultato materializzato. Un esempio di questo approccio è nell'articolo di SQL Cat Quando abbattere le query complesse.

In alcune circostanze SQL Server utilizzerà uno spool per memorizzare nella cache un risultato intermedio, ad es. di un CTE ed evitare di dover rivalutare quel sottoalbero. Questo è discusso nell'elemento Connect (migrato) Fornire un suggerimento per forzare la materializzazione intermedia di CTE o tabelle derivate. Tuttavia non vengono create statistiche su questo e anche se il numero di righe di spool doveva essere enormemente diverso da quello stimato non è possibile che il piano di esecuzione in corso si adatti dinamicamente in risposta (almeno nelle versioni correnti. I piani di query adattivi potrebbero diventare possibili in il futuro).