論理的な削除を実装する最良の方法は何ですか?
-
09-06-2019 - |
質問
現在プロジェクトに取り組んでおり、大多数のユーザー (ユーザー ロール) に対して論理的な削除を実装する必要があります。データベース内の各テーブルに「is_deleted='0'」フィールドを追加し、特定のユーザー ロールが特定のレコードの削除ボタンを押した場合にそれを「1」に設定することにしました。
今後のメンテナンスに備えて、各 SELECT クエリに is_deleted='1' のレコードが含まれていないことを確認する必要があります。
論理的な削除を実装するためのより良いソリューションはありますか?
アップデート:また、アプリケーション データベース内のすべてのテーブル/フィールドに対する変更 (フィールド、古い値、新しい値、時間、ユーザー、IP) を追跡する監査データベースがあることにも注意してください。
解決
すべてのクエリは、 WHERE IS_DELETED='0'
句。
他のヒント
私は「Rails ウェイ」に傾いていきます。 deleted_at
を含む列 削除が行われた日時. 。次に、削除に関する無料のメタデータを少し取得します。あなたのための SELECT
行を取得するだけです WHERE deleted_at IS NULL
持っている is_deleted
コラムはかなり良いアプローチです。Oracle の場合、パフォーマンスをさらに向上させるために、リスト パーティションを作成してテーブルをパーティション化することをお勧めします。 is_deleted
カラム。その後、削除された行と削除されていない行は物理的に異なるパーティションに存在しますが、ユーザーにとっては透過的です。
その結果、次のようなクエリを入力すると、
SELECT * FROM table_name WHERE is_deleted = 1
その後、Oracle は「パーティション プルーニング」を実行し、適切なパーティションのみを調べます。内部的にはパーティションは別のテーブルですが、ユーザーにとっては透過的です。パーティション化されているかどうかに関係なく、テーブル全体から選択できるようになります。ただし、Oracle はクエリを実行できます。 必要なパーティションのみ. 。たとえば、次の行が 1000 行あると仮定します。 is_deleted = 0
100000行 is_deleted = 1
, 、そしてテーブルをパーティション化します is_deleted
. 。ここで条件を含めると
WHERE ... AND IS_DELETED=0
その場合、Oracle は 1000 行のパーティションのみをスキャンします。テーブルがパーティション化されていない場合は、101000 行 (両方のパーティション) をスキャンする必要があります。
テーブルが大きくパフォーマンスが問題になる場合は、いつでも「削除された」レコードを別のテーブルに移動できます。そこには、削除時刻、レコードを削除したユーザーなどの追加情報が含まれています。
そうすれば、プライマリテーブルに別の列を追加する必要がなくなります
残念ながら、最善の対応は、論理的な削除で何を達成しようとしているのか、およびこれを実装しているデータベースによって異なります。
SQL Server の場合、最善の解決策は、SMALLDATETIME または DATETIME 型 (必要な粒度に応じて) の delete_on/deleted_at 列を使用し、その列を NULL 可能にすることです。SQL Server では、行ヘッダー データにはテーブル内の各列の NULL ビットマスクが含まれるため、列に格納されている値を確認するよりも IS NULL または IS NOT NULL を実行する方がわずかに高速です。
大量のデータがある場合は、データベース自体または 2 つの別個のテーブル (例:Products と ProductHistory)、またはインデックス付きビューを通じて。
is_deleted、is_archive などのフラグ フィールドは 1 つの意味しか持たないため、通常は避けます。null 値を許容する delete_at、archived_at フィールドは、自分自身とアプリケーションを継承する人にとって、さらなるレベルの意味を提供します。また、ペストのようなビットマスク フィールドは、意味を理解するためにビットマスクがどのように構築されたかを理解する必要があるため、避けています。
それは、必要な情報とサポートしたいワークフローによって異なります。
次のことができるようになりたいですか:
- (削除される前に)どんな情報があったか知っていますか?
- いつ削除されたか知っていますか?
- 誰が削除したか知っていますか?
- それを削除したときに彼らがどのような立場で行動していたか知っていますか?
- レコードの削除を取り消すことはできますか?
- いつ削除が解除されたかを知ることができますか?
- 等
レコードが 4 回削除され、削除されなかった場合、現在は削除されていない状態にあることがわかれば十分ですか、それともその間に何が起こったか (連続した間の編集を含む) を知りたいですか?削除!)?
論理的に削除されたレコードが一意性制約違反を引き起こすことに注意してください。DB に一意制約のある列がある場合は、以前に論理的に削除されたレコードによってレコードの再作成が妨げられないように注意してください。
サイクルについて考えてみましょう。
- ユーザーの作成 (ログイン=JOE)
- 論理的な削除 (削除された列を null 以外に設定します。)
- (再) ユーザー (ログイン=JOE) を作成します。エラー。LOGIN=JOE はすでに取られています
2 回目の作成では、login=JOE が論理的に削除された行にすでに存在するため、制約違反が発生します。
いくつかのテクニック:1.削除されたレコードを新しいテーブルに移動します。2.ログイン列とdeleted_at timestamp列全体で一意性制約を作成します。
私個人の意見としては、新しいテーブルに移動することに対して +1 です。すべてのクエリで *およびdelete_at = null *を維持するために、それは多くの規律を取ります(すべての開発者向け)
Jim が言ったように、削除されたデータを別のテーブルに移動し、いつ、なぜ、誰によって削除されたかの記録があれば、間違いなくパフォーマンスが向上します。
追加 where
すべてのクエリにクエリを実行すると、クエリの速度が大幅に低下し、テーブル上にあるインデックスの使用が妨げられます。可能な限り、テーブルに「フラグ」を置かないでください。deleted
=0
私がプロジェクトで使用するものは、statusing not null default 0列ではないstatusind int null delult intationindを使用して、ビットマスクとしてデータ管理(削除、アーカイブ、複製、復元など)を実行できます。これをビューで使用すると、使用するアプリケーションに対してデータの配布、公開などを行うことができます。ビューに関するパフォーマンスが懸念される場合は、小さなファクト テーブルを使用してこの情報をサポートし、ファクトを削除し、リレーションを削除し、呼び出した削除を可能にします。
拡張性に優れ、データ中心なのでデータ フットプリントがかなり小さく保たれます。リアルタイム性を考慮すると 350GB 以上のデータベースが重要です。代替手段、テーブル、トリガーの使用にはオーバーヘッドがあり、必要に応じて機能する場合と機能しない場合があります。
SOX 関連の監査では、ケースに役立つフィールド以上のものが必要になる場合がありますが、これが役立つ場合があります。楽しむ
どの製品については言及していませんが、SQL Server 2008 と postgresql (および他の製品も) ではフィルターされたインデックスを作成できるため、is_deleted=0 のカバー インデックスを作成して、この特定のアプローチのマイナス点の一部を軽減できます。 。
私はステータス列を保持することを好みます。そうすれば、それをいくつかの異なる構成に使用できます。公開済み、非公開、削除済み、承認が必要...
is_deleted=0 をチェックするビュー、関数、またはプロシージャを使用します。つまり、後で他の理由でテーブルを変更する必要がある場合に備えて、テーブル上で直接選択しないでください。
より大きなテーブルの is_deleted 列にインデックスを付けます
すでに監査証跡があるため、削除日の追跡は不要です
別のスキーマを作成し、それをすべてデータ スキーマに付与します。新しいスキーマに VPD を実装すると、すべてのクエリに、削除されていない行のみを選択できる述語が追加されるようになります。http://download.oracle.com/docs/cd/E11882_01/server.112/e16508/cmntopc.htm#CNCPT62345
@AdditionalCriteria("this.status <> '削除済み'")
これを @entity の上に置きます