TL; DR:どのパーティションがリサンプリングされ、どれがインクリメンタル統計が使用されていないかを見つけることは可能ですか?プラットフォームはSQL Server 2014 Enterpriseエディションです。
いくつかの背景情報を含む長いバージョンはそうです。
かなり一般的なDW環境を想定して、パーティションテーブルがあります。パーティション化は日付列に基づいています。これは、ステージングデータが別のテーブルにロードされ、前処理の後、パーティション切り替えを使用してデータが本番ファクトテーブルに移動されるときに行われます。ああ、そしてクラスター化列ストアインデックスが使用されています。使用されているパーティションは約1000あります。 DBは仮想マシンで実行されています。
ファクトテーブルには7.5ギガロウ(100 GB)があります。毎日の成長は約5メガロウです。これは、統計の自動更新をトリガーするには成長率が小さすぎるため、トレースフラグ2371(未試行)を保存します。
時代遅れの統計に対する開発者の反応は、それらを更新することでした。 7.5ギガロウの場合、すべての統計の完全な更新には約5時間かかります。単一の統計更新の場合、処理パフォーマンスは約20分、つまり1秒あたり90メガロウです。
システムはVM=プラットフォーム上にあるため、ビジネスルールによってコストが制限されます。メモリもIOPSも簡単には増加しません。5時間の更新ジョブは、夜間のETLプロセスに含めるには遅すぎるため、統計が古くなっている、予期しない時間に更新されている、またはメンテナンスウィンドウで更新される。
SQL Serverはバージョン2014 Enterpriseエディションなので、ソリューションと同じように聞こえる インクリメンタル統計 をサポートしています。統計を増分に変換した後、単一のパーティションの単一の統計の処理にかかる時間はわずか20秒です。新しく切り替えられたパーティションの合計は約5分です。これは素晴らしい音で、確かにETLプロセスに適合します。
不思議なのは、パーティションスイッチ環境で増分統計を管理する方法です。統計が日付Dに増分として変換および更新されると仮定すると、たとえば日付D + 2に未処理のパーティションをどのようにして見つけますか?切り替えプロセスは明らかにパーティションIDを認識しているため、ETLプロセスでの統計の更新は簡単です。しかし、リサンプリングされていないパーティションがある場合、どのようにしてそれらを見つけるのでしょうか?
sys.dm_db_stats_properties
で確認できますsys.partitions
から入手できますsys.partition_range_values
にあります統計の最終更新日Lを選び、それを今日の日付Tと比較することができます。次に、どのパーティションID Lがポイントし、それがTと同じであるかどうかを計算します。次に、すべてのパーティションIDの更新を続行します[L、T)。これはトリッキーでエラーが発生しやすいように聞こえるので、より良い方法はありますか?どのパーティションがリサンプリングに使用されているかを示すDMVはいいでしょうが、1つはありませんね。
私の最初の質問は、なぜ最初に実際にインクリメンタルを使用しているのかを尋ねることです。これが an answer が増分統計に関して投稿したものです blog post Erin Stellatoによるもので、主要な不満の1つと増分統計での落とし穴を明らかにします(これらはオプティマイザによるパーティションレベル)、および 2ブログの投稿 増分統計の潜在的なユースケースの評価を行う私自身による作業。
そうは言っても、パーティションの統計がいつサンプリングされたかを知るには、文書化されていないDMF(sys.dm_db_stats_properties_internal()
)を使用してパーティションレベルの情報を取得できます。かなり高いレベルで階層を理解する方法を説明する このブログ投稿 についてコメントがあります。
select
sysdatetime(),
schema_name = sh.name,
table_name = t.name,
stat_name = s.name,
index_name = i.name,
leading_column = index_col(quotename(sh.name)+'.'+quotename(t.name),s.stats_id,1),
s.stats_id,
parition_number = isnull(sp.partition_number,1),
s.has_filter,
s.is_incremental,
s.auto_created,
sp.last_updated,
sp.rows,
sp.rows_sampled,
sp.unfiltered_rows,
modification_counter = coalesce(sp.modification_counter, n1.modification_counter)
from sys.stats s
join sys.tables t
on s.object_id = t.object_id
join sys.schemas sh
on t.schema_id = sh.schema_id
left join sys.indexes i
on s.object_id = i.object_id
and s.name = i.name
cross apply sys.dm_db_stats_properties_internal(s.object_id, s.stats_id) sp
outer apply sys.dm_db_stats_properties_internal(s.object_id, s.stats_id) n1
where n1.node_id = 1
and (
(is_incremental = 0)
or
(is_incremental = 1 and sp.partition_number is not null)
)
and t.name = '<<TABLENAME>>'
and s.name like '<<STATNAME>>%'
order by s.stats_id,isnull(sp.partition_number,1);