IsDeleted(ソフト削除)を強制的に実装する場合の適切なインデックスアーキテクチャは何ですか?


16

現在、完全に機能する既存のデータベースとアプリケーションがあります。この時点でアーキテクチャを変更することはできません。今日、データベース内の各テーブルには、「IsDeleted」NOT NULL BITフィールドがあり、デフォルトは「0」です。アプリケーションがデータを「削除」すると、IsDeletedフラグが1に更新されます。

私が理解できないのは、各テーブルのインデックスの構成方法です。現在、すべてのquery / join / etcは常にIsDeletedチェックを実装しています。開発者が従わなければならない標準です。そうは言っても、各テーブルのクラスター化プライマリキーインデックスをすべて変更して、プライマリキーとIsDeleted BITフィールドを含める必要があるかどうかを判断しようとしています。また、すべてのquery / join / etcから。IsDeletedチェックを実装する必要がありますが、すべての単一のインデックス(非クラスター化)がIsDeletedフィールドをインデックスの最初のフィールドとして含むべきであるという適切な仮定ですか?

もう1つの質問は、フィルター選択されたインデックスに関するものです。「WHERE IsDeleted = 0」などのインデックスにフィルターを適用して、インデックスのサイズを小さくできることを理解しています。ただし、すべての結合/クエリはIsDeletedチェックを実装する必要があるため、(結合/クエリでIsDeleted列が使用されるため)フィルター選択されたインデックスが使用されないようにしますか?

私は、IsDeletedアプローチを変更する能力がないことを忘れないでください。

回答:


13

ここで最も簡単なアプローチは、キーとクラスター化インデックスをそのままにして、非クラスター化インデックスにフィルター選択されたインデックスを使用することです。

さらに、いくつかの大きなテーブルをパーティションヒープまたはパーティション化されたクラスター化列ストア(SQL Server 2016+)に移行し、プライマリキーと一意のインデックスをパーティション化せずに残すことができます。これにより、IsDeleted行の非キー列を別のデータ構造にプッシュできるようになります。このデータ構造は、別の方法で圧縮したり、別のファイルグループに保存したりできます。

また、開発者がパラメータの代わりにリテラルを使用して、IsDeletedの行を除外することを確認してください。パラメータを使用すると、SQL Serverは両方のケースで同じクエリプランを使用する必要があります。

例えば

SELECT ... WHERE ... AND IsDeleted=0

ではなく:

SELECT ... WHERE ... AND IsDeleted=@IsDeleted

パラメーターを使用すると、フィルター選択されたインデックスを使用できなくなり、パラメータースニッフィングで問題が発生する可能性があります。


IsDeleted列の遍在性と重要性を考えると、物理ストレージに関係なく、2つのビュー(オプションで異なるスキーマ)を介してデータを公開し、パラメーター化の問題を解決し、本来あるべきではないデータにアクセスする際にミスを犯すことはおそらく意味がありますアクセスされる可能性は低い。基本データへのアクセスは、削除されたデータと削除されていないデータを何らかの方法で組み合わせる必要があり、実際に行を「削除済み」に切り替える必要がある場合にのみ関連します。
Jeroen Mostert

@JeroenMostert良いアドバイス。RLSはここでも使用できますが、EF Core Global Query Filtersのようなものも使用できます。docs.microsoft.com/en-us/ef/core/querying/filters
デビッドブラウン

9

これは人気のない意見かもしれませんが、「これをどこでもやる」/ワンサイズがあなたの質問のすべての答えに合うとは思いません。

理由もなく多数のIsDeleted行をスキャンするクエリがある場合、1つの解決策は、そのクエリを満たすためにフィルター処理された非クラスター化インデックスを作成することです。

別のオプションは、多くの異なるクエリで利用できるインデックス付きビューを作成することです。これは、削除されていない行のみにフィルターされます。これは、NOEXPANDヒントを提供せずに自動インデックス付きビューマッチングが機能するEnterprise Editionで特に便利です。

小さなテーブル、または頻繁に読み取られるテーブルの場合、フィルター処理された非クラスター化インデックスやビューなどを追加すると、データベースに不要なオーバーヘッドが追加されるだけです。


2

削除はまれであるという合理的な仮定の下では、インデックスの変更は適切な解決策ではありません。

遅かれ早かれ、削除された行への参照を照会する必要があり、インデックスにある行は突然価値があります。

ビューを使用していない限り、すべてのクエリを編集してフィルターを含める必要があることに注意してください。


0

IS_DELETEDフラグが0またはPKの値であるシステムを見ました。他のシステムでは、PKのマイナスでした。

ほとんどのクエリは「自然」キーまたはビジネス(時にはマルチフィールド)キーで値を取得するため、結合を除いてPKによるクエリは実行されません。ただし、メインテーブルと結合テーブルの最後に常にAND IS_DELETED = 0を追加しました。

このシステムには、変更を追跡するすべてのトランザクションテーブル用の監査テーブルもありました。また、アプリケーションには、削除されたデータを含むすべてのデータ変更を表示する機能がありました。


0

クエリを変更する権利と能力があることを願っています。

ただし、すべての結合/クエリはIsDeletedチェックを実装する必要があるため、(IsDeleted列が結合/クエリで使用されるため)フィルター選択されたインデックスが使用されないようにしますか?

私は一つの重要なポイントを言いたかった、私はそれを説明できることを願っています。

複雑なクエリでwhere Transaction tableMasterテーブルの両方が使用されています。

テーブルでIsDeleted=0のみ使用してTransactionください。Masterテーブルでは使用しないでください。

例、

Select * from dbo.Order O
inner join dbo.category C on o.categoryid=o.categoryid
inner join dbo.Product P on P.Productid=o.Productid
where o.isdeleted=0

c.isdeleted=0Category表で使用する)には意味がありません。それは不要です。

同様に、使用する点はありますP.isdeleted=0か?

削除されていない注文とその詳細がすべて欲しいからです。

いつ、どこで参照ProductされてOrderいるActiveかを削除する方法Productid

したがって、この方法で重要なクエリを慎重にデバッグすると、isdeleted = 0の一部を削除できる場合があります。

盲目的にフィルタインデックスを作成しないでください。まず、非常に重要で遅いクエリをすべて選択してください。

これらの遅いクエリを最適化してから、フィルターインデックスまたはインデックスの調整のみを決定します。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.