ある王国国家には恐るべき王が住んでいました。 皇帝には日々のレポートを準備する多くのボイヤーがいました。何人の軍隊が奉仕していたか、王の財務省が素晴らしいかどうか、今年小麦が生まれたかどうか、牛乳を与える牛の数です。
王はボイヤの名前を、彼女を数えた各ツシフェルカの反対側の報告書に反映させたいと思っていました。 はい、彼は大工に海外の絵に従ってメカニズムを構築するように命じたので、ボイヤーのいずれかが間違いを犯した場合、彼はラックにいて、偽の数字を彼に返します。
Oracle Flashbackが呼ばれる大工の図面の手に回されたが、疑いました。 実際、デフォルトで3分に設定されているUNDO_RETENTIONレバーは、何も失わず、ギガバイトの余分なデータにdrれないように、結果なしで最大1年まで締めることができます。 独自のメカニズムを組み立てることにしました。
物語は終わった後、メカニズムの厳しい実装。
メインテーブルは幅が広く、レコードID、相互に関係のない100の情報フィールド、およびサービス情報が含まれています。ラインが作成または変更された日付、Webインターフェイスを介してラインを作成または編集したボイヤーの識別子です。 異なる間隔の各ボイヤーは、彼に知られているデータでラインを埋めるか、すでに他のボイヤーで満たされたフィールドを編集します。
create table MAIN_TABLE (
ID number primary key ,
INFO_FIELD1 number,
INFO_FIELD2 varchar2(100),
INFO_FIELD3 date ,
…
CREATE_DATE date default sysdate,
CREATE_USER_ID number NOT NULL ,
UPDATE_DATE date ,
UPDATE_USER_ID number
)
partition by range ( CREATE_DATE ) …
;
変更履歴テーブルは、メインテーブルの構造をほぼ完全に繰り返しますが、CREATE_DATE、CREATE_USER_IDフィールドが削除されている点が異なります。これらのフィールドは変更されないため、履歴を保存しても意味がありません。 さらに、履歴テーブルは政府のプロセスに関与していないため、キーは必要ありません。 パーティション化は、行が作成された日付から変更の日付に転送されます。これにより、編集内容が古いセクションを移動または削除できます。
create table HISTORY_TABLE (
ID number,
INFO_FIELD1 number,
INFO_FIELD2 varchar2(100),
INFO_FIELD3 date ,
…
UPDATE_DATE date ,
UPDATE_USER_ID number
)
partition by range ( UPDATE_DATE ) …
;
ここで、メインテーブルを変更する場合、編集したフィールドの以前の値を履歴テーブルに保存して、新しい行を作成する必要があります。 したがって、メインテーブルに現在の情報が格納されます。 履歴テーブルでは、メインテーブルの各行IDに対して、同じIDを持つ複数の行があり、UPDATE_DATEによって異なります。
結果1 :履歴テーブルの行の数は、メインへの変更の数と同じです。
結果2 :行がメインテーブルに作成されたが、編集されていない場合、そのIDは履歴テーブルにありません。
履歴テーブルを埋めるために、大工はトリガーを使用しました-最も信頼できる方法です。 他のすべての中で変更されたフィールドを強調するために、各フィールドの古い値と新しい値を比較し始めました。 変更はありません-NULLを書き込みます。 これはボリュームにプラスの効果があります。前回の編集以降に変更されていないデータを複製する必要はありません。
しかし、運が悪いと、有害なボヤールはすでに満たされたフィールドを消去することができます。 そのような場合は個別に処理する必要があり、NULLを書き込むことはできません。これは変更がないことです。 NULLの代わりに、履歴内の論理的に不可能な値を置き換え始めました。 たとえば、彼自身の自由意志のある世界の単一の財務担当者は、tsarへの報告書で財務の負の価値を示すことはなく、Webインターフェイスはこれを許可しません。 値-99(日付タイプのフィールドの場合は1970年1月1日)をフィールドの操作消去コードと見なすと、調和が復元されます。
create or replace trigger TRG_MAIN_BEFORE_UPD
before update on MAIN_TABLE
for each row
declare
HIST HISTORY_TABLE %ROWTYPE;
begin
HIST.ID := : OLD .ID;
if (: OLD .UPDATE_DATE is null ) then
-- -
HIST.UPDATE_DATE := : OLD .CREATE_DATE;
HIST.UPDATE_USER_ID := : OLD .CREATE_USER_ID;
HIST.INFO_FIELD1 := : OLD .INFO_FIELD1;
HIST.INFO_FIELD2 := : OLD .INFO_FIELD2;
HIST.INFO_FIELD3 := : OLD .INFO_FIELD3;
else
-- - ,
-- , NULL
-- NULL,
HIST.UPDATE_DATE := : OLD .UPDATE_DATE;
HIST.UPDATE_USER_ID := : OLD .UPDATE_USER_ID;
if nvl(: NEW .INFO_FIELD1, -99) <> nvl(: OLD .INFO_FIELD1, -99) then
HIST.INFO_FIELD1 := nvl(: OLD .INFO_FIELD1, -99);
end if ;
if nvl(: NEW .INFO_FIELD2, '-99' ) <> nvl(: OLD .INFO_FIELD2, '-99' ) then
HIST.INFO_FIELD2 := nvl(: OLD .INFO_FIELD2, '-99' );
end if ;
if nvl(: NEW .INFO_FIELD3, to_date( '01.01.1970' , 'dd.mm.yyyy' )) <>
nvl(: OLD .INFO_FIELD3, to_date( '01.01.1970' , 'dd.mm.yyyy' )) then
HIST.INFO_FIELD3 :=
nvl(: OLD .INFO_FIELD3, to_date( '01.01.1970' , 'dd.mm.yyyy' ));
end if ;
end if ;
--
insert into HISTORY_TABLE (ID, UPDATE_DATE, UPDATE_USER_ID,
INFO_FIELD1, INFO_FIELD2, INFO_FIELD3)
values (HIST.ID, HIST.UPDATE_DATE, HIST.UPDATE_USER_ID,
HIST.INFO_FIELD1, INFO_FIELD2, HIST.INFO_FIELD3);
end TRG_MAIN_BEFORE_UPD;
* This source code was highlighted with Source Code Highlighter .
履歴表から選択すると、誰が貢献したかをすぐに確認できます。
select *
from HISTORY_TABLE
where ID = 1
oder by UPDATE_DATE;
---------------------------------------------------------------------------
ID | INFO_FIELD1 | INFO_FIELD2 | INFO_FIELD3 | UPDATE_DATE | UPDATE_USER_ID
---------------------------------------------------------------------------
1 12 AAA 05.11.2010 1
1 -99 01.11.2010 06.11.2010 2
1 BBB 07.11.2010 3
エントリを作成するとき、ユーザー1は最初の2つの情報フィールドを完成させ、3番目のフィールドを無視(空のまま)します。 翌日、ユーザー2は最初のフィールドの値を消去し(明らかに誤っていると思われる)、3番目のフィールドに値を入力しました。 1日後、ユーザー3は2番目のフィールドを修正しましたが、最初のフィールドは空のままでした。 これらが歴史的な編集であることを忘れないでください。したがって、エディター07.11.2010は最後ではありません。 メインテーブルには、このレコードの現在のバージョンが格納されます。これは、以前のバージョンとは大幅に異なる場合があります。
明らかに、IDと変更日による検索は、履歴テーブルで最も人気があります。 これらのフィールドにローカルインデックスを作成する価値があります。
国王のニーズを満たすために、任意の期間のデータを復元する機能がメカニズムに追加されました。 これを行うには、履歴テーブルの穴(NULL)をその期間に関連する値に置き換えます。 -99の不可能な値はNULLに置き換えられます。 大工は、便宜上、プレゼンテーションを行いました。
create or replace view V_HISTORY_RESTORE (
ID,
INFO_FIELD1,
INFO_FIELD2,
INFO_FIELD3,
UPDATE_DATE,
UPDATE_USER_ID
)
as
select
ID,
case
when LAST_VALUE(INFO_FIELD1 ignore NULLS) over (partition by ID order by UPDATE_DATE) = -99
then NULL
else
LAST_VALUE(INFO_FIELD1 ignore NULLS) over (partition by ID order by UPDATE_DATE)
end as INFO_FIELD1,
case
when LAST_VALUE(INFO_FIELD2 ignore NULLS) over (partition by ID order by UPDATE_DATE) = '-99'
then NULL
else
LAST_VALUE(INFO_FIELD2 ignore NULLS) over (partition by ID order by UPDATE_DATE)
end as INFO_FIELD2,
case
when LAST_VALUE(INFO_FIELD3 ignore NULLS) over (partition by ID order by UPDATE_DATE) = to_date( '01.01.1970' , 'dd.mm.yyyy' )
then NULL
else
LAST_VALUE(INFO_FIELD3 ignore NULLS) over (partition by ID order by UPDATE_DATE)
end as INFO_FIELD3,
UPDATE_DATE,
UPDATE_USER_ID
from HISTORY_TABLE;
* This source code was highlighted with Source Code Highlighter .
かなり面倒ですが、すぐに動作します。 ここでは、NULL_Sを無視するLAST_VALUE分析関数を使用します。これは、NULLでない場合、前の(日付でソートされた)行からフィールド値を取得します。 前の行でNULLである場合-空でない値に出会うまで上に移動します。
ビューから行を選択して履歴を復元すると、特定の時点でのメインテーブルの状態を確認できます。
select *
from V_HISTORY_RESTORE
where ID = 1
oder by UPDATE_DATE;
---------------------------------------------------------------------------
ID | INFO_FIELD1 | INFO_FIELD2 | INFO_FIELD3 | UPDATE_DATE | UPDATE_USER_ID
---------------------------------------------------------------------------
1 12 AAA 05.11.2010 1
1 AAA 01.11.2010 06.11.2010 2
1 BBB 01.11.2010 07.11.2010 3
ここで、行を復元するには、編集の日付のみを選択し、メインテーブルを更新するだけで十分です。 メインテーブルが更新されるため、履歴テーブルでもう1行取得されます。 これは、将来、過去に戻る前の瞬間に戻ることができることを意味します(フレーズが矛盾しているように聞こえる場合は、映画「Back to the Future」を確認してください)。
不良債権者の貢献を嫌うために、履歴テーブルから行を削除し、不十分な編集を含む行のメインテーブルで行の以前の状態を復元するだけで十分です。
これで物語は終わりです。 王は喜んだ。
このメカニズムは機能し、どのフィールド、どのテーブルを保存するかを柔軟に構成します。 ストーリーはより管理しやすくなっています。 必要に応じて、キングによって認定された不要な行編集を削除して、ストレージスペースを解放できます。
1つ悪いのは、大工がすべてを切ることにうんざりしていることです。 そして、左利きの人が来て、メインテーブルにフィールドを追加し、歴史を忘れる危険があります。 曲がった手に対する防御はありません。
スナックの記事について
バージョン管理とデータ履歴
PS私の自転車。 私は革新的であるふりはしませんが、グーグル検索では類似物は見つかりませんでした。 記事の写真はインターネット上で掘られています。