履歴管理をするリソース

以前から履歴管理をするリソースってどういうモデル設計にすれば良いのか悩んでいたのだけど、なんとなく解決策が見えてきた。

まず重要なのがID。IDは実装技術なのだけど、この履歴管理するリソースを扱う際には非常に重要。まず、例として商品マスタを考える。要件としては次のような感じ。

  • 当時のイベントからは当時の商品名が参照できること
  • 特定の日付時点での商品名が引けること
  • 単なる間違いは直接修正したい
  • 利用開始日より前にマスタを登録するが、期日が来るまで有効にならないこと
  • 利用終了日が設定できる
  • コードを変更しても串刺し検索が可能

まず、1番目はイベントにIDを付けておくだけで良いので、次のようなテーブルにすれば問題は解決する。

ID 商品コード 商品名
1 A001 おでん100%

次に「特定の日付時点での商品を引く必要がある」ので、少なくとも適用開始日が必要なことはわかる。普通は適用終了日が必要だが、単に引くだけなら日付以前で最大の適用開始日を持つレコードを引っ張ればよいから必要ない(IDのない設計でこんな設計をするとトランザクションとマスタの結合で死ねるが、IDを使えば結合の際に適用期間を見なくてすむので特に問題は起こらない)。

ID 商品コード 商品名 適用開始日
1 A001 おでん100% 1900/01/01
2 A001 おでん20% 2007/09/20

次に「単なる間違いは直接修正したい」だが、これは該当のレコードを直接UPDATEするだけだ。過去のレコードも含め間違っている場合はそれぞれ修正する。IDだと適用開始日は主キーでないし、終了日のカラムがないので適用開始日の修正も単にUPDATEするだけ。ただし、適用開始日のUPDATEでは過去のイベントデータとの紐付きは変化しない*1

ID 商品コード 商品名 適用開始日
1 A001 いちご100% 1900/01/01
2 A001 いちご20% 2007/09/20

次に「期日が来るまで有効にならないこと」だが、これは適用開始日を未来日付にして登録するだけで良い。

ID 商品コード 商品名 適用開始日
1 A001 いちご100% 1900/01/01
2 A001 いちご20% 2007/09/20
3 A001 いちご3000% 3000/01/01

次に「利用終了日が設定できる」だが、ここで初めて適用終了日が必要となる。ただし、適用終了日はNULL可の任意項目としておき、設定する場合も次の適用開始日より前であることを条件とする。

ID 商品コード 商品名 適用開始日 適用終了日
1 A001 いちご100% 1900/01/01 (null)
2 A001 いちご20% 2007/09/20 2010/12/31
3 A001 いちご3000% 3000/01/01 (null)

問題は次の「コードを変更しても串刺し検索が可能」か。単なるポインタと考えれば、連結リストにしちゃうのが自然か?

ID 商品コード 商品名 適用開始日 適用終了日 前ID
1 A001 いちご100% 1900/01/01 (null) (null)
2 A001 いちご20% 2007/09/20 2010/12/31 1
3 ICHIGO-3000 いちご3000% 3000/01/01 (null) 2

いやはや、これだとconnect byとか使わないと検索できないが、しょせんマスタを一回検索するだけと考えれば無茶じゃないか。もっと良い案あったら教えてください(はあと)

[追記] ひとつあるのが、商品マスタと商品マスタ履歴を分けるという方法だな。商品マスタの方は通称と備考だけ持っているようなもので、残りはすべて商品マスタ履歴。検索子画面で選択するのは商品マスタだけど、イベントに紐付くのは商品マスタ履歴の方みたいな。結構よさげだけど、開発者に理解してもらうのはかなり困難が伴うかも(w

*1:ちなみにこれは利点と考えるべき。イベントデータは発注書などの外部用帳票の元ネタになることが多いので後で勝手に変わると困ることが多い。ただし、そういう場合が多いだけで業務要件は当然確認すべし。