かなり大きなレコード数(1000万から2000万行)のデータウェアハウスがあり、特定の日付の間のレコードをカウントしたり、特定のフラグ(たとえば、.
SELECT
f.IsFoo,
COUNT(*) AS WidgetCount
FROM Widgets AS w
JOIN Flags AS f
ON f.FlagId = w.FlagId
WHERE w.Date >= @startDate
GROUP BY f.IsFoo
パフォーマンスは悪くありませんが、比較的遅くなる可能性があります(コールドキャッシュでおそらく10秒)。
最近、インデックス付きビューでGROUP BY
を使用できることがわかり、次のようなものを試しました
CREATE VIEW TestView
WITH SCHEMABINDING
AS
SELECT
Date,
FlagId,
COUNT_BIG(*) AS WidgetCount
FROM Widgets
GROUP BY Date, FlagId;
GO
CREATE UNIQUE CLUSTERED INDEX PK_TestView ON TestView
(
Date,
FlagId
);
その結果、最初のクエリのパフォーマンスは100ミリ秒未満になり、結果のビューとインデックスは100k未満になります(行数は多いですが、日付とフラグIDの範囲は、このビューには1000〜2000行しか含まれないことを意味します)。
これはおそらくウィジェットテーブルへの書き込みのパフォーマンスを低下させると思いましたが、違います-このテーブルへの挿入と更新のパフォーマンスは、私が知る限り、ほとんど影響を受けません(さらに、このテーブルは頻繁に更新されないデータウェアハウスであるため)とにかく)
私にとって、これは本当であるにはあまりにも良すぎるようです-それは本当ですか?このようにインデックス付きビューを使用する場合、何に注意する必要がありますか?
お気づきのように、ビュー自体は少数の行しか実体化しません。したがって、テーブル全体を更新しても、ビューの更新に関連する追加 I/Oは無視できます。おそらく、ビューを作成したときに感じる最大の痛みをすでに感じています。次に近いのは、ビューに新しい行を必要とする新しいIDの束を含むベーステーブルにガジリオンの行を追加した場合です。
これは本当であるには余りにも良くない。インデックス付きビューを、その使用目的とまったく同じように、または少なくとも最も効果的な方法の1つとして使用します。書き込み時に将来のクエリ集計に料金を支払うためです。これは、結果がソースよりもはるかに小さい場合、およびもちろん、基礎となるデータが更新されるよりも頻繁に集計が要求される場合に最適に機能します(一般に、OLTPよりもDWで一般的です)。
残念ながら、多くの人はビューにインデックスを付けることは魔法だと思っています-インデックスはすべてのビュー、特にテーブルを結合したりソースと同じ数の行を生成する(または乗算する)ビューをより効率的にするわけではありません。これらの場合、ビューからのI/Oは元のクエリと同じか、さらには元のクエリよりも悪くなります。これは、同じ行または複数の行があるためだけでなく、多くの場合、それらはより多くの列も格納および実体化しているためです。したがって、SSDを使用する場合でも、I/O、ネットワーク、およびクライアントの処理/レンダリングが、大きな結果セットをクライアントに返す際の主要なボトルネックのままであるため、事前にそれらを具体化してもメリットはありません。実行時に結合を回避することで得られる節約は、まだ使用している他のすべてのリソースと比較して測定できません。
非クラスター化インデックスと同様に、過度に実行しないように注意してください。 1つのテーブルに10の異なるインデックス付きビューを追加すると、特にグループ化列がクラスタリングキーではない場合、ワークロードの書き込み部分への影響が大きくなります。
まあ、私はこのトピックについてブログするつもりでした。
アーロンズの回答はこの質問をうまくカバーしていた。追加する2つのこと:
集約ビューと結合ビューの両方を使用して、非常に有益です。
全体として、ユースケースは完璧なケースのようです。インデックス付きビューは、十分に活用されていない手法です。