1

テーブルのレコードレベルのバージョン管理について、この投稿を行っていました。アーキテクチャが履歴テーブルの使用法を扱っていることに気付きました。ただし、私のシナリオではロールバックは必要ありませんが、特定の時点のレコードを取得する必要があります。これは、バージョン管理に単一のテーブルを使用する設計を試みた場所です。これは必要最小限のテーブル データであることに注意してください (制約、インデックスなどはありません)。列に group by 句が含まれているため、id に基づいてインデックスを作成する予定です。

たとえば、私はテーブル Test を持っています

id は識別子です。

modstamp はデータのタイムスタンプです (非 null)

上記の列に加えて、テーブルには簿記の列が含まれます

local_modstamp は、レコードが更新されたタイムスタンプです

del_modstamp は、レコードが削除されたときのタイムスタンプです

バックアップ中に、すべてのレコードがソースから取得され、レコードの値が local_modstamp = null および del_stamp = null になる場所に挿入されます。

id |modstamp                   |local_modstamp |del_modstamp |
---|---------------------------|---------------|-------------|
1  |2016-08-01 15:35:32 +00:00 |               |             |
2  |2016-07-29 13:39:45 +00:00 |               |             |
3  |2016-07-21 10:15:09 +00:00 |               |             |

レコードが取得されたら、データを処理するためのシナリオを次に示します (参照時間[ref_time]がプロセスが実行される時間であると仮定します)。

  1. 通常どおり挿入します。

  2. 更新: local_modstamp = ref_time で最新のレコードを更新します。次に、新しいレコードを挿入します。クエリは次のようになります: update test set local_modstamp = where id = and local_modstamp is not null and del_modstamp is not null insert into test values(...)

  3. 削除: 最新のレコードを del_modstamp = ref_time で更新します。update test set del_modstamp = where id = and local_modstamp is not null and del_modstamp is not null

この設計は、local_modstamp が null ではなく、del_modstamp が null ではない最新のレコードを取得することを目的としています。ただし、クエリ (最も内側のクエリ) を使用して特定の時点を取得しようとしている問題に遭遇しました。

select id, max(modstamp) from test where modstamp <= <ref_time> and (del_modstamp is null || del_modstamp <= <ref_time>) group by id;

テーブルの最新のレコードを識別するために null をプレースホルダーとして使用するという間違いを犯したようです (そうですか?)。既存の設計を使用して特定時点の記録を取得する方法はありますか?

そうでない場合、可能性のある解決策は、local_modstamp を最新のレコードに設定することだと思います。これには、更新の場合に max(local_modstamp) を使用してロジックを更新する必要があります。特定の時点のデータを取得するために、既存のアーキテクチャを維持できますか?

私は現在 SQL-Server を使用していますが、この設計は他のデータベース製品にも拡張される可能性があります。ベンダー固有のハックを使用する代わりに、より一般的なアプローチを使用してデータを取得するつもりです。

4

1 に答える 1

3

バージョン通常形の紹介。次の表を検討してください。

create table Entities(
    ID     int identity primary key,
    S1     [type],  -- Static data
    Sn     [type],  -- more static data
    V1     [type],  -- Volatile data
    Vn     [type]   -- more volatile data
);

静的データは、エンティティの有効期間中に変更されないデータ、または追跡を必要としないデータです。揮発性データの変更とそれらの変更を追跡する必要があります。

volatile 属性を別のテーブルに移動します。

create table EntityVersions(
    ID        int  not null,
    Effective date not null default sysdate(),
    Deleted   bit  not null default 0,
    V1        [type],
    Vn        [type],
    constraint PK_EntityVersions primary key( ID, Effective ),
    constraint FK_EntityVersionEntity foreign key( ID )
        references Entities( ID )
);

エンティティ テーブルには、揮発性属性が含まれなくなりました。

挿入操作は、静的データを含むマスター エンティティ レコードを作成し、一意の ID 値を生成します。その値は、揮発性データの初期値を持つ最初のバージョンを挿入するために使用されます。更新は通常、(静的な値が実際に変更されない限り) マスター テーブルには何も行わず、新しい揮発性データの新しいバージョンがバージョン テーブルに書き込まれます。既存のバージョン、特に最新または「現在の」バージョンは変更されないことに注意してください。新しいバージョンが挿入され、操作が終了します。

最新バージョン、または実際には任意のバージョンを「元に戻す」には、バージョン テーブルからそのバージョンを削除するだけです。

たとえば、次の属性を持つ Employees テーブル:

EmployeeNum, HireDate, FirstName, LastName, PayRate, Dept, PhoneExt

もちろん、EmployeeNum は、HireDate および FirstName とともに静的になります。PhoneExt は時々変更される可能性がありますが、私たちは気にしません。したがって、静的に指定されます。最終的なデザインは次のとおりです。

Employees_S
===========
  EmployeeNum (PK), HireDate, FirstName, PhoneExt

Employees_V
===========
  EmployeeNum (PK), Effective (PK), IsDeleted, LastName, PayRate, Dept

2016 年 1 月 1 日にサリー・スミスを採用しました。静的データが Employees_S に挿入され、EmployeeNum 値 1001 が生成されます。この値を使用して、最初のバージョンも挿入します。

Employees_S
===========
  1001, 2016-01-01, Sally, 12345

Employees_V
===========
  1001, 2016-01-01, 0, Smith, 35.00, Eng

3 月 1 日に、彼女は昇給します。

Employees_S
===========
  1001, 2016-01-01, Sally, 12345

Employees_V
===========
  1001, 2016-01-01, 0, Smith, 35.00, Eng
  1001, 2016-03-01, 0, Smith, 40.00, Eng

5 月 1 日に、彼女は結婚します。

Employees_S
===========
  1001, 2016-01-01, Sally, 12345

Employees_V
===========
  1001, 2016-01-01, 0, Smith, 35.00, Eng
  1001, 2016-03-01, 0, Smith, 40.00, Eng
  1001, 2016-05-01, 0, Jones, 40.00, Eng

同じエンティティのバージョンは、発効日が同じであってはならないという制限を除いて、互いに完全に独立していることに注意してください。

従業員 1001 の現在の状態を確認するには、次のクエリを実行します。

select  s.EmployeeNum, s.HireDate, s.FirstName, v.LastName, v.PayRate, v.Dept, s.PhoneExt
from    Employees_S s
join    Employees_V v
    on  v.EmployeeNum = s.EmployeeNum
    and v.Effective = ( select  Max( Effective )
                        from    Employees_V
                        where   EmployeeNum = v.EmployeeNum
                            and Effective <= SysDate() )
where   s.EmployeeNum = 1001
    and v.IsDeleted = 0;

これがクールな部分です。たとえば 2 月 11 日の従業員 1001 の状態を確認するには、次のクエリを実行します。

select  s.EmployeeNum, s.HireDate, s.FirstName, v.LastName, v.PayRate, v.Dept, s.PhoneExt
from    Employees_S s
join    Employees_V v
    on  v.EmployeeNum = s.EmployeeNum
    and v.Effective = ( select  Max( Effective )
                        from    Employees_V
                        where   EmployeeNum = v.EmployeeNum
                            and Effective <= '2016-02-11' )
where   s.EmployeeNum = 1001
    and v.IsDeleted = 0;

サブクエリの最後の行を除いて、同じクエリです。現在のデータと履歴データは同じテーブルにあり、同じステートメントでクエリされます。

ここにもう 1 つの優れた機能があります。現在は 7 月 1 日で、9 月 1 日にサリーがマーケティング部門に異動し、さらに昇給することがわかっています。書類の手続きはすでに済んでいます。新しいデータを挿入します。

Employees_S
===========
  1001, 2016-01-01, Sally, 12345

Employees_V
===========
  1001, 2016-01-01, 0, Smith, 35.00, Eng
  1001, 2016-03-01, 0, Smith, 40.00, Eng
  1001, 2016-05-01, 0, Jones, 40.00, Eng
  1001, 2016-09-01, 0, Jones, 50.00, Mkt

最後から 2 番目のバージョンは引き続き現在のバージョンとして表示されますが、9 月 1 日以降に実行された最初のクエリにはマーケティング データが表示されます。

これは、私がテック フェアで数回行ったプレゼンテーションのスライドです。クエリを含め、上記のすべてを実行する方法の詳細が含まれています。そして、ここにさらに詳細なドキュメントがあります。

于 2016-08-03T06:11:36.777 に答える