2018.09.26

予算テーブルと実績テーブルを分ける?

 ある企業のシステム刷新に関わっていた時のことだ。予実管理サブシステムのデータモデルをレビューしているときに興味深いやりとりがあった。私のモデルは次のようなものだったのだが、これに対して「予算額と実績額が同じテーブルに置かれているのはおかしい」と指摘されたのである。

[組織別予実サマリ]{組織ID,年月},予算額,実績額,...

 同様の指摘は他にもある。{倉庫id,商品id}を主キーとする「在庫テーブル」には「現在庫数量」や「月初繰越数量」といった在庫関連項目が置かれる。これらの他に倉庫と商品の組み合わせで決まる「標準ロケーション」のような属性項目を置いたら、「刻々と変化する在庫項目の他にマスター項目が同居しているのはおかしい。マスター項目は別のテーブルに分けるべきではないか」と言われたことがある。いずれの場合も、そのデータを扱う業務(アプリ)が違っているような項目を単一テーブル上で混在すべきではない、という設計方針にもとづく指摘である。

 いっぽう私はそれらを単一テーブル上にまとめてしまう。理由は単純だ。「それらの項目が主キーに関数従属しているから」である。どんな行為や業務やアプリがその項目の値を設定するか、あるいはどの部署がその項目値の管理責任があるかは関係ない。それらは私にとってアプリ設計や業務設計の問題であって、データ設計の段階では意図的に問題にしない。「マスターかトランザクションか残高か」とか「リソースかイベントか」といった区別もしない。

 ただし「単一テーブルにまとめる」の基本方針には但し書きがつく。たとえば「脊椎動物種テーブル」では、その主キー「脊椎動物種ID」には、「種名」や「学名」や「網区分(両生類、爬虫類、鳥類、哺乳類、魚類といった区分)」が関数従属する。

 ここで「妊娠期間」の項目の位置づけを考えてみてほしい。この属性は「脊椎動物種ID」に関数従属することは明らかなのだが、「網区分」が"哺乳類"である場合に限られている。したがってこの項目は「哺乳類属性テーブル」に置かれる。つまり各「網」に固有の属性は、それぞれの「サブタイプテーブル」に分けて置かれることになる。

[脊椎動物種]{脊椎動物種ID},種名,学名,網区分,...<スーパータイプ>

[哺乳類属性]{脊椎動物種ID},妊娠期間,...<サブタイプ>

 一般に「条件付き関数従属性」については、条件毎のテーブルをサブタイプとして切り出してまとめたらよい。言い換えれば、このような特別の事情がなければ、主キーに関数従属する項目群は1個のテーブルにまとめて置けばよい。

 上述した「予算額」と「実績額」は値が決まるタイミングが違うだけで、スーパータイプとサブタイプの関係ではない。にもかかわらず、それらが同じテーブルに置かれているのはおかしいと考える人々がいるのはなぜか。それは彼らに「DB構造にはアプリや業務のあり方が反映されていなければならない」という強い思い込みがあるからだ(*1)。

 では「データがどのように扱われるか」を基礎とするDB設計と、関数従属性を基礎とするDB設計のどちらが合理的かといえば、後者である。「どのように扱われるか」と比べて関数従属性が変化しにくいからだ。

 組織体制の変化や実装基盤の刷新にともなって、「データがどのように扱われるか」は変化しやすい。そして、テーブル構造を変化させるには多大なコストがかかる。それゆえDB構造は、「データがどのように扱われるか」の変化に攪乱されにくい位置づけで維持されなければいけない。そのための基準をデータ項目間の静的な側面としての関数従属性だけ(厳密には定義域制約を含める)に求めるというのは、データ構造を安定的に維持するためのより合理的な設計方針である。

 この方針には「データモデリングが楽になる」という利点もある。私は設計作業を効率化するために、クライアントの話を聴きながら「その場」でモデリングするスタイルにこだわっている。モデリングライブのようなイベントで披露すると驚かれるのだが、これが可能なのは私が賢いからではなく、関数従属性だけを手がかりにしているからだ。多くの技術者はDB設計するために業務や組織やアプリのあり方までを考慮しようとするから、仕事を無駄に難しくしてしまっているような気がする。

 そういうわけなので、「扱うアプリが違う」、「値が決まるタイミングが違う」、「扱う業務が違う」、「扱う組織が違う」といったこまごました「データの扱われ方」は、ユーザインタフェースや業務態勢のあり方として考慮すればよい。そこらへんの変化しやすい条件を中途半端にでも持ち込めば、DB構造のエントロピーは年々増大してゆく。最終的には、関数従属性ともアプリ構成とも業務体制とも連動していない「おそ松DB」が完成する。

 とどのつまり、どんなやり方だろうが「DB構造をわかりやすい形で維持できればよい」という話ではある。ただしそれは「シンプルイズベスト」を狙うためではない。シンプル過ぎるDB構造はアプリを無駄に複雑にしてしまう。ここで言うわかりやすさとは「関数従属性がDB構造として端的に表現されていること」である。たとえば「id」などの単独主キーを多用すれば、一見するとシンプルだが関数従属性や更新不可制約は失われる(*2)。

 「(関数従属性が)わかりやすいDB」でありながらアプリのあり方がわかりにくいのであれば、アプリを作り替えるなどして対処できる。しかし、関数従属性を読み取りにくいのであれば、DB構造は死んだも同然だ。データの矛盾や不整合を生じさせずにシステム仕様を変化・発展させることが難しいからだ。最悪の場合、データの矛盾や不整合が生じたことさえわからないカオスに落ち込む。DB構造が死んで変化・発展できないだけならまだマシで、それがゾンビ化して精力的に事業の足を引っ張り始めるなんてたまらない。


*1.タイミング違いに伴うnull値を避けたいのであれば、ステータス管理できるようにしておけばいい。値がゼロやブランクであってもその値が決まるタイミングが到来しているかどうかは区別できる。
*2.自然キーを主キーとすべきと言っているのではない。複合主キーの必要性と自然キーを用いることとは無関係の話である。参考記事

| | コメント (0) | トラックバック (0)

«「技術者とのコネ」で決まる開発の成否