並列クエリの例
次のクエリでは、2000 年 4 月 1 日を開始日とする特定の四半期に受けた注文のうち、納品期日を過ぎた項目を 1 つ以上含む注文の件数を数えます。さらに、該当する注文を受注優先度別にグループ分けし、受注優先度の昇順に並べ替えてその注文数を一覧表示します。
この例には、架空のテーブル名と列名を使用しています。
SELECT o_orderpriority, COUNT(*) AS Order_Count
FROM orders
WHERE o_orderdate >= '2000/04/01'
AND o_orderdate < DATEADD (mm, 3, '2000/04/01')
AND EXISTS
(
SELECT *
FROM lineitem
WHERE l_orderkey = o_orderkey
AND l_commitdate < l_receiptdate
)
GROUP BY o_orderpriority
ORDER BY o_orderpriority
lineitem テーブルと orders テーブルで次のインデックスが定義されていると想定します。
CREATE INDEX l_order_dates_idx
ON lineitem
(l_orderkey, l_receiptdate, l_commitdate, l_shipdate)
CREATE UNIQUE INDEX o_datkeyopr_idx
ON ORDERS
(o_orderdate, o_orderkey, o_custkey, o_orderpriority)
上記のクエリに対して生成される並列プランの例を次に示します。
|--Stream Aggregate(GROUP BY:([ORDERS].[o_orderpriority])
DEFINE:([Expr1005]=COUNT(*)))
|--Parallelism(Gather Streams, ORDER BY:
([ORDERS].[o_orderpriority] ASC))
|--Stream Aggregate(GROUP BY:
([ORDERS].[o_orderpriority])
DEFINE:([Expr1005]=Count(*)))
|--Sort(ORDER BY:([ORDERS].[o_orderpriority] ASC))
|--Merge Join(Left Semi Join, MERGE:
([ORDERS].[o_orderkey])=
([LINEITEM].[l_orderkey]),
RESIDUAL:([ORDERS].[o_orderkey]=
[LINEITEM].[l_orderkey]))
|--Sort(ORDER BY:([ORDERS].[o_orderkey] ASC))
| |--Parallelism(Repartition Streams,
PARTITION COLUMNS:
([ORDERS].[o_orderkey]))
| |--Index Seek(OBJECT:
([tpcd1G].[dbo].[ORDERS].[O_DATKEYOPR_IDX]),
SEEK:([ORDERS].[o_orderdate] >=
Apr 1 2000 12:00AM AND
[ORDERS].[o_orderdate] <
Jul 1 2000 12:00AM) ORDERED)
|--Parallelism(Repartition Streams,
PARTITION COLUMNS:
([LINEITEM].[l_orderkey]),
ORDER BY:([LINEITEM].[l_orderkey] ASC))
|--Filter(WHERE:
([LINEITEM].[l_commitdate]<
[LINEITEM].[l_receiptdate]))
|--Index Scan(OBJECT:
([tpcd1G].[dbo].[LINEITEM].[L_ORDER_DATES_IDX]), ORDERED)
この図は、並列処理の次数が 4 で実行され、2 つのテーブルが結合されているクエリ オプティマイザのプランを示しています。
並列プランには、3 つの並列処理操作が含まれています。o_datkey_ptr インデックスの Index Seek 操作と l_order_dates_idx インデックスの Index Scan 操作が並列に実行されます。これにより、複数の排他ストリームが生成されます。この処理は、Index Scan 操作と Index Seek 操作の上位にあり最も近い Parallelism 操作からそれぞれ決定できます。どちらの操作もストリームを再分割し、交換の種類を決定しています。つまり、単にストリーム間でデータを再移動して、入力時と同じ数のストリームを出力時に生成しているということです。このストリーム数は並列処理の次数と同じになります。
l_order_dates_idx の Index Scan 操作の上位にある Parallelism 操作は、L_ORDERKEY の値をキーとして使用して、入力ストリームを再分割しています。このようにして、同じ L_ORDERKEY の値が同じ出力ストリームで終了します。同時に、出力ストリームは L_ORDERKEY 列の順序を維持し、Merge Join 操作の入力要件を満たします。
Index Seek 操作の上位にある Parallelism 操作は、O_ORDERKEY の値を使用して入力ストリームを再分割します。この入力は O_ORDERKEY 列の値で並べ替えられておらず、Merge Join 操作の結合列でもあるので、Parallelism 操作と Merge Join 操作の間の Sort 操作は、入力が Merge Join 操作のために結合列で並べ替えられることを保証しています。Sort 操作は、Merge Join 操作と同様に並列処理されます。
最上位にある Parallelism 操作は、複数のストリームから得た結果を 1 つのストリームに集めます。この Parallelism 操作の下位にある Stream Aggregate 操作で実行された部分集計は、Parallelism 操作の上位にある Stream Aggregate 操作の O_ORDERPRIORITY の異なる値ごとに 1 つの SUM 値に累計されます。このプランには並列処理の次数が 4 に設定された 2 つの交換セグメントが含まれているため、8 個のスレッドが使用されます。