Como posso me livrar de uma ramificação paralela inútil ao remover uma única linha?

9

Considere a seguinte consulta que desassocia alguns punhados de agregados escalares:

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);

No SQL Server 2017, recebo um plano com duas ramificações paralelas. O ramo paralelo esquerdo parece deslocado para mim. O otimizador tem garantia de que haverá apenas uma saída de linha do agregado escalar global, mas o operador pai dele é um Distribute Streams com particionamento round robin:

pisco de peito vermelho redondo

Quando executo a consulta, todas as linhas vão para um único thread conforme o esperado. Não há problema de desempenho com essa consulta, mas a consulta reserva 8 threads paralelos com MAXDOP definido como 4. Novamente, acho que isso está fora do lugar. É impossível que ambas as ramificações paralelas sejam executadas ao mesmo tempo. Desejo evitar reservas desnecessárias de threads de trabalho porque tenho o TF 2467 ativado, o que altera o algoritmo de agendamento para analisar o número de threads de trabalho por agendador.

É possível reescrever a consulta para ter exatamente uma ramificação paralela que contém a verificação da tabela e o agregado local? Por exemplo, eu ficaria bem com a forma geral abaixo, exceto que eu quero que o loop aninhado seja executado em uma zona serial:

insira a descrição da imagem aqui

Por motivos de aplicação ™, prefiro evitar dividir esta consulta em partes. Se desejar, você pode visualizar o plano de consulta real aqui . Se você quiser jogar em casa, aqui está o T-SQL para criar a tabela usada na consulta:

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;
Joe Obbish
fonte

Respostas:

8

Consigo obter a forma de plano desejada com uma junção de loop serial quando todas as seguintes opções forem verdadeiras:

  • Um APPLYou CROSS JOINé usado em vez deUNPIVOT
  • O APPLYnão contém referências externas
  • A origem das linhas no APPLYé um construtor de valor de tabela, em oposição a uma tabela

Por exemplo, aqui está uma maneira de fazer isso:

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);

Recebo a forma desejada do plano de plano, conforme reivindicado com apenas um ramo paralelo:

insira a descrição da imagem aqui

Eu tentei muitas outras coisas que não funcionaram. Essa resposta é insatisfatória, pois não sei por que ela funciona e pode não funcionar em uma versão futura do SQL Server, mas resolveu meu problema.

Joe Obbish
fonte
8

É impossível que ambas as ramificações paralelas sejam executadas ao mesmo tempo.

A execução começa na borda esquerda do plano. O ramo de loops aninhados está em execução (abrindo, aguardando dados) quando o ramo de varredura de tabela está em execução. Isso é inevitável . Ambas as ramificações estão ativas ao mesmo tempo, portanto, o SQL Server reservará 2 * trabalhadores DOP para esse plano.

Para uma solução robusta, você pode colocar o pivô em uma função com valor de tabela:

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;

Em seguida, reescreva a consulta como:

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;

A função usa paralelismo com uma única ramificação, conforme desejado:

Plano de funções

O plano de execução de nível superior é:

Consulta de nível superior

Paul White 9
fonte