« レコードの変更予約管理を合理化する | トップページ | 「EXCEL仕様書からコード生成」のアンバランス »

2016.05.15

正規化崩しの目的は「高速化」だけではない

 ひとつの事実が1か所にしか記録されないようにする――これはDB構造を正規化する際の基本だが、このルールを意図的に違反すること(正規化崩し)で、効果的なDB構造が生み出されることがある。正規化崩しは高速化のためだけにあると思われがちだが、それ以外の目的もある。そのような正規化崩しのテクニックとして、「抽象化(汎化)」を取り上げよう。

 説明の前に、正規化崩しに関して大事なことを言い添えておきたい。勘違いしている技術者がいるが、正規化崩しとは「正規化してから崩す」という意味である。本来の正規形を経由せずに非正規形になっているとしたら、正規化崩しではなく、単なる「無手勝流」でしかない。

 では、いったん正規化してから崩すことがなぜそれほど重要なのか。事前に「更新時異状(更新処理にともなって発生するデータの不整合)」に対処しておくためだ。正規化崩しにせよ無手勝流にせよ、そのままでは遅かれ早かれ更新時異状が起こる。ところが後者のやり方では、更新時異状にどう対処しておけばよいのかわからない。

 じっさい、無手勝流で設計されたDBを抱えたシステムは悲惨で、苦労が開発担当者ではなく保守担当者にしわ寄せされる。運用が始まってしばらくたつと、どうもデータの不整合が生じていることに気づく。ところがその機序や対処法がわからない。当の設計者はというと、すでに次のプロジェクトでもうひとつの困ったDBを鋭意設計中だったりする。おめでたく、傍迷惑な話である。

 では、本題に入ろう。当然ながら「抽象化」も、正規形を認識した次の段階で実施されなければいけない。更新時異状に事前に対処しておくためだ。まず正規化崩しする前のモデルを見てほしい(図1)。

▼図1.さまざまな売上管理簿と顧客の関係
20160513a

 物販管理システムにおいて、売掛残高の変化は「出荷明細(受注にもとづく出荷)」のみを契機として起こるわけではない。「特殊売上(受注にもとづかない出荷や返品)」や、サービス提供を伴わない「売上振替」もある。「受領」にともなって売掛残高が控除されたりもする。

 これらの多様な取引が、それぞれ独特な項目を含むテーブルで管理されている。そしてそれらの管理簿は、売掛残高の増減をもたらすデータとして、顧客テーブルに対する関連を持っている。顧客テーブル上の現売掛残高は、それらの管理簿レコードを集計することで得られる「導出属性」である。

 このモデルでも間違いではないのだが、さすがにこのままでは使いにくい。たとえば、特定月度での売上一覧を出力するとか、特定顧客の売掛残高を見ようとするだけで、さまざまなテーブルを横断的にスキャンしなければいけない。それではアプリも複雑だしレスポンスも悪い。また、売上というのは重大な会計情報なので、それぞれの管理簿上のデータを修正した過程をトレースできるようにしたい。ところがこの形だと、修正する度に各管理簿上に新たなレコードを追加する形で履歴を取る形になるので、アプリが不必要に複雑化する。

 そこで、大胆に正規化崩しがなされる(図2)。まず、さまざまな売上を抽象化したテーブル「売掛増減履歴(*1)」が導入される。また各管理簿には、履歴レコードとの対応関係を持つための二次識別子として「取引管理№」が組み込まれる。そうしたうえで、それぞれの管理簿上で売上計上が起こるような更新があれば、その度に履歴テーブルに売掛残高の増減額が書き込まれるようにする。いったん売上計上した後に管理簿レコードが修正された場合には、「赤黒」として書きこまれる。

▼図2.各種の売上を抽象化したテーブルを導入する
20160513b

 他にも、図1で導出属性であった現売掛残高が、期首売掛残高、当期計上額等の物理フィールドとして置かれている。なぜか。管理簿データというものは未来永劫に記録され続けるものではなく、期末等のタイミングでパージ(切り離し)されるからだ。ゆえに、期首残高を物理的に保持しておく必要がある。また、現時点での売掛残高を手早く見るためにも、期中の増減額の合計値も物理フィールドとして持っておいたほうがいい。

 結果的にこのモデルでは、たとえば「受注にもとづいて出荷した売上の事実」が、出荷明細と売掛増減履歴の2箇所に重複して記録されることになる。また、売掛増減額の顧客別合計値が物理的に記録されるというのも、ある種の正規化違反である(本来であれば合計値は都度導出されるべきだ)。

 これらの正規化崩しにともなう更新時異状を起こさないために、どんな配慮が要るのだろう。モデルから明らかなように、「管理簿の内容が変更された場合に、その内容に応じて売掛増減履歴を追加し、同時に売掛項目を更新する」という業務ロジックが徹底されていれば(*2)、不整合は起こらない。

 なお、簿記を理解している読者はお気づきだと思うが、売掛増減履歴は「売掛金勘定専用の仕訳帳」に相当するテーブルである。売掛金の相手勘定については、商品や販管費や現預金などが売上区分にしたがって対応することになる。とどのつまりは簿記というものも、事業活動における価額の変動を引き起こすさまざまな取引を抽象化することで生み出された枠組みである。「抽象化」が効果的な帳簿組織を構想するための鍵であることがおわかりだろうか。

 さて、図2は「1対多」の抽象テーブルの例だったが、「1対1」の場合もある。図3は「花束問題」のモデルの一部だが、いくつかの管理簿を抽象化した「受払予定」が導入されている。これは各管理簿上のレコードから見て「1対1」の関係にある(受払予定から見れば1対1または0の排他関係にある)。こうすることでDBは、各管理簿の内容の変化に単品毎の在庫推移をリアルタイムで追随させるための基礎になってくれる。

▼図3.各種の受払予定を抽象化したテーブルを導入する
20160513c

 データモデリングにおける抽象化は、上級者向けのテクニックである。なぜならそれは、正規化崩しの一種であるだけでなく、ある種の創造性が求められる過程だからだ。抽象テーブルに相当する実体は、現実世界には存在しない「概念」でしかない。そういう対象を洞察して導入するためには、設計者の言語能力とそのドメインに関する深い理解に頼るしかない。

 上級者向けとは言うものの、こういった工夫がなされていないモデルは(よほど単純なシステムでない限り)役に立たない。「観察される事物をボトムアップでまとめて正規化しました」だけのモデルは、いかに誠実なものであっても、料理における「上質な素材」でしかない。それをいかに調理し、盛りつけし、顧客に提供できるか。実務レベルでは、そこまでが問われる。その意味で、こういった「基本的」なテクニックを「上級者向け」などと言ってはいけないのかもしれない。


*1.売上履歴、売上明細、売上伝票などと呼ばれることが多いが、私は「売掛増減履歴」と呼ぶのが好きだ。受領のような売掛残高の控除取引までを含めて抽象化した概念を「売上履歴」などと呼ぶことに抵抗があるからだ。
*2.この種の業務ロジックをどのようにシステムに組み込むかも重大な問題である。従来のようにアプリにオンコーディングするようなやり方ではなく、テーブルの拡張定義として組み込めたほうがいい。アプリをうすっぺらにすることで、システムの保守性を高めるためだ。参考記事「業務ロジックをアプリに置いてはいけない

|

« レコードの変更予約管理を合理化する | トップページ | 「EXCEL仕様書からコード生成」のアンバランス »

コメント

コメントを書く



(ウェブ上には掲載しません)




トラックバック


この記事へのトラックバック一覧です: 正規化崩しの目的は「高速化」だけではない:

« レコードの変更予約管理を合理化する | トップページ | 「EXCEL仕様書からコード生成」のアンバランス »