前回の投稿では、1から1,000までの連続した数字のシーケンスを生成する方法について話しました。次に、次のレベルのスケールについて説明します。50,000と1,000,000の数値のセットを生成します。
50,000個の数字のセットを生成する
このシリーズを始めたとき、私はさまざまなアプローチがより大きな数のセットにどのようにスケーリングするのか本当に興味がありました。ローエンドでは、sys.all_objects
を使用して、私のお気に入りのアプローチを見つけて少しがっかりしました。 –最も効率的な方法ではありませんでした。しかし、これらのさまざまな手法を50,000行に拡張するにはどうすればよいでしょうか。
数値表
1,000,000行のNumbersテーブルをすでに作成しているため、このクエリは実質的に同じままです:
SELECT TOP (50000) n FROM dbo.Numbers ORDER BY n;
計画:
spt_values
spt_values
には最大2,500行しかないため 、セットジェネレータのソースとして使用する場合は、もう少しクリエイティブにする必要があります。より大きなテーブルをシミュレートする1つの方法は、CROSS JOIN
です。 それ自体に対して。これを生で実行すると、最大2,500行の2乗(600万行以上)になります。必要な行は50,000行だけで、約224行の2乗が必要です。だから私たちはこれを行うことができます:
;WITH x AS ( SELECT TOP (224) number FROM [master]..spt_values ) SELECT TOP (50000) n = ROW_NUMBER() OVER (ORDER BY x.number) FROM x CROSS JOIN x AS y ORDER BY n;
これは、このバリエーションと同等ですが、より簡潔であることに注意してください。
SELECT TOP (50000) n = ROW_NUMBER() OVER (ORDER BY x.number) FROM (SELECT TOP (224) number FROM [master]..spt_values) AS x CROSS JOIN (SELECT TOP (224) number FROM [master]..spt_values) AS y ORDER BY n;
どちらの場合も、計画は次のようになります。
sys.all_objects
spt_values
のように 、sys.all_objects
単独では50,000行の要件を完全には満たしていないため、同様のCROSS JOIN
を実行する必要があります。 。
;;WITH x AS ( SELECT TOP (224) [object_id] FROM sys.all_objects ) SELECT TOP (50000) n = ROW_NUMBER() OVER (ORDER BY x.[object_id]) FROM x CROSS JOIN x AS y ORDER BY n;
計画:
スタックCTE
正確に50,000行を取得するには、スタックされたCTEをわずかに調整するだけです。
;WITH e1(n) AS ( SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 ), -- 10 e2(n) AS (SELECT 1 FROM e1 CROSS JOIN e1 AS b), -- 10*10 e3(n) AS (SELECT 1 FROM e2 CROSS JOIN e2 AS b), -- 100*100 e4(n) AS (SELECT 1 FROM e3 CROSS JOIN (SELECT TOP 5 n FROM e1) AS b) -- 5*10000 SELECT n = ROW_NUMBER() OVER (ORDER BY n) FROM e4 ORDER BY n;
計画:
再帰的CTE
再帰CTEから50,000行を取得するには、さらに重要性の低い変更が必要です。WHERE
を変更します。 句を50,000に変更し、MAXRECURSION
を変更します ゼロにするオプション。
;WITH n(n) AS ( SELECT 1 UNION ALL SELECT n+1 FROM n WHERE n < 50000 ) SELECT n FROM n ORDER BY n OPTION (MAXRECURSION 0);
計画:
この場合、ソートに警告アイコンがあります。私のシステムでは、ソートがtempdbに流出する必要がありました。システムに流出が見られない場合もありますが、これはこの手法に必要なリソースに関する警告であるはずです。
パフォーマンス
最後の一連のテストと同様に、コールドキャッシュとウォームキャッシュの両方、および圧縮と非圧縮の両方を使用したNumbersテーブルを含む各手法を比較します。
ミリ秒単位のランタイムで、50,000個の連続した数値を生成します>
より良い視覚を得るために、このテストでは完全な犬であり、結果を歪める再帰CTEを削除しましょう:
実行時間(ミリ秒単位)で、50,000個の連続した数値を生成します(再帰を除く) CTE)
1,000行では、クエリはそれぞれ8ページと9ページを読み取るだけでよいため、圧縮と非圧縮の違いはわずかでした。 50,000行では、ギャップが少し広がります。74ページ対113です。ただし、データを解凍するための全体的なコストは、I/Oの節約を上回っているようです。したがって、50,000行の場合、非圧縮の数値テーブルが連続したセットを導出するための最も効率的な方法のように見えますが、確かに、利点はわずかです。
1,000,000個の数値のセットを生成する
これほど大きな数の連続したセットが必要になるような多くのユースケースを想像することはできませんが、完全を期すために、そしてこの規模でいくつかの興味深い観察を行ったので、それを含めたかったのです。
数値表
ここで驚くことはありません。クエリは次のようになります:
SELECT TOP 1000000 n FROM dbo.Numbers ORDER BY n;
TOP
厳密に必要というわけではありませんが、これは、Numbersテーブルと目的の出力の行数が同じであることがわかっているためです。計画は以前のテストと非常によく似ています:
spt_values
CROSS JOIN
を取得するには 1,000,000行になるので、1,000行を2乗する必要があります:
;WITH x AS ( SELECT TOP (1000) number FROM [master]..spt_values ) SELECT n = ROW_NUMBER() OVER (ORDER BY x.number) FROM x CROSS JOIN x AS y ORDER BY n;>
計画:
sys.all_objects
ここでも、1,000行の外積が必要です:
;WITH x AS ( SELECT TOP (1000) [object_id] FROM sys.all_objects ) SELECT n = ROW_NUMBER() OVER (ORDER BY x.[object_id]) FROM x CROSS JOIN x AS y ORDER BY n;
計画:
スタックCTE
スタックされたCTEの場合、CROSS JOIN
のわずかに異なる組み合わせが必要です。 s 1,000,000行に到達するには:
;WITH e1(n) AS ( SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 ), -- 10 e2(n) AS (SELECT 1 FROM e1 CROSS JOIN e1 AS b), -- 10*10 e3(n) AS (SELECT 1 FROM e1 CROSS JOIN e2 AS b), -- 10*100 e4(n) AS (SELECT 1 FROM e3 CROSS JOIN e3 AS b) -- 1000*1000 SELECT n = ROW_NUMBER() OVER (ORDER BY n) FROM e4 ORDER BY n;
計画:
この行サイズでは、スタックされたCTEソリューションが並列になっていることがわかります。そこで、MAXDOP 1
を使用したバージョンも実行しました 以前と同様の計画形状を取得し、並列処理が本当に役立つかどうかを確認するには:
再帰CTE
再帰CTEにも、わずかな変更があります。 WHERE
のみ 条項を変更する必要があります:
;WITH n(n) AS ( SELECT 1 UNION ALL SELECT n+1 FROM n WHERE n < 1000000 ) SELECT n FROM n ORDER BY n OPTION (MAXRECURSION 0);
計画:
パフォーマンス
ここでも、再帰CTEのパフォーマンスがひどいことがわかります。
1,000,000個の連続した数値を生成するための実行時間(ミリ秒単位)>
グラフからその外れ値を削除すると、パフォーマンスに関するより良い画像が得られます:
実行時間(ミリ秒単位)で、1,000,000個の連続した数値を生成します(再帰を除く) CTE)
ここでも、圧縮されていないNumbersテーブル(少なくともウォームキャッシュを使用)が勝者と見なされますが、このスケールでも違いはそれほど顕著ではありません。
続く…
数列を生成するためのいくつかのアプローチを徹底的に調査したので、日付に移ります。このシリーズの最後の投稿では、カレンダーテーブルの使用を含む、セットとしての日付範囲の構築と、これが便利ないくつかのユースケースについて説明します。
[パート1|パート2|パート3]
付録:行数
正確な数の行を生成しようとしていない可能性があります。代わりに、多くの行を生成する簡単な方法が必要な場合があります。以下は、単にSELECT
するだけで、さまざまな行数を取得できるカタログビューの組み合わせのリストです。 WHERE
なし 句。これらの数値は、RTMまたはサービスパックのどちらを使用しているか(一部のシステムオブジェクトが追加または変更されるため)、および空のデータベースがあるかどうかによっても異なることに注意してください。
ソース | 行数 | ||
---|---|---|---|
SQL Server 2008 R2 | SQL Server 2012 | SQL Server 2014 | |
master..spt_values | 2,508 | 2,515 | 2,519 |
master..spt_values CROSS JOIN master..spt_values | 6,290,064 | 6,325,225 | 6,345,361 |
sys.all_objects | 1,990 | 2,089 | 2,165 |
sys.all_columns | 5,157 | 7,276 | 8,560 |
sys.all_objects CROSS JOIN sys.all_objects | 3,960,100 | 4,363,921 | 4,687,225 |
sys.all_objects CROSS JOIN sys.all_columns | 10,262,430 | 15,199,564 | 18,532,400 |
sys.all_columns CROSS JOIN sys.all_columns | 26,594,649 | 52,940,176 | 73,273,600 |
表1:さまざまなカタログビュークエリの行数