Come posso sbarazzarmi di un ramo parallelo inutile quando si elimina una singola riga?


9

Considera la seguente query che non fa il pivot di alcune manciate di aggregati scalari:

SELECT A, B
FROM (
    SELECT 
      MAX(CASE WHEN ID = 1 THEN 1 ELSE 0 END) VAL1
    , MAX(CASE WHEN ID = 2 THEN 1 ELSE 0 END) VAL2
    , MAX(CASE WHEN ID = 3 THEN 1 ELSE 0 END) VAL3
    , MAX(CASE WHEN ID = 4 THEN 1 ELSE 0 END) VAL4
    , MAX(CASE WHEN ID = 5 THEN 1 ELSE 0 END) VAL5
    , MAX(CASE WHEN ID = 6 THEN 1 ELSE 0 END) VAL6
    , MAX(CASE WHEN ID = 7 THEN 1 ELSE 0 END) VAL7
    , MAX(CASE WHEN ID = 16 THEN 1 ELSE 0 END) VAL16
    FROM dbo.PARALLEL_ZONE_REPRO
) q
UNPIVOT(B FOR A IN (
    VAL1
    ,VAL2
    ,VAL3
    ,VAL4
    ,VAL5
    ,VAL6
    ,VAL7
    ,VAL16
)) U
OPTION (MAXDOP 4);

Su SQL Server 2017, ottengo un piano con due rami paralleli. Il ramo parallelo sinistro mi sembra fuori posto. L'ottimizzatore ha la garanzia che ci sarà un solo output di riga dall'aggregato scalare globale, ma l'operatore principale di esso è uno Stream di distribuzione con partizionamento round robin:

pettirosso

Quando eseguo la query, tutte le righe passano a un singolo thread come previsto. Non ci sono problemi di prestazioni con questa query, ma la query riserva 8 thread paralleli con MAXDOP impostato su 4. Ancora una volta, ritengo che sia fuori posto. È impossibile eseguire entrambi i rami paralleli contemporaneamente. Voglio evitare la prenotazione non necessaria del thread di lavoro perché ho abilitato TF 2467 che modifica l'algoritmo di pianificazione per esaminare il numero di thread di lavoro per programmatore.

È possibile riscrivere la query in modo che abbia esattamente un ramo parallelo che contiene la scansione della tabella e l'aggregazione locale? Ad esempio, starei bene con la forma generale di seguito, tranne per il fatto che voglio eseguire il ciclo nidificato in una zona seriale:

inserisci qui la descrizione dell'immagine

Per Application Reason ™ preferisco fortemente evitare di suddividere questa query in parti. Se lo si desidera, è possibile visualizzare il piano di query effettivo qui . Se desideri giocare a casa, ecco T-SQL per creare la tabella utilizzata nella query:

DROP TABLE IF EXISTS dbo.PARALLEL_ZONE_REPRO;

CREATE TABLE dbo.PARALLEL_ZONE_REPRO (
    ID BIGINT,
    FILLER VARCHAR(100)
);

INSERT INTO dbo.PARALLEL_ZONE_REPRO WITH (TABLOCK)
SELECT
  ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) % 15
, REPLICATE('Z', 100)
FROM master..spt_values t1
CROSS JOIN master..spt_values t2;

Risposte:


8

Sono in grado di ottenere la forma del piano desiderata con un join ad anello seriale quando sono vere tutte le seguenti condizioni:

  • Viene utilizzato un APPLYo CROSS JOINanzichéUNPIVOT
  • Non APPLYcontiene riferimenti esterni
  • L'origine delle righe in APPLYè un costruttore di valori di tabella anziché una tabella

Ad esempio, ecco un modo per farlo:

SELECT A, B
FROM 
(
    SELECT A
    , MAX(
        CASE
            WHEN A = 'VAL1' THEN VAL1 
            WHEN A = 'VAL2' THEN VAL2
            WHEN A = 'VAL3' THEN VAL3
            WHEN A = 'VAL4' THEN VAL4
            WHEN A = 'VAL5' THEN VAL5
            WHEN A = 'VAL6' THEN VAL6
            WHEN A = 'VAL7' THEN VAL7
            WHEN A = 'VAL16' THEN VAL16
            ELSE NULL
        END
    ) B
    FROM (
         SELECT 
           MAX(CASE WHEN ID = 1 THEN 1 ELSE 0 END) VAL1
         , MAX(CASE WHEN ID = 2 THEN 1 ELSE 0 END) VAL2
         , MAX(CASE WHEN ID = 3 THEN 1 ELSE 0 END) VAL3
         , MAX(CASE WHEN ID = 4 THEN 1 ELSE 0 END) VAL4
         , MAX(CASE WHEN ID = 5 THEN 1 ELSE 0 END) VAL5
         , MAX(CASE WHEN ID = 6 THEN 1 ELSE 0 END) VAL6
         , MAX(CASE WHEN ID = 7 THEN 1 ELSE 0 END) VAL7
         , MAX(CASE WHEN ID = 16 THEN 1 ELSE 0 END) VAL16
         FROM dbo.PARALLEL_ZONE_REPRO
    ) q
    CROSS APPLY (
        VALUES ('VAL1'), ('VAL2'), ('VAL3'), ('VAL4'),
        ('VAL5'), ('VAL6'), ('VAL7'), ('VAL16') 
    ) ca (A)
    GROUP BY A
) q
WHERE q.B IS NOT NULL
OPTION (MAXDOP 4);

Ottengo la forma del piano del piano desiderata come richiesto con un solo ramo parallelo:

inserisci qui la descrizione dell'immagine

Ho provato molte altre cose che non hanno funzionato. Questa risposta non è soddisfacente in quanto non so perché funzioni e potrebbe non funzionare in una versione futura di SQL Server, ma ha risolto il mio problema.


8

È impossibile eseguire entrambi i rami paralleli contemporaneamente.

L'esecuzione inizia sul bordo sinistro del piano. Il ramo di cicli nidificati è in esecuzione (apertura, in attesa di dati) quando è in esecuzione il ramo di scansione della tabella. Questo è inevitabile . Entrambe le filiali sono attive contemporaneamente, pertanto SQL Server riserverà ai lavoratori DOP 2 * per questo piano.

Per una soluzione affidabile, è possibile posizionare il perno in una funzione con valori di tabella:

CREATE OR ALTER FUNCTION dbo.PivotPZR()
RETURNS @R table 
(
    VAL1 bigint NOT NULL, VAL2 bigint NOT NULL,
    VAL3 bigint NOT NULL, VAL4 bigint NOT NULL,
    VAL5 bigint NOT NULL, VAL6 bigint NOT NULL,
    VAL7 bigint NOT NULL, VAL16 bigint NOT NULL
)
WITH SCHEMABINDING AS
BEGIN
    DECLARE 
        @Val1 bigint, @Val2 bigint, @Val3 bigint, @Val4 bigint,
        @Val5 bigint, @Val6 bigint, @Val7 bigint, @Val16 bigint;

    -- Can use parallelism
    SELECT
        @Val1 = MAX(CASE WHEN PZR.ID = 1 THEN 1 ELSE 0 END),
        @Val2 = MAX(CASE WHEN PZR.ID = 2 THEN 1 ELSE 0 END),
        @Val3 = MAX(CASE WHEN PZR.ID = 3 THEN 1 ELSE 0 END),
        @Val4 = MAX(CASE WHEN PZR.ID = 4 THEN 1 ELSE 0 END),
        @Val5 = MAX(CASE WHEN PZR.ID = 5 THEN 1 ELSE 0 END),
        @Val6 = MAX(CASE WHEN PZR.ID = 6 THEN 1 ELSE 0 END),
        @Val7 = MAX(CASE WHEN PZR.ID = 7 THEN 1 ELSE 0 END),
        @Val16 = MAX(CASE WHEN PZR.ID = 16 THEN 1 ELSE 0 END)
    FROM dbo.PARALLEL_ZONE_REPRO AS PZR;

    -- Single result row
    INSERT @R
        (VAL1, VAL2, VAL3, VAL4, VAL5, VAL6, VAL7, VAL16)
    VALUES
        (@Val1, @Val2, @Val3, @Val4, @Val5, @Val6, @Val7, @Val16);

    RETURN;
END;

Quindi riscrivere la query come:

SELECT
    U.A,
    U.B
FROM dbo.PivotPZR() AS PP
UNPIVOT
(
    B FOR A IN (VAL1, VAL2 ,VAL3 ,VAL4, VAL5 ,VAL6 ,VAL7 ,VAL16)
) AS U;

La funzione utilizza il parallelismo con un singolo ramo come desiderato:

Piano funzionale

Il piano di esecuzione di livello superiore è:

Query di primo livello

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.