UNPIVOT(ループ結合)でバッチモードを使用する方法は?


12

次の形式のクエリがあります。

SELECT ...
FROM ColumnstoreTable cs
CROSS APPLY (
    SELECT *
    FROM (VALUES
        ('A', cs.DataA)
        , ('B', cs.DataB)
        , ('C', cs.DataC)
    ) x(Col0, Col1)
) someValues

これは、Columnstore-backedサブクエリ(ColumnstoreTable)からすべての行を取得し、それらの行を乗算します。これは本質的にUNPIVOTです。実際のクエリはこれよりも大きくなります。クエリのこの部分は、他の処理に送られます。

ここでの問題は、これCROSS APPLYが合理的な選択であるループ結合として実装されていることです。残念ながら、ループ結合はバッチモードをサポートしていません。

クエリのこの部分はパフォーマンスが非常に重要であり、バッチモードで実行するとパフォーマンスに非常に有益であると思われます。

バッチモードから移行しないように、このクエリを書き換えるにはどうすればよいですか?

の代わりに一時テーブルを使用してみましたVALUESが、ハッシュ結合に等価結合条件がないという事実は変わりませんでした。

回答:


12

1つの方法は、値に#tempテーブルを使用し、ダミーの等価結合列を導入してハッシュ結合を可能にすることです。例えば:

-- Create a #temp table with a dummy column to match the hash join
-- and the actual column you want
CREATE TABLE #values (dummy INT NOT NULL, Col0 CHAR(1) NOT NULL)
INSERT INTO #values (dummy, Col0)
VALUES (0, 'A'),
        (0, 'B'),
        (0, 'C')
GO

-- A similar query, but with a dummy equijoin condition to allow for a hash join
SELECT v.Col0, 
    CASE v.Col0 
        WHEN 'A' THEN cs.DataA 
        WHEN 'B' THEN cs.DataB 
        WHEN 'C' THEN cs.DataC
        END AS Col1
FROM ColumnstoreTable cs
JOIN #values v
    -- Join your dummy column to any numeric column on the columnstore,
    -- multiplying that column by 0 to ensure a match to all #values
    ON v.dummy = cs.DataA * 0

パフォーマンスとクエリプラン

このアプローチでは、次のようなクエリプランが生成され、ハッシュモードはバッチモードで実行されます。

ここに画像の説明を入力してください

私が交換した場合SELECTとの声明をSUMするCASE私が転がっているコンソールにすべてのそれらの行をストリーミングして、実際の100MM行columnstoreテーブルにクエリを実行するのを避けるために声明、私は必要な300MMを生成するために、かなり良好なパフォーマンスを参照してください行:

CPU time = 33803 ms, elapsed time = 4363 ms.

そして、実際の計画では、ハッシュ結合の適切な並列化が示されています。

ここに画像の説明を入力してください

すべての行が同じ値を持つ場合のハッシュ結合並列化に関する注意

このクエリのパフォーマンスは、完全なハッシュテーブルにアクセスできる結合のプローブ側の各スレッドに大きく依存します(ハッシュパーティションバージョンでは、1つの異なる値しかない場合にすべての行を1つのスレッドにマップします)以下のためのdummyコラム)。

幸いなことに、これはこの場合に当てはまり(Parallelismプローブ側に演算子がないことからわかるように)、バッチモードはスレッド間で共有される単一のハッシュテーブルを構築するため、確実に当てはまるはずです。したがって、各スレッドはから行を取得Columnstore Index Scanし、その共有ハッシュテーブルと一致させることができます。SQL Server 2012では、こぼれによりオペレーターが行モードで再起動し、バッチモードの利点を失い、Repartition Streamsこの場合スレッドスキューを引き起こす結合のプローブ側にオペレーターを必要とするため、この機能の予測性ははるかに低くなりました。 。流出をバッチモードのままにしておくことは、SQL Server 2014の大幅な改善です。

私の知る限り、行モードにはこの共有ハッシュテーブル機能はありません。ただし、通常、ビルド側の推定行数が100行未満の場合、SQL Serverは各スレッドのハッシュテーブルの個別のコピーを作成します(Distribute Streamsハッシュ結合の先頭で識別可能)。これは非常に強力ですが、カーディナリティの見積もりに依存し、SQL Serverは各スレッドのハッシュテーブルの完全なコピーを構築する利点とコストを評価しようとするため、バッチモードよりも信頼性がはるかに低くなります。

UNION ALL:より単純な代替

Paul Whiteは、別の、場合によってはより単純なオプションを使用UNION ALLして、各値の行を結合することを指摘しました。これはおそらく、このSQLを動的に構築するのが簡単だと仮定した場合の最善策です。例えば:

SELECT 'A' AS Col0, c.DataA AS Col1
FROM ColumnstoreTable c
UNION ALL
SELECT 'B' AS Col0, c.DataB AS Col1
FROM ColumnstoreTable c
UNION ALL
SELECT 'C' AS Col0, c.DataC AS Col1
FROM ColumnstoreTable c

これにより、バッチモードを利用できるプランが得られ、元の回答よりも優れたパフォーマンスが得られます。(どちらの場合もパフォーマンスは十分に速いので、データを選択したりテーブルに書き込むとすぐにボトルネックになります。)このUNION ALLアプローチでは、0を掛けるなどのゲームをすることも避けられます。

CPU time = 8673 ms, elapsed time = 4270 ms.

ここに画像の説明を入力してください


UNION ALLトリックは知っておくと便利ですが、ソースクエリが単なるテーブルではなく、コストのかかるクエリ自体の場合は失敗します。
boot4life
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.