2つのカスケード型の自己完結型(実際のテーブルなし)の再帰的CTEがある場合:
create view NumberSequence_0_100_View
as
with NumberSequence as
(
select 0 as Number
union all
select Number + 1
from NumberSequence
where Number < 100
)
select Number
from NumberSequence;
go
create view NumberSequence_0_10000_View
as
select top 10001
v100.Number * 100 + v1.Number as Number
from Common.NumberSequence_0_100_View v100
cross join Common.NumberSequence_0_100_View v1
where v1.Number < 100
and v100.Number * 100 + v1.Number <= 10000
-- please resist complaining about "order by in view" for this question
order by v100.Number * 100 + v1.Number
go
次に、以下の見積もり/実際の計画を生成します。
select * from NumberSequence_0_10000_View
見積もり 実際
実行時間は23ミリ秒ですが、最終出力用に1行だけを見積もります(最初のビューだけで2行)。
問題は、これが実際のデータと結合するためのサブクエリとして使用される場合(たとえば、「DaysAgo」によって)、プランは通常、非常に遅いネストされたループであり、結合ヒント/逆順などを追加する必要があることがよくあります。
CTEアプローチを維持しながら見積もりを改善する方法はありますか? 「with(AssumeMinRows = N)」ヒントのリクエストはありましたか?これは、多くの場合(CTEだけでなく)の優れた汎用ヘルパーのようです。
再帰CTEが1行だけを推定するのはなぜですか?
再帰共通テーブル式のカーディナリティ推定は非常に制限されています。
元のカーディナリティ推定モデルでは、推定値はアンカー部分と再帰部分のカーディナリティ推定値の単純な合計です。これは、再帰部分が1回だけ実行されると想定するのと同じです。
SQL Server 2014では、新しいカーディナリティ推定モデルが有効になっているため、ロジックがわずかに変更され、再帰部分が3回実行され、各反復で同じ行数が返されると想定されます。
これらは両方とも知識のない推測であるため、再帰的CTを使用すると、通常、品質の推定が不十分になるのは当然のことです。より一般的には、再帰的プロセスの結果を推定することはほぼ不可能であるため、オプティマイザーは試行すらしません。これは、特に連続番号を生成することを明確に意図した、特に単純な再帰構造を使用しても変更されません。オプティマイザーには、このパターンを検出するロジックがありません。
特定のケースでは、オプティマイザーが[Recr1007]<(100)
(フィルター内のNode ID 3)および([Recr1003]*(100)+[Recr1007])<=(10000)
(ネストされたループの残りの述語はNode ID 2)で結合します。)繰り返しますが、これらは推測であり、結果は残念ですが、驚くことではありません。
CTEアプローチを維持しながら見積もりを改善する方法はありますか?
私が知っていることではありません。
「with(AssumeMinRows = N)」ヒントのリクエストはありましたか?
それらの用語で直接ではありません。 CTEの具体化 に対する多くの要求がありました。これは、そのような具体化に自動統計生成が付属している場合に役立ちます。あなたはすでにそれらの提案のほとんどにコメントしているように見えるので、私は他のものをリストしません:)
選択性のヒント の提案もありますが、このようなものはまだ製品に組み込まれていません。
質問へのコメントに記載されているように、現時点での最善の策は、再帰CTEを使用してオンザフライで生成するのではなく、実際の 数値テーブル を使用することです。 2番目のオプションは、手動のマテリアライゼーション(一時テーブル)を使用することです。ご存知のとおりです。