次の形式のクエリがあります。
SELECT ...
FROM ColumnstoreTable cs
CROSS APPLY (
SELECT *
FROM (VALUES
('A', cs.DataA)
, ('B', cs.DataB)
, ('C', cs.DataC)
) x(Col0, Col1)
) someValues
これは、Columnstoreを使用するサブクエリ(ColumnstoreTable
)からすべての行を取得し、それらの行を乗算します。これは基本的にUNPIVOT
です。実際のクエリはこれより大きくなります。クエリのこの部分は、他の処理に送られます。
ここでの問題は、このCROSS APPLY
は、適切な選択であるループ結合として実装されます。残念ながら、ループ結合はバッチモードをサポートしていません。
クエリのこの部分はパフォーマンスが非常に重要であり、バッチモードで実行するとパフォーマンスに非常に有利になると思います。
このクエリを書き換えて、バッチモードから移行しないようにするにはどうすればよいですか?
VALUES
の代わりに一時テーブルを使用してみましたが、ハッシュ結合の等価結合条件がないという事実は変わりませんでした。
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でクエリを実行する必要がなくなります。私がうそをついている列ストアテーブルでは、必要な300MM行を生成するのにかなり良いパフォーマンスを示しています。
CPU time = 33803 ms, elapsed time = 4363 ms.
そして、実際の計画はハッシュ結合の適切な並列化を示しています。
すべての行の値が同じである場合のハッシュ結合の並列化に関する注意
このクエリのパフォーマンスは、結合のプローブ側の各スレッドが完全なハッシュテーブルにアクセスできるかどうかに大きく依存します(ハッシュパーティションバージョンとは異なり、1つの異なる値しかない場合にすべての行を単一のスレッドにマップします) dummy
列の場合)。
幸いなことに、これはこの場合に当てはまり(プローブ側にParallelism
演算子がないことからわかるように)、バッチモードはスレッド間で共有される単一のハッシュテーブルを作成するため、確実に当てはまるはずです。したがって、各スレッドはColumnstore Index Scan
から行を取得し、それらをその共有ハッシュテーブルと照合できます。 SQL Server 2012では、流出によってオペレーターが行モードで再起動し、バッチモードの利点が失われるだけでなく、結合のプローブ側でRepartition Streams
オペレーターが必要になるため、この機能はあまり予測できませんでした。この場合、スレッドのスキュー。流出をバッチモードのままにできるようにすることは、SQL Server 2014の主な改善点です。
私の知る限りでは、行モードにはこの共有ハッシュテーブル機能はありません。ただし、場合によっては、通常、ビルド側の推定行数が100行未満の場合、SQL Serverは各スレッドのハッシュテーブルの個別のコピーを作成します(ハッシュ結合につながるDistribute Streams
で識別可能)。これは非常に強力ですが、カーディナリティの見積もりに依存し、SQL Serverが各スレッドのハッシュテーブルの完全なコピーを作成することの利点とコストを評価しようとしているため、バッチモードよりも信頼性が低くなります。
UNION ALL:より簡単な代替手段
ポールホワイトは、別の、場合によってはより単純なオプションとして、各値の行を結合するために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.