web-dev-qa-db-ja.com

ソフト削除を実装する最良の方法は何ですか?

現在プロジェクトに取り組んでおり、大多数のユーザー(ユーザーロール)に対してソフト削除を実装する必要があります。データベースの各テーブルに「is_deleted = '0'」フィールドを追加し、特定のユーザーロールが特定のレコードの削除ボタンを押した場合に「1」に設定することにしました。

今後のメンテナンスのために、各SELECTクエリには、is_deleted = '1'のレコードが含まれないようにする必要があります。

ソフト削除を実装するためのより良いソリューションはありますか?

更新:アプリケーションデータベース内のすべてのテーブル/フィールドに対する変更(フィールド、古い値、新しい値、時間、ユーザー、IP)を追跡する監査データベースがあることにも注意してください。

43
Josh Smeaton

WHERE IS_DELETED='0'句を含むビューに対してすべてのクエリを実行できます。

46
David J. Sokol

削除が行われた日時を含むdeleted_at列に頼ります。次に、削除に関する無料のメタデータを少し取得します。 SELECTの場合、行を取得するだけWHERE deleted_at IS NULL

84
ctcherry

is_deleted列を持つことは、かなり良いアプローチです。 Oracleの場合は、パフォーマンスをさらに向上させるために、is_deleted列にリストパーティションを作成してテーブルをパーティション分割することをお勧めします。次に、削除された行と削除されていない行は物理的に異なるパーティションにありますが、あなたにとっては透過的です。

その結果、次のようなクエリを入力すると

SELECT * FROM table_name WHERE is_deleted = 1

次に、Oracleは「パーティションのプルーニング」を実行し、適切なパーティションのみを調べます。内部的にはパーティションは別のテーブルですが、ユーザーとしては透過的です。パーティション化されているかどうかに関係なく、テーブル全体を選択できます。ただし、Oracleはクエリを実行できます必要なパーティションのみ。たとえば、is_deleted = 0で1000行、is_deleted = 1で100000行があり、is_deletedでテーブルをパーティション分割するとします。条件を含めると

WHERE ... AND IS_DELETED=0

その後、Oracleは1000行のパーティションのみをスキャンします。テーブルがパーティション化されていない場合、101000行(両方のパーティション)をスキャンする必要があります。

20
Sergey Stadnik

残念ながら、最良の対応は、一時的な削除を使用して何を達成しようとしているか、これを実装しているデータベースによって異なります。

SQL Serverでの最善の解決策は、SMALLDATETIMEまたはDATETIMEのタイプ(必要な粒度に応じて)でdeleted_on/deleted_at列を使用し、その列をnullにできるようにすることです。 SQL Serverでは、行ヘッダーデータにテーブルの各列のNULLビットマスクが含まれているため、IS NULLまたはIS NOT列に格納されている値をチェックするよりもNULL。

大量のデータがある場合は、データベース自体または2つの個別のテーブル(ProductsやProductHistoryなど)またはインデックス付きビューのいずれかを使用して、データのパーティション分割を確認する必要があります。

Is_deleted、is_archiveなどのフラグフィールドは、1つの意味しか持たないため、通常は避けます。 nullableなdeleted_at、archived_atフィールドは、自分自身とアプリケーションを継承する誰にとっても追加のレベルの意味を提供します。また、ペストのようなビットマスクフィールドは、意味を理解するためにビットマスクがどのように構築されたかを理解する必要があるため、避けます。

14

テーブルが大きくパフォーマンスに問題がある場合は、「削除された」レコードをいつでも別のテーブルに移動できます。このテーブルには、削除時間、レコードを削除したユーザーなどの追加情報があります。

そうすれば、プライマリテーブルに別の列を追加する必要がなくなります。

13
Jiaaro

これは、必要な情報とサポートするワークフローによって異なります。

次のことができるようにしますか?

  • (削除される前の)どんな情報があったか知っていますか?
  • いつ削除されたか知っていますか?
  • 誰が削除したか知っていますか?
  • 彼らがそれを削除したときに彼らがどんな能力で行動していたか知っていますか?
  • レコードの削除を取り消すことができますか?
  • いつ削除が取り消されたかを知ることができますか?
  • 等.

レコードが削除されて4回削除されなかった場合、それが現在、削除されていない状態であることを知っていれば十分ですか、それとも途中で何が起こったかを知ることができますか(連続する間の編集を含む)削除!)?

10
Daniel Fortunov

一意性制約違反を引き起こすソフト削除されたレコードに注意してください。 DBに一意の制約のある列がある場合は、以前に一時削除されたレコードによってレコードの再作成が妨げられないように注意してください。

サイクルについて考えてみましょう:

  1. ユーザーの作成(ログイン= JOE)
  2. ソフト削除(削除された列をnull以外に設定します。)
  3. (再)ユーザーを作成します(login = JOE)。エラー。 LOGIN = JOEはすでに使用されています

2番目の作成では、login = JOEがすでにソフト削除された行にあるため、制約違反が発生します。

いくつかの手法:1.削除されたレコードを新しいテーブルに移動します。 2.ログインおよびdeleted_atタイムスタンプ列全体で一意性制約を作成します

私の意見は、新しいテーブルに移動するための+1です。すべてのクエリで* AND delete_at = NULL *を維持するには、多くの規律が必要です(すべての開発者向け)。

7
Andy Rappaport

削除されたデータをジムが言ったように別のテーブルに移動すると、確実にパフォーマンスが向上します。また、いつ、なぜ、誰によって削除されたかの記録も得られます。

すべてのクエリにwhere deleted = 0 </ code>を追加すると、クエリの速度が大幅に低下し、テーブルにあるインデックスの使用が妨げられます。可能な限り、テーブルに「フラグ」を設定しないでください。

4
Brent

あなたはどの製品について言及していませんが、SQL Server 2008とpostgresql(およびその他の確信がある)では、フィルター処理されたインデックスを作成できるため、is_deleted = 0のカバリングインデックスを作成して、この特定のアプローチのいくつかの欠点を軽減できます。

2
Andy Irving

プロジェクトで使用するものは、statusIndをビットマスクとして使用するstatusInd tinyint not null default 0列です。これにより、データ管理(削除、アーカイブ、複製、復元など)を実行できます。これをビューで使用して、使用するアプリケーションのデータの配布、公開などを行うことができます。ビューに関するパフォーマンスが問題になる場合は、小さなファクトテーブルを使用してこの情報をサポートし、ファクトを削除して、リレーションを削除し、いわゆる削除を許可します。

適切にスケーリングし、データ中心であり、データのフットプリントをかなり小さく保ちます-リアルタイムの懸念がある350GB以上のDBの鍵。代替手段、テーブル、トリガーを使用すると、必要に応じて機能する場合と機能しない場合があるオーバーヘッドがあります。

SOX関連の監査では、ケースを支援するためにフィールド以上のものが必要になる場合がありますが、これは役立つ場合があります。楽しい

1
Raymond Porrata

is_deleted = 0をチェックするビュー、関数、またはプロシージャを使用します。つまり、後で他の理由でテーブルを変更する必要がある場合に備えて、テーブルを直接選択しないでください。

より大きなテーブルのis_deleted列にインデックスを付けます

すでに監査証跡があるため、削除日を追跡することは冗長です

0
Steven A. Lowe

私はステータス列を保持することを好むので、いくつかの異なる構成、つまり、公開、プライベート、削除、needsAproval ...に使用できます。

0
UnkwnTech

他のスキーマを作成し、すべてをデータスキーマに付与します。新しいスキーマにVPDを実装して、すべてのクエリに述語が追加され、削除されていない行のみを追加できるようにする述語を追加します。 http://download.Oracle.com/docs/cd/E11882_01/server.112/e16508/cmntopc.htm#CNCPT62345

0
adsm

@AdditionalCriteria( "this.status <> 'deleted'")

これを@entityの上に置きます

http://wiki.Eclipse.org/EclipseLink/Examples/JPA/SoftDelete

0
Kalpesh Soni