データベースで削除をどのように処理する必要がありますか?


44

ユーザーが気を変えて、削除されたレコードを回復できるように、Webアプリケーションに「削除解除」機能を実装したいと思います。これを実装する方法についての考えは?私が検討したいくつかのオプションは、実際に問題のレコードを削除し、別の監査テーブルに変更を保存するか、レコードを削除せず、ブールの「deleted」列を使用して削除済みとしてマークすることです。後者のソリューションでは、通常の状況で「削除された」レコードを無視するために追加のアプリケーションロジックが必要になりますが、アプリケーション側でレコードの回復を実装するのがはるかに簡単になります。


2番目のケースでは、フラグを立てたレコードを適切な期間が経過した後に削除または移動する必要があることを忘れていました。
アビー

どのデータベースを使用していますか?
エヴァンキャロル

Temporal Tableは、SQL Server 2016以降に最適なソリューションです。
サミール

回答:


37

ええ、私は間違いなく2番目のオプションを選びますが、日付フィールドにもう1つフィールドを追加します。

あなたは追加します:

delete       boolean
delete_date  timestamp

削除を取り消すアクションの時間を与えることができます。

時間が1時間未満の場合は、削除を取り消すことができます。

削除されたエントリを本当に削除するには、deleteをtrueに設定し、1時間を超える時間ですべてのエントリをクリーンアップするストアドプロシージャを作成し、24時間ごとに実行されるcronタブとして配置します。

時間は単なる例です。


または、cleanedこのレコードに関連付けられたデータが適切に、包括的に削除されたことを示す別のフラグ- または何か-を使用することもできます。このレコードcleanedは、trueでない限り復元できません。trueの場合、復元できません。
ガウラフ

14
これが一般的なアプローチです。通常deleted_atdeleteブールの意味とdelete_dateタイムスタンプの両方を保持する1つのフィールドを使用します。場合deleted_atされたNULL場合の取り扱いdeleteであるFALSEdelete_dateされNULLdeleted_atケースは、タイムスタンプハンドルを含むdeleteあるTRUEし、delete_dateあなたの時間、ストレージ、アプリケーション・ロジックを保存し、タイムスタンプが含まれています。
ジュリアン

1
ブール値と日付フィールドが好きです。削除ロジックの実装方法に応じて、「削除」されたレコードの日付と一意のキーを保持する個別のテーブルを作成することもできます。ストアドプロシージャはこれを簡単にします。必要な行あたりの追加スペースは、8ビット以上に対して1ビットになります。また、ソーステーブルに触れることなく、1日あたりの削除についてレポートすることもできます。
AndrewSQL

注:deleteはMySQLの予約語です。
ジェイソンリカード

deletedフィールドのフィルター選択されたインデックスは、削除されていない行を照会する際のパフォーマンスを大幅に改善できることを忘れないでください
Ross Presser

21

私たちのアプリケーションでは、とにかくユーザーのリクエストに応じて何も削除しませ(クライアントは規制された環境にあり、何でも削除すると法的な問題が発生する可能性があります)。

古いバージョンは、追加のバージョン識別子(DBが十分な粒度の時間値をサポートする場合のタイムスタンプ、整数バージョン番号)を除いて同一の別個の監査テーブル(some_tableのテーブルはsome_table_auditとも呼ばれます)に保持しますまたは一般的な監査テーブルへの外部キーであるUUIDなど)、トリガーによって監査テーブルを自動的に更新します(したがって、レコードを更新するすべてのコードで監査要件を認識させる必要はありません)。

こちらです:

  • 削除操作は単純な削除です-追加のコードを追加する必要はありません(実際に削除されていない場合でも、削除する行を要求した人を記録することができます)
  • 挿入と更新も同様に簡単です
  • 「通常の」行を古いバージョンに戻すだけで、削除の取り消しまたは元に戻すことができます(監査トリガーが再度起動され、監査証跡表にもこの変更が反映されます)
  • 最後のバージョンの削除を取り消すだけでなく、過去のバージョンを確認したり元に戻したりする機会を提供できます。
  • 「削除済みとしてマークされていますか?」を追加する必要はありません。問題のテーブルを参照するすべてのコードポイント、または行を削除/更新するすべてのコードポイントに対する「監査コピーの更新」ロジックを確認します(ただし、監査テーブルで削除された行の処理を決定する必要があります。そこに各バージョンの削除済み/未通知フラグがあるため、レコードが削除され、後で削除されても履歴に穴はありません)
  • 監査コピーを別のテーブルに保存することは、それらを異なるファイルグループに簡単に分割できることを意味します。

整数のバージョン番号の代わりに(または同様に)タイムスタンプを使用する場合、これを使用して、必要に応じて一定時間後に古いコピーを削除できます。しかし、最近のディスク容量は比較的安いので、古いデータを削除する理由がない限り(つまり、Xか月/年後にクライアントデータを削除する必要があるというデータ保護規則)、そうしません。


この答えは数年前からあり、それ以降、この種の計画に影響を与える可能性のあるいくつかの重要な事項が変更されました。私は大規模な詳細には立ち入らないが、今日これを読んでいる人々の便宜のために。

  • SQL Server 2016では、この作業の多くを行う「システムバージョンのテンポラルテーブル」が導入されました。さらに、歴史的なクエリの構築と保守を容易にするための優れた構文シュガーが提供され、スキーマの変更のサブセットを調整しますベースおよび履歴テーブル。それらには警告がないわけではありませんが、この種の目的のための強力なツールです。同様の機能は、他のDBシステムでも使用できます。

  • データ保護法の変更、特にGDPRの導入により、データを完全に削除する時期の問題が大幅に変わる可能性があります。検討する際に、人々の権利を尊重する必要性(一般に、また関連する法律で具体的に規定されているものの両方)に対して、後の監査目的に役立つ(または実際に法的に必要とされる)データを削除しないことのバランスを検討する必要がありますあなたのデザイン。これはシステムのバージョン管理されたテンポラルテーブルで問題になる可能性があります。スキーマの短期的な変更なしで個人データをパージするために履歴を変更することはできません。


列の削除と名前変更をどのように処理しますか?すべてをヌル可能に設定しますか?
スティン

1
@Stijn:構造が変更されることはあまりないので、あまり出てきません。コロンは通常、実稼働環境に存在すると削除されることはありません-使用を停止する場合は、NULLを停止する制約を削除するだけです(または、「マジックバリュー」を使用して制約を処理するデフォルトを追加しますが、それはより汚い感じがします)他のコードでそれらの参照を停止します。名前の変更:新規追加、古い使用の停止、必要に応じて古いデータを新しいデータにコピーします。列の名前を変更する場合は、基本テーブルと監査テーブルの両方に同じ変更が同時に行われるようにしてください。
デビッドスピレット

9

ブール削除された列を使用すると、テーブルが大きくなり始めて本当に大きくなった場合、問題が起こり始めます。削除された列を週に一度(仕様に応じて多かれ少なかれ)別のテーブルに移動することをお勧めします。そうすれば、素敵な小さなアクティブテーブルと、時間の経過とともに収集されたすべてのレコードを含む大きなアクティブテーブルができます。


7

別のテーブルに行きます。Ruby on Railsにはacts_as_versionedプラグインがあります。これは、基本的_versionに、更新する前にpostfixを使用して行を別のテーブルに保存します。正確な動作は必要ありませんが、ケースでも機能するはずです(削除する前にコピーしてください)。

@Spredzyのdelete_dateように、X時間/日/何でも復元されていないレコードをプログラムでパージできるように列を追加することもお勧めします。


4

この問題で内部的に使用するソリューションは、オブジェクトの特定の状態に対してハードコードされた値を含むステータス列を使用することです:削除済み、アクティブ、非アクティブ、オープン、クローズ、ブロック-アプリケーションで使用される何らかの意味を持つ各ステータス。dbの観点からは、オブジェクトを削除するのではなく、ステータスを変更し、オブジェクトテーブルの各変更の履歴を保持するだけです。


3

「後者のソリューションでは、「削除された」レコードを無視するために追加のアプリケーションロジックが必要になる」と言うとき、単純なソリューションは、それらを除外するビューを持つことです。


それは単なる見方の問題ではありません。セットで実行されている操作では、「削除された」レコードを除外する必要があります。
アビー

2

Spredzyが提案したものと同様に、すべてのアプリケーションで削除にタイムスタンプフィールドを使用します。タイムスタンプが設定されていることは、レコードが削除されたことを示すため、ブール値は不要です。このようにしてAND (deleted IS NULL OR deleted = 0)、モデルが削除されたレコードを含めるよう明示的に要求しない限り、PDOは常にselectステートメントに追加します。

現在、ブロブまたはテキストを含むテーブル以外のガベージコレクションは行っていません。レコードが十分に正規化されている場合、スペースは重要ではありません。deletedフィールドにインデックスを付けると、選択速度への影響が制限されます。


0

代わりに、ユーザー(および開発者)に責任を負わせ、「本当によろしいですか?」、「間違いなくよろしいですか?」の順序で進むこともできます。そして「あなたは絶対に、よく、本当に確信していますか?」レコードが削除される前の質問。ややファセット的ですが、検討する価値があります。


0

「DeletedDate」などの列が含まれるテーブル行を表示するのに慣れていて、それらが好きではありません。「削除済み」の概念は、そもそもエントリを作成すべきではないということです。実際には、それらをデータベースから削除することはできませんが、ホットデータでそれらを使用したくありません。論理的に削除された行は、誰かが削除されたデータを見たい場合を除き、定義上、コールドデータです。

さらに、作成されるすべてのクエリはそれらを特に除外する必要があり、インデックスもそれらを考慮する必要があります。

私が見たいのは、データベースアーキテクチャレベルとアプリケーションレベルでの変更です。「削除済み」というスキーマを作成します。各ユーザー定義テーブルには、「削除された」スキーマと同じものがあり、メタデータを保持する追加のフィールドがあります。これは、テーブルをいつ削除したかです。外部キーを作成する必要があります。

次に、削除は挿入と削除になります。最初に、削除される行が、対応する「削除された」スキーマに挿入されます。その後、メインテーブル内の問題の行を削除できます。ただし、線に沿ったどこかに追加のロジックを追加する必要があります。外部キー違反は処理できます。

外部キーは適切に処理する必要があります。行を論理的に削除するのは悪い習慣ですが、そのプライマリ/ユニークな行には、それを参照する他のテーブルの列があります。とにかくこれは起こらないはずです。通常のジョブでは、未亡人の行(外部キーが存在するにもかかわらず、主キーが他のテーブルに参照を持たない行。これはビジネスロジックです)を削除できます。

全体的な利点は、テーブル内のメタデータが削減され、パフォーマンスが向上することです。「deletedDate」列は、この行が実際にここにあるべきではないことを示していますが、便宜上、そこに残して、SQLクエリに処理させます。削除された行のコピーが「削除された」スキーマに保持されている場合、ホットデータを含むメインテーブルは、ホットデータの割合が高く(タイムリーにアーカイブされていると仮定)、不要なメタデータ列が少なくなります。インデックスとクエリでは、このフィールドを考慮する必要がなくなりました。行サイズが短いほど、ページにより多くの行を収めることができ、SQL Serverの動作が速くなります。

主な欠点は、操作のサイズです。現在、1つではなく2つの操作と、追加のロジックとエラー処理があります。そうしないと、単一の列を更新するよりも多くのロックが発生する可能性があります。トランザクションはテーブルのロックをより長く保持し、2つのテーブルが関係します。少なくとも私の経験では、実稼働データを削除することはめったに行われません。それでも、メインテーブルの1つでは、ほぼ1億のエントリの7.5%に「DeletedDate」列のエントリがあります。

質問への回答として、アプリケーションは「削除取り消し」に注意する必要があります。単に逆の順序で同じことを行う必要があります。「削除済み」スキーマからメインテーブルに行を挿入し、「削除済みスキーマ」から行を削除します。ここでも、エラー、外部キーの問題などを回避するために、いくつかの追加のロジックとエラー処理が必要です。

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