SQL Server memorizza nella cache il risultato di una funzione con valori di tabella con più istruzioni?


Risposte:


23

I risultati di una funzione con valori di tabella a più istruzioni (msTVF) non vengono mai memorizzati nella cache o riutilizzati tra istruzioni (o connessioni), ma ci sono un paio di modi in cui un risultato msTVF può essere riutilizzato all'interno della stessa istruzione. In tal senso, un msTVF non viene necessariamente ripopolato ogni volta che viene chiamato.

Esempio msTVF

Questo msTVF (volutamente inefficiente) restituisce un intervallo specificato di numeri interi, con un timestamp su ogni riga:

IF OBJECT_ID(N'dbo.IntegerRange', 'TF') IS NOT NULL
    DROP FUNCTION dbo.IntegerRange;
GO
CREATE FUNCTION dbo.IntegerRange (@From integer, @To integer)
RETURNS @T table 
(
    n integer PRIMARY KEY, 
    ts datetime DEFAULT CURRENT_TIMESTAMP
)
WITH SCHEMABINDING
AS
BEGIN
    WHILE @From <= @To
    BEGIN
        INSERT @T (n)
        VALUES (@From);

        SET @From = @From + 1;
    END;
    RETURN;
END;

Variabile di tabella statica

Se tutti i parametri della chiamata di funzione sono costanti (o costanti di runtime), il piano di esecuzione popolerà il risultato della variabile della tabella una volta. Il resto del piano può accedere alla variabile tabella più volte. La natura statica della variabile tabella può essere riconosciuta dal piano di esecuzione. Per esempio:

SELECT
    IR.n,
    IR.ts 
FROM dbo.IntegerRange(1, 5) AS IR
ORDER BY
    IR.n;

Restituisce un risultato simile a:

Risultato semplice

Il piano di esecuzione è:

Piano di esecuzione semplice

L'operatore Sequence prima chiama l'operatore Function Valued Table, che popola la variabile table (nota che questo operatore non restituisce righe). Successivamente, la sequenza chiama il suo secondo input, che restituisce il contenuto della variabile di tabella (utilizzando una scansione dell'indice cluster in questo caso).

L'omaggio che il piano utilizza un risultato di variabile di tabella "statica" è l'operatore Funzione con valori di tabella al di sotto di una sequenza: la variabile di tabella deve essere completamente popolata una volta prima che il resto del piano possa iniziare.

Accessi multipli

Per mostrare il risultato della variabile della tabella a cui si accede più di una volta, useremo una seconda tabella con le righe numerate da 1 a 5:

IF OBJECT_ID(N'dbo.T', 'U') IS NOT NULL
    DROP TABLE dbo.T;

CREATE TABLE dbo.T (i integer NOT NULL);

INSERT dbo.T (i) 
VALUES (1), (2), (3), (4), (5);

E una nuova query che unisce questa tabella alla nostra funzione (potrebbe anche essere scritta come APPLY):

SELECT T.i,
       IR.n,
       IR.ts
FROM dbo.T AS T
JOIN dbo.IntegerRange(1, 5) AS IR
    ON IR.n = T.i;

Il risultato è:

Unisci il risultato

Il piano di esecuzione:

Unisciti al piano

Come prima, la sequenza popola per prima il risultato della variabile di tabella msTVF. Successivamente, i cicli nidificati vengono utilizzati per unire ciascuna riga dalla tabella Ta una riga dal risultato msTVF. Poiché la definizione della funzione includeva un indice utile sulla variabile della tabella, è possibile utilizzare una ricerca indice.

Il punto chiave è che quando i parametri di msTVF sono costanti (comprese variabili e parametri) o trattati come costanti di runtime per l'istruzione dal motore di esecuzione, il piano presenterà due operatori separati per il risultato della variabile della tabella msTVF: uno per popolare il tavolo; un altro per accedere ai risultati, eventualmente accedendo alla tabella più volte e eventualmente utilizzando gli indici dichiarati nella definizione della funzione.

Parametri correlati e parametri non costanti

Per evidenziare le differenze quando vengono utilizzati parametri correlati (riferimenti esterni) o parametri di funzione non costanti, cambieremo il contenuto della tabella in Tmodo che la funzione abbia molto più lavoro da fare:

TRUNCATE TABLE dbo.T;

INSERT dbo.T (i) 
VALUES (50001), (50002), (50003), (50004), (50005);

La seguente query modificata ora utilizza un riferimento esterno alla tabella Tin uno dei parametri della funzione:

SELECT T.i,
       IR.n,
       IR.ts
FROM dbo.T AS T
CROSS APPLY dbo.IntegerRange(1, T.i) AS IR
WHERE IR.n = T.i;

Questa query richiede circa 8 secondi per restituire risultati come:

Risultato correlato

Notare la differenza di tempo tra le righe nella colonna ts. La WHEREclausola limita il risultato finale per un output di dimensioni ragionevoli, ma la funzione inefficiente impiega ancora un po 'di tempo per popolare la variabile della tabella con 50.000 righe dispari (a seconda del valore correlato di idalla tabella T).

Il piano di esecuzione è:

Piano di esecuzione correlato

Notare la mancanza di un operatore Sequence. Ora, esiste un singolo operatore Funzione con valori di tabella che popola la variabile di tabella e restituisce le sue righe su ogni iterazione dei loop di loop nidificati.

Per essere chiari: con solo 5 righe nella tabella T, l'operatore Table Valued Function viene eseguito 5 volte. Genera 50.001 righe sulla prima iterazione, 50.002 sulla seconda ... e così via. La variabile della tabella viene "eliminata" (troncata) tra le iterazioni, quindi ciascuna delle cinque chiamate è una popolazione completa. Questo è il motivo per cui è così lento e ogni riga impiega circa lo stesso tempo per apparire nel risultato.

Note a margine:

Naturalmente, lo scenario sopra è deliberatamente ideato per mostrare quanto scarse possano essere le prestazioni quando msTVF popola molte righe su ogni iterazione.

Un sensibile attuazione del codice precedente fisserebbe entrambi i parametri msTVF a i, e rimuovere la ridondanza WHEREclausola. La variabile della tabella verrebbe comunque troncata e ripopolata su ogni iterazione, ma solo con una riga ogni volta.

Potremmo anche recuperare i ivalori minimo e massimo da Te memorizzarli in variabili in un passaggio precedente. Chiamare la funzione con variabili anziché con parametri correlati consentirebbe di utilizzare il modello di variabili di tabella "statica" come notato in precedenza.

Memorizzazione nella cache per parametri correlati invariati

Tornando a rispondere nuovamente alla domanda originale, in cui non è possibile utilizzare il modello statico Sequence, SQL Server può evitare di troncare e ripopolare la variabile della tabella msTVF se nessuno dei parametri correlati è cambiato dalla precedente iterazione di un join di loop nidificato.

Per dimostrarlo, sostituiremo il contenuto Tcon cinque valori identici i :

TRUNCATE TABLE dbo.T;

INSERT dbo.T (i) 
VALUES (50005), (50005), (50005), (50005), (50005);

La query con un parametro correlato di nuovo:

SELECT T.i,
       IR.n,
       IR.ts
FROM dbo.T AS T
CROSS APPLY dbo.IntegerRange(1, T.i) AS IR
WHERE IR.n = T.i;

Questa volta i risultati compaiono in circa 1,5 secondi :

Risultati di riga identici

Nota i timestamp identici su ogni riga. Il risultato memorizzato nella cache della tabella viene riutilizzato per successive iterazioni in cui il valore correlato irimane invariato. Riutilizzare il risultato è molto più veloce che inserire 50.005 righe ogni volta.

Il piano di esecuzione è molto simile a prima:

Pianificare file identiche

La differenza chiave sta nelle proprietà Riavvolgi reali e Riavvolgi effettivi dell'operatore Funzione con valori di tabella:

Proprietà dell'operatore

Quando i parametri correlati non cambiano, SQL Server può riprodurre (riavvolgere) i risultati correnti nella variabile tabella. Quando la correlazione cambia, SQL Server deve troncare e ripopolare la variabile di tabella (rebind). L'unico rebind avviene alla prima iterazione; le quattro iterazioni successive sono tutte riavvolgenti poiché il valore di T.iè invariato.

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.