以下のクエリがあり、変数でTOPを使用した場合のソートの見積もりを理解しようとしています。
create table justint ( c1 int )
insert into justint
select top 1000000 row_number() over(order by t1.number) as N
from master..spt_values t1
cross join master..spt_values t2
declare @MaxRowsToReturn INT = 500
SELECT top (@MaxRowsToReturn) c1
FROM justint WITH (NOLOCK)
where c1 >= 600000
ORDER BY c1 ASC
では、ソート演算子に入る404986の見積もりは、100の見積もりとしてどのように出力されますか? TOP演算子で変数をスニッフィングできないので、それは単なる任意の数ですか?
これは、パラメータスニッフィング(またはこの場合はその欠如)の問題のように思われます。
オプティマイザーは、ユーザーがTOPオペレーターから必要とする行数を知らなくても、バッチに最適なプランを選択する必要があります。ここで重要なのは、@ Maxrowstoreturnは、それが使用されるバッチ中に設定されるということです。つまり、エンジンは、その変数が初期化または使用される前に、プラン全体を作成する必要があります。なぜ100という数字が使われるのかというと、とにかくこの状況で確実な見積もりを提供する方法がないので、開発者による恣意的な決定である可能性があります。
次のコードを実行すると、SQLは入力パラメーターを使用してより正確な実行プランを作成できるため、見積もりが修正されます。
create table justint ( c1 int )
go
insert into justint
select top 1000000 row_number() over(order by t1.number) as N
from master..spt_values t1
cross join master..spt_values t2
go
update statistics justint with fullscan;
go
create procedure #tmpproc
(@Maxrowstoreturn int)
AS
begin
SELECT top (@MaxRowsToReturn) c1
FROM justint WITH (NOLOCK)
--where c1 >= 600000
ORDER BY c1 ASC
end
go
exec #tmpproc 10000
drop table justint