データベーステーブルに「レコードステータス」列を持つことは悪い習慣ですか?


12

最初に、ステータス列は、テーブル内のレコード(行)で表される実際のアイテムのステータスを反映するものではないことを明確にする必要があります。むしろ、レコード自体のステータスを表示することを目的としています。

これは、アクティブ/非アクティブとして、単純なようであるかのように複雑なことができます承認/削除された/ロック/保留/拒否などのステータスがマッピングで、ブール/短整数の列または単一文字列に保存することができるようにtrue/ 1=アクティブまたはA=承認済み。

基本的な考え方は、アプリケーションでごみ箱/ゴミのような回復をサポートすることです(データベースでシミュレートします)。ユーザーがレコードを「削除」できると思われるフロントエンドGUIまたはその他のインターフェイスがある場合、テーブル内のレコードは実際には削除されず、単にレコードのステータスが非アクティブまたは削除済みに変更されます。インターフェイスがレコードをフェッチするとき、ステータスがアクティブまたは承認済みであるという条件にのみ一致するレコードを常に取得します。

ユーザーがミスを犯し、「ユーザーの観点から」「削除された」レコードを回復する必要がある場合、DBAはレコードを簡単に修正してアクティブまたは承認済みに戻すことができます。そこ。または、インターフェイス自体で、ユーザーが削除されたレコードを別のビューで表示し、必要に応じてそれらを復元したり、完全に削除したりすることもできます(実際のレコードを削除します)。

私の質問:

  • これは良い習慣ですか、それとも悪い習慣ですか?
  • データの正規化に影響しますか?
  • 潜在的な落とし穴は何ですか?
  • 同じ目標を達成する代替方法はありますか?(ノートを参照してください)
  • 特定のステータスのデータに対してのみデータベースに一意の制約を適用するにはどうすればよいですか(ただし、他のステータスの重複をいくつでも許可します)。
  • データベースがネイティブで「ごみ箱」のような機能やテーブルトラッキング/リカバリを提供しないので、心配せずにインターフェイスで実際のレコードを削除できます。

注:別の履歴テーブルを維持することについて読みましたが、ストレージの観点からは悪く、トリガーを生成し、追跡テーブルのスキーマでトリガーを最新に保つ必要があるようです。


一意の制約(既に名前を付けた)の問題は、履歴テーブルがしばしば望ましい理由です-一意のキー制約を元のテーブルに保持し、履歴テーブルに追加しないでください。特定の(DB依存の)ストレージオプションを使用するため、多くの場合、ストレージの点で優れていますが、悪化することはありません。これらのテーブルがたくさんある場合、トリガーと履歴テーブルは手書きではなく、生成する必要があります。これにより、「最新」に保つ方法の問題が解決します。
ドックブラウン

回答:


5

私はこれを「ソフト削除」として知っています。本当にそうではないにせよ、レコードを「削除済み」としてマークするだけです。

これは良い習慣ですか、それとも悪い習慣ですか?

場合によります。
これがユーザーが必要とするものである場合、それはおそらく良いことです。しかし、ほとんどの場合、ほとんど利益をもたらさずにオーバーヘッドが追加されると主張します。

データの正規化に影響しますか?

いいえ、それはします、そのデータのあなたのインデックスに影響を与えます。
これらの行がクエリでできるだけ早く除外されるように、インデックスに「削除済み」列を含めるようにしてください。

潜在的な落とし穴は何ですか?

データはもう少し複雑になります。データの近くにあるすべてのものは、これらの余分な「実際には存在しない」レコードについて「知る」必要があります。または、これらの行を除外するビューをこれらのテーブルに作成し、これらのビューをたとえば選択ツールで使用する必要があります。

データベースのサイズが大きくなる場合があります。これらの行を実際に削除していない場合、それらはまだそこにあり、スペースを占有します。これは問題になる場合もあれば、そうでない場合もあります。特にインデックスにインデックスを含めているため、それらが消費するスペースが増加します。

同じ目標を達成する代替方法はありますか?(ノートを参照してください)

そうでもない

特定のステータスのデータに対してのみデータベースに一意の制約を適用するにはどうすればよいですか(ただし、他のステータスの重複をいくつでも許可します)。

簡単ではありません。宣言参照整合性(外部キー句)はこれを実装する最もクリーンな方法であり、レポートツールなどがこれらのルールを利用してテーブル間の関係を判断するのが簡単です。このようなルールは、「ステータス」に関係なく、すべてのレコードに適用されます(そして、それを回避する方法はありません)。

別の方法は、トリガーを使用することです。トリガーは、テーブル間の参照整合性を強制し、必要なすべての巧妙な条件付き処理を実行する手続き型コードのスニペットです。これは特定のケースには適していますが、宣言型RIの利点のほとんどは範囲外です。テーブルの間に(外部的に)検出可能な関係はありません。それはすべてトリガーに「隠された」ものです。

データベースがネイティブで「ごみ箱」のような機能やテーブルトラッキング/リカバリを提供しないので、心配せずにインターフェイスで実際のレコードを削除できます。

なぜだろう、彼らは?

結局、これらはデータベースであり、ファイルシステムやスプレッドシートではありません。

彼らがすること、彼らは非常に、非常にうまくやることができます。

彼らがしていないことは、おそらくあまり需要がなかったでしょう。


良い答えですが、代替オプションがあります。たとえば、行をバックアップテーブルに移動して、そこから回復することができます。バックアップテーブルには最小限のインデックスを設定できます。これにより、既存のアプローチで注意する問題(より大きなインデックス、テーブルのユーザーにとって混乱の可能性など)は最小限に抑えられますが、メンテナンスする別のテーブルがあるという事実が明らかに追加されます(そして、エントリが外部キー参照になっていることを意味します)。他にも多くのオプションがありますが、実際に思い浮かぶのは、すべてのカスタム実装であり、そのような場合にすべてのSQLデータベースによって提供される一般的なものではありません。
フランクホプキンス

9

それは実践です。それが良いか悪いかは、アプリケーションと、「削除を取り消す」ことを本当に必要とするか、本当にしたいかに大きく依存します。私は、システム内のすべてのテーブルのそのような列を配置する計画についてかなり疑っています。システム内のすべてのテーブルでundeleteを実装することを本当に気にすることはほとんどありません。また、実装が必要です。ほとんどの場合、1つのテーブルから1つの行を削除解除するのではなく、行を削除して関連テーブルを更新する子テーブルをウォークスルーする必要があります。

残りの質問のほとんどについては、実装に大きく依存しています。たとえば、Oracleはテーブルへのすべての変更を追跡するためのさまざまな方法を提供します。フラッシュバックデータアーカイブ(FDAはTotal Recallとも呼ばれます)は、行のすべてのバージョンの完全な履歴を維持するための最新のアプローチであり、実装のためのデータベース内アーカイブソフト削除パターン。他のデータベースは、パターンを実装する他の方法を提供する場合があります。データベースとソフト削除の実装方法に応じて、パフォーマンス、制約を実施できるかどうか、どのように実施するかなど、パフォーマンスにさまざまな影響があります。Oracleの場合、関数ベースのインデックスで多くのことができます。 SQL Serverでは、多くの場合、同様の目的でフィルター選択されたインデックスを使用できます。


Oracle Flashbackはまさに私が望むものにとって理想的なソリューションです。残念なことに、Oracle独自のものです。
ADTC

4

MRP / ERPシステムで「削除フラグ付き」フィールドを使用することは非常に一般的です。

たとえば、非アクティブとして販売されなくなった部品または在庫レコードをマークしたい場合がありますが、未処理の注文はまだ関連付けられています。レコードの実際の削除を行うと、まだ出荷されていない注文、まだ投稿されていない元帳エントリ、月末まで構築されない履歴テーブルなどに影響を与える可能性があります。他のテーブルに対する検証の。関係を介して削除をカスケードしている場合、実際の削除はさらに破壊的です。

代わりに、削除のフラグを立てることにより、レコードに意図の明確なマーカーを付け、後で関連するすべてのテーブルがそれを参照していないことを確認した場合、スケジュールされたタスクがレコードを削除できます。

顧客テーブルおよび他の「長期」テーブルのこの機能についても、同様のケースを作成できます。注文のようなより揮発性の高いテーブルでも意味がありますが、フラグの名前は「出荷済み」または「キャンセル済み」のようになります。これは同じ機能を提供します。この秒で削除しないで、パージプログラムのフラグとして使用して、将来レコードの削除を検証しようとします。


3

代替ソリューションとして、イベントソーシングを使用すると、テーブル構造を複雑にすることなく同様の目標を実現できますが、イベント履歴に永続化できるイベントに変更を書き込む必要があるため、データを変更するためのコードが少し複雑になります。これにより、いつでもデータベースを再作成できます。これは非常に便利な機能です。

(これが「履歴テーブル」の意味ではないと思います。これは、変更または削除されたレコードを、変更する前に別のテーブルに単にコピーすることを意味すると思います)


興味深いコンセプト。これをどのように実装できるかを検討します。
ADTC

1

私はこれらのユースケースのためにこのパターンを頻繁に見、使用します:

  • 現在有効な値のみを表示するメタデータ。たとえば、enabled = 1のドロップダウンリストで自動車メーカーのリストから選択するには、ID、VALUE、ENABLEDのテーブル値は1、「Ford」、1および2、「Edsel」、0、3、「Toyota」です。 、1はFordとToyotaの選択肢のみを提供します
  • ケースが一度に1つの状態にしかなれないというパラダイムがあるケース管理システムの場合。この場合、トグル列はCURRENTと呼ばれ、0または1の値がチェック制約によって強制されました。ケースが1つの状態から別の状態に移行すると、アプリケーションは古い状態のCURRENTフラグを0に、新しい状態を1に更新します

問題は、複数のアプリケーションまたはWebサービスがテーブルに書き込む場合にデータの整合性を強制することです。ケースの現在の状態が1つだけであることをどのように確認しますか?Justin Caveが指摘しているように、これは関数に基づいて仮想インデックスを作成することでOracleで実行できますが、元々は単純な概念に思えたものに対するこの余分なオーバーヘッドです。


1

レポートにデータを使用する予定がある場合は、この方法をお勧めします(十分な大きさのアプリケーションにはレポートが必要です)。

アプリケーションを高速化するために、データベースでレポートツールを実行させないでください。そのため、別のデータベースへのコピー/同期を行う必要があります。

recordStatus2つの状態のみを使用するACTIVECANCELLEDlastUpdatedOnタイムスタンプと組み合わせて使用します。私は通常、ビジネス上の意味を持つrecordStatusものよりも使用しますstatus

レポートデータベースをアプリケーションと同期するときに、フィルターを実行lastUpdatedOnして、レポート側でどのデータベースを置き換えるかを判断します。

報告側では私が持っていないrecordStatusか、lastUpdatedOn一般的に報告されることはないだろうので、フィールドを。そのため、CANCELLEDステータスが表示された場合、アクティブなレコードのみを持つようにレポート側からレコードを削除します。

これは、ほぼ完全な同期が必要なアーカイブやバックアップなど、他のタイプのストアに拡張できます。ただし、レポートはより一般的な目的です。

あなたの例を注意してくださいApprovedNewPendingそれはそれだけで、それは賢明なセンスのビジネスを作るところに行くべき意味のビジネスを持っているような一般的なフィールドとして置くのは良い考えではありません。

versionNoロックに関しては、レコードに楽観的なロックを提供する使用。

代わりに別のオプションrecordStatusrecordActiveあり、それをaとして保存します。booleanこれにより、スペースとインデックス作成が少なくなりますが、予測できない将来のニーズが心配です。

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