少数のスカラー集計をアンピボットする次のクエリを考えてみます。
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);
SQL Server 2017で、2つの並列ブランチを持つプランを取得します。左の平行枝は私には場違いだと感じます。オプティマイザーは、グローバルスカラー集約からの単一の行出力のみが存在することを保証しますが、その親オペレーターはラウンドロビンパーティション化を使用した分散ストリームです。
クエリを実行すると、期待どおりにすべての行が1つのスレッドに移動します。このクエリにパフォーマンスの問題はありませんが、クエリはMAXDOPが4に設定された8つの並列スレッドを予約しています。繰り返しますが、これは場違いだと感じています。両方の並列ブランチを同時に実行することは不可能です。不要なワーカースレッドの予約を回避したいのは、TF 2467を有効にして、スケジューリングアルゴリズムを変更してスケジューラーごとのワーカースレッドの数を調べるためです。
クエリを書き換えて、テーブルスキャンとローカル集計を含む並列ブランチを1つだけにすることはできますか?たとえば、ネストされたループをシリアルゾーンで実行することを除いて、以下の一般的な形状で問題ありません。
Application Reasons™の場合、このクエリを分割して分割しないことを強く推奨します。必要に応じて、実際のクエリプラン ここ を表示できます。自宅で遊んでみたい場合は、クエリで使用するテーブルを作成するT-SQLを次に示します。
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;
次のすべてが当てはまる場合、シリアルループ結合を使用して目的のプラン形状を取得できます。
APPLY
またはCROSS JOIN
はUNPIVOT
の代わりに使用されますAPPLY
には外部参照が含まれていませんAPPLY
の行のソースは、テーブルではなくテーブル値コンストラクターですたとえば、これを行う1つの方法を次に示します。
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);
並列ブランチが1つだけあると主張されているように、目的のプランplan shapeを取得します。
うまくいかなかった他の多くのことを試しました。この回答は、なぜ機能するのかわからず、SQL Serverの将来のバージョンでは機能しない可能性があるため、満足のいくものではありませんが、私の問題は解決しました。
両方の並列ブランチを同時に実行することは不可能です。
実行が開始 計画の左端 。ネストされたループブランチは、テーブルスキャンブランチの実行中に実行されます(データを開いて待機しています)。これは 避けられない です。両方のブランチが同時にアクティブであるため、SQL Serverはreserve2 *このプランのDOPワーカーを予約します。
堅牢なソリューションの場合、ピボットをテーブル値関数に配置できます。
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;
次に、クエリを次のように書き換えます。
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;
この関数は、必要に応じて単一のブランチで並列処理を使用します。
トップレベルの実行プランは次のとおりです。