Spooling a scansione costante


14

Ho un tavolo con poche dozzine di righe. Segue una configurazione semplificata

CREATE TABLE #data ([Id] int, [Status] int);

INSERT INTO #data
VALUES (100, 1), (101, 2), (102, 3), (103, 2);

E ho una query che unisce questa tabella a un set di righe costruite con valori di tabella (fatte di variabili e costanti), come

DECLARE @id1 int = 101, @id2 int = 105;

SELECT
    COALESCE(p.[Code], 'X') AS [Code],
    COALESCE(d.[Status], 0) AS [Status]
FROM (VALUES
        (@id1, 'A'),
        (@id2, 'B')
    ) p([Id], [Code])
    FULL JOIN #data d ON d.[Id] = p.[Id];

Il piano di esecuzione della query sta dimostrando che la decisione dell'ottimizzatore è quella di utilizzare la FULL LOOP JOINstrategia, il che sembra appropriato, poiché entrambi gli input hanno pochissime righe. Una cosa che ho notato (e non posso essere d'accordo), tuttavia, è che le righe TVC sono in fase di spool (vedere l'area del piano di esecuzione nella casella rossa).

Spooling a scansione costante

Perché Optimizer introduce la spool qui, qual è il motivo per farlo? Non c'è nulla di complesso oltre la bobina. Sembra che non sia necessario. Come sbarazzarsene in questo caso, quali sono i modi possibili?


Il piano di cui sopra è stato ottenuto il

Microsoft SQL Server 2014 (SP2-CU11) (KB4077063) - 12.0.5579.0 (X64)


Suggerimento correlato su feedback.azure.com
i-one

Risposte:


19

Perché Optimizer introduce la spool qui, qual è il motivo per farlo? Non c'è nulla di complesso oltre la bobina.

La cosa oltre lo spool non è un semplice riferimento alla tabella, che potrebbe semplicemente essere duplicato quando viene generata l' alternativa sinistra / anti semi join .

Può sembrare un po 'una tabella (Scansione costante) ma per l'ottimizzatore * è una UNION ALLdelle righe separate nella VALUESclausola.

La complessità aggiuntiva è sufficiente affinché l'ottimizzatore scelga di eseguire lo spooling e la riproduzione delle righe di origine e di non sostituire lo spool con un semplice "get tabella" in seguito. Ad esempio, la trasformazione iniziale dal join completo è simile alla seguente:

piano iniziale

Notare le bobine extra introdotte dalla trasformazione generale. Gli spool sopra una semplice tabella vengono ripuliti in seguito dalla regola SpoolGetToGet.

Se l'ottimizzatore aveva una SpoolConstGetToConstGetregola corrispondente , in linea di principio potrebbe funzionare come desideri.

Come sbarazzarsene in questo caso, quali sono i modi possibili?

Utilizzare una tabella reale (temporanea o variabile) o scrivere manualmente la trasformazione dal join completo, ad esempio:

WITH 
    p([Id], [Code]) AS
    (
        SELECT @id1, 'A'
        UNION ALL
        SELECT @id2, 'B'
    ),
    FullJoin AS
    (
        SELECT
            p.Code,
            d.[Status]
        FROM p
        LEFT JOIN #data d 
            ON d.[Id] = p.[Id]
        UNION ALL
        SELECT
            NULL,
            D.[Status]
        FROM #data AS D
        WHERE NOT EXISTS
        (
            SELECT *
            FROM p
            WHERE p.Id = D.Id
        )
    )
SELECT
    COALESCE(FullJoin.Code, 'X') AS Code,
    COALESCE(FullJoin.Status, 0) AS [Status]
FROM FullJoin;

Pianificare la riscrittura manuale:

Piano di riscrittura manuale

Questo ha un costo stimato di 0,0067201 unità, rispetto a 0,0203412 unità per l'originale.


* Può essere osservato come a LogOp_UnionAllnell'albero convertito (TF 8605). Nell'albero di input (TF 8606) è a LogOp_ConstTableGet. L' albero convertito mostra l'albero degli elementi di espressione dell'ottimizzatore dopo l'analisi, la normalizzazione, l'algebrizzazione, l'associazione e alcuni altri lavori preparatori. L' albero di input mostra gli elementi dopo la conversione in Negation Normal Form (conversione NNF), il collasso della costante di runtime e alcuni altri bit e bob. La conversione NNF include la logica per comprimere le unioni logiche e la tabella comune ottiene, tra le altre cose.


3

Lo spool della tabella sta semplicemente creando una tabella dalle due serie di tuple presenti nella VALUESclausola.

Puoi eliminare lo spool inserendo prima quei valori in una tabella temporanea, in questo modo:

DROP TABLE IF EXISTS #data;
CREATE TABLE #data ([Id] int, [Status] int);

INSERT INTO #data
VALUES (100, 1), (101, 2), (102, 3), (103, 2);

DROP TABLE IF EXISTS #p;
CREATE TABLE #p
(
    Id int NOT NULL
    , Code char(1) NOT NULL
);

DECLARE @id1 int = 101, @id2 int = 105;

INSERT INTO #p (Id, Code)
VALUES
        (@id1, 'A'),
        (@id2, 'B');


SELECT
    COALESCE(p.[Code], 'X') AS [Code],
    COALESCE(d.[Status], 0) AS [Status]
FROM #p p
    FULL JOIN #data d ON d.[Id] = p.[Id];

Guardando il piano di esecuzione per la tua query, vediamo che l'elenco di output contiene due colonne che usano il Unionprefisso; questo è un suggerimento che lo spool sta creando una tabella da una fonte sindacata:

inserisci qui la descrizione dell'immagine

Il FULL OUTER JOINrichiede SQL Server per accedere ai valori in pdue volte, una per ogni "parte" del join. La creazione di uno spool consente ai loop interni risultanti di unirsi per accedere ai dati di spool.

È interessante notare che, se si sostituiscono FULL OUTER JOINcon a LEFT JOINe a RIGHT JOINe UNIONi risultati insieme, SQL Server non utilizza uno spool.

SELECT
    COALESCE(p.[Code], 'X') AS [Code],
    COALESCE(d.[Status], 0) AS [Status]
FROM (VALUES
        (101, 'A'),
        (105, 'B')
    ) p([Id], [Code])
    LEFT JOIN #data d ON d.[Id] = p.[Id]
UNION
SELECT
    COALESCE(p.[Code], 'X') AS [Code],
    COALESCE(d.[Status], 0) AS [Status]
FROM (VALUES
        (101, 'A'),
        (105, 'B')
    ) p([Id], [Code])
    RIGHT JOIN #data d ON d.[Id] = p.[Id];

inserisci qui la descrizione dell'immagine

Nota, non sto suggerendo di utilizzare la UNIONquery sopra; per gruppi di input più grandi, potrebbe non essere più efficiente del semplice che FULL OUTER JOINhai già.


Nel tuo vero carico di lavoro, la bobina è davvero così costosa?
Max Vernon,
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.