DB データの履歴管理設計

このページは、DB データの履歴管理について考えるページです。

目次

注意

  • このページを作ったひとが現在進行形で考えていることのため随時内容が変わることがあります。
  • 基本的にテンポラルテーブル (SQL Server 2016 以降 などに実装されている) が事情により使用できないケースを想定しています。(テンポラルテーブルの仕組みが無い DB 製品を使用する場合や開発要員などの都合)

設計時に検討すること

どのような要件・ユースケースで対象データの履歴を持つ必要があるのか

  • ログとしてのみ記録するため?
    • DB 上に持たなくてもいいかもしれない
  • 過去のデータを画面上で参照できるようにするため?
  • 誤操作の差し戻し (Undo) ができるようにするため?
  • マスタデータを更新したときに過去のデータが変わってしまうと困るため?

対象データの過去データを「履歴」として扱うのが適切か

  • 「マスタデータを更新したときに過去のデータが変わってしまうと困る」場合、それは「ある特定の時点での事実」を表すデータ
  • 「ある特定の時点での事実」を表すデータであればトランザクションデータのため、そのようなデータはそもそも履歴として扱う考え方が適切でないかもしれない
    • 例: 売上時点の商品情報
      • 売上明細テーブルに商品名、価格列などを持たせて売上発生時点で商品マスタの情報を売上明細に転記する方式にしたほうがいい
        (下図。売上時点のデータが変わる可能性がなくなり、「この売上伝票では特別に商品名を変える必要があった」などのケースが発生しても対応しやすい。また、売上データを取得するためのクエリが単純になる)
    • 正規化の見極めが必要
例: 売上明細に商品名と単価、売上に取引先名を転記する例。商品マスタと取引先マスタにあるデータは現在の情報、売上と売上明細にあるデータは当時の情報になる

データ量がどの程度になりそうか

  • 対象データの中に変わりそうもないデータが多い場合は方式によっては履歴の大半の容量が無駄になる可能性がある
  • 対象データが頻繁に変更されるデータの場合は方式によっては大量にデータが増える可能性がある

実際の処理で使用するクエリが複雑にならないか

  • 方式によってはクエリ (SELECT 文) が複雑になる可能性がある

よくある方式

履歴テーブル方式

対象テーブルとは別に履歴用のテーブルを設ける方式です。

例: 社員・部署マスタに履歴テーブルを設ける例。社員履歴上の部署名などは必要に応じて部署・部署履歴の両方から参照する
項目 説明
利点 ・マスタなどの対象テーブルに最新データのみが格納されるため最新データの取り扱いに関しては履歴が無いテーブルと同じように扱える
留意点 ・データの UPDATE 時に履歴テーブルの INSERT を適切に行う必要がある
・管理が必要なテーブルが増える

同一テーブル方式

対象テーブル内に履歴データも含める方式です。

例: 社員・部署マスタに履歴データを含める例
項目 説明
利点 ・テーブル数が少なくて済む
留意点 ・主キーがサロゲートキーの場合 (上図の内部 ID など)、対象データを参照している他データの外部キーを更新する必要が無いように最新データを常に同じ内部 ID にしておく必要がある (常に最新は UPDATE, 履歴は INSERT にする)
その他 ・親 ID 列を設けておくと親 ID IS NULL のデータが最新、親 ID IS NOT NULL のデータが履歴とわかるので、対象データによっては適用開始日・終了日を設けなくても可
 (実際に WordPress の wp_posts (投稿テーブル) が同一テーブル + 親 ID 形式。上図の場合は社員履歴の部署を引くために日付が必要)
・バージョン番号やリビジョン番号列は設けても設けなくてもいい
 (履歴の順序を知りたいだけなら親 ID IS NOT NULL のデータのうち ORDER BY 内部 ID や ORDER BY 適用開始日でわかる。番号は Window 関数 (ROW_NUMBER() OVER ...) で付与してもいい)