データベースにレコードがあり、管理者と通常のユーザーの両方が更新を実行できるとしましょう。
レコードを以前のリビジョンにロールバックできるように、このテーブルのすべての変更をバージョン管理する方法について、誰かが良いアプローチ/アーキテクチャを提案できますか?
データベースにレコードがあり、管理者と通常のユーザーの両方が更新を実行できるとしましょう。
レコードを以前のリビジョンにロールバックできるように、このテーブルのすべての変更をバージョン管理する方法について、誰かが良いアプローチ/アーキテクチャを提案できますか?
FOO
管理者とユーザーが更新できるテーブルがあるとします。ほとんどの場合、FOO テーブルに対してクエリを作成できます。幸せな日々。
次に、FOO_HISTORY
テーブルを作成します。これには、テーブルのすべての列がありFOO
ます。主キーは、FOO に RevisionNumber 列を加えたものと同じです。FOO_HISTORY
からへの外部キーがありますFOO
。UserId や RevisionDate などのリビジョンに関連する列を追加することもできます。*_HISTORY
すべてのテーブルにわたって増え続ける方法で RevisionNumbers を入力します (つまり、Oracle シーケンスまたは同等のものから)。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 Change Tracking を使用してみてください。タイムスタンプや廃棄列ハックの代わりに、この新しい機能を使用して、データベース内のデータの変更を追跡できます。
2 つのオプション:
SQL トリガーを介して SQL テーブルの監査を実行できます。トリガーから、2 つの特別なテーブル (挿入および削除) にアクセスできます。これらのテーブルには、テーブルが更新されるたびに挿入または削除された正確な行が含まれています。トリガー SQL では、これらの変更された行を取得して、監査テーブルに挿入できます。このアプローチは、監査がプログラマに対して透過的であることを意味します。彼らの努力や実装に関する知識は必要ありません。
このアプローチの追加の利点は、SQL 操作がデータ アクセス DLL を介して行われたか、手動の SQL クエリを介して行われたかに関係なく、監査が行われることです。(監査はサーバー自体で実行されるため)。
あなたはどのデータベースとは言いませんし、投稿タグにも表示されません。Oracle の場合は、Designer に組み込まれているアプローチ、つまりジャーナル テーブルを使用することをお勧めします。他のデータベースの場合は、基本的に同じ方法をお勧めします...
別のDBに複製したい場合、または単に理解したい場合の仕組みは、テーブルの場合、同じフィールド仕様を持つ通常のデータベーステーブルだけであるシャドーテーブルも作成されることです。 、およびいくつかの追加フィールド: 最後に実行されたアクション (文字列、典型的な値は挿入の場合は「INS」、更新の場合は「UPD」、削除の場合は「DEL」)、アクションが実行された日時、実行者のユーザー IDそれ。
トリガーを介して、テーブル内の任意の行に対するすべてのアクションは、ジャーナル テーブルに新しい行を挿入し、新しい値、どのアクションがいつ、どのユーザーによって実行されたかを示します。行を削除することはありません (少なくとも過去数か月間)。はい、それは大きくなり、簡単に数百万行になりますが、ジャーナリングが開始されてから、または古いジャーナル行が最後にパージされてから、いつでも任意のレコードの値を簡単に追跡でき、最後に変更を加えたのは誰ですか.
Oracle では、必要なものはすべて SQL コードとして自動的に生成され、コンパイル/実行するだけで済みます。また、それを検査するための基本的な CRUD アプリケーション (実際には "R" のみ) が付属しています。
@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 つの列のみを更新し、楽観的ロックやトランザクションを回避したい場合に特に便利です。
フラグ アプローチと履歴テーブル アプローチでは、2 つの行を挿入/更新する必要があります。
リビジョン番号アプローチのもう 1 つの利点はouter join
、トリガーは基本的に上記のようなことを行う必要があるため、後でトリガーを使用していつでも複数テーブル アプローチにリファクタリングできることです。
私も同じことをしています。授業計画のデータベースを作っています。これらの計画には、アトミックな変更のバージョン管理の柔軟性が必要です。言い換えれば、どんなに小さな変更であっても、授業計画への各変更を許可する必要がありますが、古いバージョンもそのままにしておく必要があります。こうすることで、レッスンの作成者は、生徒がレッスン プランを使用している間にレッスン プランを編集できます。
それが機能する方法は、学生がレッスンを完了すると、その結果が完了したバージョンに添付されるというものです。変更が行われた場合、その結果は常にそのバージョンを指します。
このように、レッスン基準が削除または移動されても、結果は変わりません。
私が現在これを行っている方法は、すべてのデータを 1 つのテーブルで処理することです。通常は id フィールドを 1 つだけ使用しますが、このシステムでは id と sub_id を使用しています。sub_id は、更新と削除を通じて、常に行に残ります。ID は自動インクリメントされます。授業計画ソフトウェアは、最新の sub_id にリンクします。学生の結果はIDにリンクされます。変更がいつ発生したかを追跡するためのタイムスタンプも含めましたが、バージョン管理を処理する必要はありません。
一度テストしたら、変更する可能性があることの 1 つは、前述の endDate null のアイデアを使用する可能性があることです。私のシステムでは、最新バージョンを見つけるには、max(id) を見つける必要があります。もう一方のシステムは endDate = null を探すだけです。利益が別の日付フィールドを持っているかどうかはわかりません。
私の2セント。