データベースにレコードがあり、管理者と通常のユーザーの両方が更新できるとしましょう。
誰でも、この表のすべての変更をバージョン管理する方法/アーキテクチャを提案して、レコードを前のリビジョンにロールバックできるようにすることができます。
管理者とユーザーが更新できるFOO
テーブルがあるとします。ほとんどの場合、FOOテーブルに対してクエリを作成できます。幸せな日々。
次に、FOO_HISTORY
テーブルを作成します。これには、FOO
テーブルのすべての列があります。主キーは、FOOにRevisionNumber列を加えたものと同じです。 FOO_HISTORY
からFOO
への外部キーがあります。 UserIdやRevisionDateなど、リビジョンに関連する列を追加することもできます。すべての*_HISTORY
テーブルにわたって(つまり、Oracleシーケンスまたは同等のものから)常にRevisionNumbersを増やします。 1秒に1つの変更しかないことに依存しないでください(つまり、RevisionDate
を主キーに入れないでください)。
これで、FOO
を更新するたびに、更新を行う直前に、古い値をFOO_HISTORY
に挿入します。プログラマがこのステップを誤って見逃すことがないように、設計の基本レベルでこれを行います。
FOO
から行を削除する場合、いくつかの選択肢があります。すべての履歴をカスケードして削除するか、FOO
に削除済みのフラグを立てて論理削除を実行します。
このソリューションは、現在の値に主に興味があり、履歴にたまにしか興味がない場合に適しています。常に履歴が必要な場合は、有効な開始日と終了日を入力し、FOO
自体にすべてのレコードを保持できます。すべてのクエリは、それらの日付を確認する必要があります。
BIの世界では、バージョン管理するテーブルにstartDateとendDateを追加することでこれを実現できます。テーブルに最初のレコードを挿入すると、startDateが入力されますが、endDateはnullです。 2番目のレコードを挿入すると、最初のレコードのendDateも2番目のレコードのstartDateで更新されます。
現在のレコードを表示するには、endDateがnullのレコードを選択します。
これはタイプ2と呼ばれることもあります- 緩やかに変化する次元 。 TupleVersioning も参照してください
SQL 2008にアップグレードします。
SQL 2008でSQL変更追跡を使用してみてください。タイムスタンプと廃棄の列ハッキングの代わりに、この新しい機能を使用してデータベース内のデータの変更を追跡できます。
この問題に対する良い解決策の1つを追加したいのは、 Temporal database を使用することです。多くのデータベースベンダーは、この機能をすぐに使用できるようにするか、拡張機能を介して提供しています。 PostgreSQLで temporal table 拡張機能を使用できましたが、他の拡張機能もあります。データベースのレコードを更新するたびに、データベースはそのレコードの前のバージョンも保持します。
2つのオプション:
SQLトリガーを介してSQLテーブルで監査を実行できます。トリガーから、2つの特別なテーブルにアクセスできます( 挿入および削除 )。これらのテーブルには、テーブルが更新されるたびに挿入または削除された正確な行が含まれます。トリガーSQLでは、これらの変更された行を取得して監査テーブルに挿入できます。このアプローチは、監査がプログラマーに対して透過的であることを意味します。それらからの努力や実装知識を必要としません。
このアプローチの追加のボーナスは、SQL操作がデータアクセスDLLを介して行われたか、手動のSQLクエリを介して行われたかに関係なく監査が行われることです。 (監査はサーバー自体で実行されるため)。
あなたはどのデータベースを言っていない、そして私は投稿タグにそれを見ない。 Oracleの場合は、Designerに組み込まれているアプローチ journal tables を使用することをお勧めします。他のデータベース用の場合は、基本的に同じ方法をお勧めします...
別のDBに複製したい場合、または単に理解したい場合、それが機能する方法は、テーブルには同じフィールド仕様の通常のデータベーステーブルだけでなくシャドウテーブルも作成されることです、いくつかの追加フィールド:最後に実行されたアクション(文字列、挿入の場合は「INS」、更新の場合は「UPD」、削除の場合は「DEL」)、アクションが実行された日時、および実行されたユーザーのIDそれ。
トリガーを介して、everyテーブル内の任意の行に対するアクションは、新しい値、実行されたアクション、いつ、およびどのユーザーによって。行を削除することはありません(少なくとも過去数か月間は)。はい、大きくなり、数百万行になりますが、anyレコードの値はで簡単に追跡できますジャーナリングが開始された後、または古いジャーナル行が最後にパージされてからの任意の時点、および最後の変更を行った人。
Oracleでは、必要なものはすべてSQLコードとして自動的に生成されます。必要なのは、コンパイル/実行するだけです。そして、それを検査するための基本的なCRUDアプリケーション(実際には「R」のみ)が付属しています。
私も同じことをしています。授業計画用のデータベースを作成しています。これらの計画には、アトミックな変更バージョン管理の柔軟性が必要です。言い換えれば、レッスンプランの変更はどれほど小さくても許可する必要がありますが、古いバージョンもそのまま維持する必要があります。これにより、レッスン作成者は、生徒が使用している間にレッスン計画を編集できます。
それが機能する方法は、学生がレッスンを行うと、彼らの結果は完了したバージョンに添付されるということです。変更が行われた場合、結果は常にバージョンを指します。
このように、レッスン条件が削除または移動されても、結果は変わりません。
現在これを行う方法は、1つのテーブルですべてのデータを処理することです。通常、idフィールドは1つだけですが、このシステムでは、idとsub_idを使用しています。 sub_idは、更新および削除の間、常に行にとどまります。 IDは自動インクリメントされます。レッスン計画ソフトウェアは最新のsub_idにリンクします。学生の結果はIDにリンクします。また、変更が発生したタイミングを追跡するためのタイムスタンプも含めましたが、バージョン管理を処理する必要はありません。
変更したら、テストしたら、前述のendDate nullアイデアを使用できます。私のシステムでは、最新バージョンを見つけるには、max(id)を見つける必要があります。もう一方のシステムは、endDate = nullを探すだけです。別の日付フィールドがあることで利益が出るかどうかはわかりません。
私の2セント。
@WWながら。別の方法は、バージョン列を作成し、すべてのバージョンを同じテーブルに保持することです。
1つのテーブルアプローチの場合:
outer join
よりも大きな厄介な処理を行います。リビジョン番号を使用するouter join
メソッドのSQLの例は次のとおりです。
SELECT tc.*
FROM text_content tc
LEFT OUTER JOIN text_content mc ON tc.path = mc.path
AND mc.revision > tc.revision
WHERE mc.revision is NULL
AND tc.path = '/stuff' -- path in this case is our natural id.
悪いニュースは、上記のouter join
が必要であり、外部結合が遅くなる可能性があることです。幸いなことに、新しいエントリを作成する方が、トランザクションを使用せずに1回の書き込み操作で実行できるため、理論的には安価です(データベースがアトミックであると仮定)。
'/stuff'
の新しいリビジョンを作成する例は次のとおりです。
INSERT INTO text_content (id, path, data, revision, revision_comment, enabled, create_time, update_time)
(
SELECT
(md5(random()::text)) -- {id}
, tc.path
, 'NEW' -- {data}
, (tc.revision + 1)
, 'UPDATE' -- {comment}
, 't' -- {enabled}
, tc.create_time
, now()
FROM text_content tc
LEFT OUTER JOIN text_content mc ON tc.path = mc.path
AND mc.revision > tc.revision
WHERE mc.revision is NULL
AND tc.path = '/stuff' -- {path}
)
古いデータを使用して挿入します。これは、1つの列のみを更新し、楽観的なロックやトランザクションを避けたい場合に特に役立ちます。
フラグアプローチと履歴テーブルアプローチでは、two行を挿入/更新する必要があります。
outer join
リビジョン番号アプローチのもう1つの利点は、トリガーが本質的に上記のような処理を行う必要があるため、後でトリガーを使用していつでも複数テーブルアプローチにリファクタリングできることです。
Alokが提案したAudit table
上記の投稿で説明したいと思います。
このスキーマレスの単一テーブル設計をプロジェクトに採用しました。
スキーマ:
このテーブルは、各テーブルの履歴レコードをすべて一度に保持し、完全なオブジェクト履歴を1つのレコードに保持できます。このテーブルは、データが変更され、ターゲット行の古い値と新しい値のスナップショットを保存するトリガー/フックを使用して作成できます。
この設計の長所:
この設計の短所: