Hot Heart, Cool Mind.

会計×IT の深層へ

主キーの設計③-いいとこ取りは可能か?

主キー論争の分析その③である。
前のエントリーでは、コード派とID派の意見の相違の源泉には、コッド博士が作り出した正規化の理論の取り扱いをどうするか、という問題が横たわっているという仮説を提示した。そして、正規化の理論には「エンティティ」の居場所がなく、したがって(実質はエンティティ参照である)IDにも居場所がない、という僕の理解を説明した。


このことの結果として、正規化の理論を重視すると、設計段階でのIDの導入には慎重にならざるを得ず、IDの使用は認めたとしても「実装テクニック」と位置づけることになる。一方、関連の実装に外部識別子(ユーザが認識しているコードのたぐい)を用いることによってもたらされる弊害(前々回説明した)を気にする人々は、IDの導入に積極的であり、数学的な意味での正規化の実効性に対しては批判的となる。
このような説明が「コード派」と「ID派」の議論の構図を模式的に表したものとなっていないだろうか。

こうした模式化が容易に戯画化に転化する危険は、承知している。本当は、それぞれの個人は、こういった小難しい理論ではなく、実際の経験に裏打ちされた思想にもとづいてご自分の考えを語っていると思う。とはいえ、さまざまな点にわたる意見の相違の根底に、このように比較的単純な相違点がある可能性を、それは排除するものではない。
この問題はかなり以前から頭の隅にひっかかっていたが、最近、ギャップを埋める方策があるのではないかと考えるようになった。
今回はその方法を説明する。

前回の検討で、問題の核が正規化の理論の基礎にある「関数従属性」の数学的定義にあるらしいことがわかってきた。そこで、数学をいったん離れて考えてみよう。関数従属性の実務上の意義はどんなものだろうか。
僕らはデータ要件定義をしている間、四六時中、次のような問いをユーザに投げかけている:

「部門がきまれば事業部は決まるのですね?」

売掛金の回収サイトは、得意先と事業部の組合せによって変わり得る、ということですか?」

このように、データが「何々ごとに存在するのか」を、あるいは換言すれば「データの存在単位」を明らかにすることが、「関数従属性」を意識することの実務上の意義だ。データの存在単位はとても大事で、これを捉え誤ると開発段階や運用段階でひどい目に合うことは、多くの読者が経験済みだろう(僕ももちろんそのひとりだ)。
存在単位を表現するのに、普通の会話では、「得意先」「事業部」といった概念、すなわちエンティティ概念を僕らは用いる。ところが、正規化理論での関数従属性は「エンティティ」の概念なしに定義されているので、「得意先と事業部が決まれば回収サイトは決まる」とは言えない。その代りに「得意先コードと事業部コードが決まれば回収サイト区分は決まる」と言う。後者の表現が、ほとんど必然的に、(模式化された)コード派の主張するデータモデルに結びつくことは明らかだろう。

この事態の打開策を根本から考えるなら、リレーショナルモデルの数学的基礎に何らかの修正が必要だと、僕は思う。しかし僕にはそれを実行するのに必要な数学的スキルがないし、仮にあったとしても、そんなややこしい作業をする前には、それをすれば本当に問題が解決できるのか、確信を得たい。それに、僕らが直面するほとんどの設計問題には、そもそも数学的バックグラウンドがない。正規化についてだけ数学にこだわることはないじゃないか(と言ってみる)。

そこで、「存在単位の明示」という関数従属性の実務的意味だけを取り入れて、リレーショナルモデルのシンタックスを変更することを考えてみよう。必要な変更は以下の通りである:

  1. 「エンティティ」概念を導入する(「リレーション」は「エンティティ」に紐つけられていることにする)。
  2. 属性値として「エンティティ参照」を許す。そのかわりにエンティティ参照の実装としてのID(内部識別子)は許さないこととする。
  3. 「主キー」という概念(構文要素)を廃止する。そのかわりに存在単位を表現する「一意キー」を導入する(脚注①)。主キーは必須だが、一意キーは不要なら設けなくてもよい。
このシンタックスに沿って作成したデータモデルの例を下に示す:

主キーサンプル②-注文書IDEF1X

表記法はIDEF1X類似だが、シンタックスを変更したため以下の修正を加えている。
  1. 「エンティティ」を表す箱の内部、横線で区切られた上部には、IDEF1Xでは主キーを記述するが、ここでは一意キーを記述している(脚注②)。
  2. エンティティ参照属性を表す際には、エンティティ名を[]で囲んで表記している(例.「注文」エンティティの「得意先」属性)。
データモデルの内容については、以下の点にご留意頂きたい:
  1. 注文明細の一意キーが「[注文]+行No」となっていること。
    これは「注文」と「行No」の組合せにより、ただひとつの注文明細が指定されることを意味している。オリジナルのリレーショナルモデルなら、注文明細の主キーを「注文日+注文番号+行No」とするところだ。エンティティ参照([注文])を導入したことにより、複合主キーによる関連付けを避けることができた。
  2. 「得意先」には「得意先C」のような一意キーがないこと。
    得意先を一覧から選択できるとか、電話番号をキーとして検索できるユーザインターフェースを想定するなら、得意先コードは不要だ。得意先コードがなくとも、「エンティティ参照」を用いれば得意先と注文書の関連(アソシエーション)は表現できる。だから一意キーは必須ではないのだ。
  3. 内部識別子としてのIDが登場しないこと。
    IDはエンティティ参照の実装である。これは実装独立の論理モデルだから実装要素であるIDは登場しないのである。
さて、上記のデータモデルはどのように実装に変換されるのだろうか。これはとても簡単だ。データモデルに対応するSQL文の一例を以下に示そう:
CREATE TABLE 得意先 {
	ID		INTEGER		NOT NULL UNIQUE,
	得意先名		VARCHAR(100),
	電話番号		VARCHAR(100)		
}

CREATE TABLE 商品 {
	ID		INTEGER		NOT NULL UNIQUE,
	商品C		VARCHAR(10),
	商品名		VARCHAR(100),		

	UNIQUE(商品C),
}

CREATE TABLE 注文 {
	ID		INTEGER		NOT NULL UNIQUE,
	注文日		DATE		NOT NULL,
	注文番号		INTEGER		NOT NULL,
	得意先ID		INTEGER		NOT NULL,
	合計金額		INTEGER		NOT NULL,
	消費税額		INTEGER		NOT NULL,

	UNIQUE(注文日,注文番号),

	FOREIGN KEY (得意先ID)	
				REFERENCES 得意先(ID)
}

CREATE TABLE 注文明細 {
	注文ID		INTEGER		NOT NULL,
	行NO		INTEGER		NOT NULL,
	商品ID		INTEGER		NOT NULL,
	単価		INTEGER		NOT NULL,
	個数		INTEGER		NOT NULL,

	UNIQUE(注文ID,行NO),

	FOREIGN KEY (注文ID)	
				REFERENCES 注文(ID),
	FOREIGN KEY (商品ID)	
				REFERENCES 商品(ID)
}
ここでは以下の点にお気づき頂きたい:
  1. 他のエンティティから参照されているエンティティに対応するテーブルには、IDカラムが付加されていること。
    IDはエンティティ参照の実装手段だから、他のエンティティから参照されないエンティティに対応するテーブルでは、IDカラムは不要である(この例では「注文明細」テーブル)。参照の有無を問わずすべてのテーブルにIDカラムを設けても構わないが、それは実装上の選択事項であるということだ。
    IDには"NOT NULL"制約と"UNIQUE"制約が付される。二つあわせると、"PRIMARY KEY"制約とほぼ同じ意味になるが、先に述べたようにモデルのシンタックスから「主キー」概念を削除してしまったので、混乱を避けるためにこのようにした。
  2. エンティティ参照は、参照先エンティティに対応するテーブルのIDカラムに結び付けられた FOREIGN KEYに変換されること。
  3. 一意キーに対応して"UNIQUE"制約が生成されること。

コード派のデータモデルは、上記のモデルに簡単に変換できる。外部キーをすべてエンティティ参照に置換するだけだ(主キーを構成する外部キーも含めて)。ID派のデータモデルでは、存在単位が示されていないこともあるので、このモデルに変換するにはもう少し作業が必要だ。しかし、存在単位を(一意キーとして)明示する作業には、やる価値があると思う。

この新しいモデルによれば、データの存在単位が明確に示されるとともに、関連の実装にはIDが用いられる。ここにおいて、コード派とID派の主張が調和したと言えないだろうか。
最初のエントリで、僕は以下のように書いた。

主キーには以下の二つの機能がある。

  1. ユーザーからコンピュータシステムに対する要求において、対象データを一意に指定する手段を提供する(外部識別子機能)
  2. エンティティ間の結びつきを表現する手段を提供する。すなわちUMLでいう「関連(アソシエーション)」の実装手段としての機能(内部識別子機能)
この二つの機能は本来別のものなのだが、リレーショナルモデルでは、あえて両方を主キーに担わせている。....

上記のモデルではまさにこの二つの機能を、一意キーとエンティティ参照に別々に割り当てている。これが問題解決のカギだと僕は考えているのだ。

さてこれで本論は終わりだ。しかしあとには大変な仕事が残されている。リレーショナルモデルの数学的基礎を再構成するする作業だ。上でも書いたように、僕にはこれをやるのに必要な数学的スキルがないが、素人ながらいくらかの着想はある。次回それを書き留めてこのテーマを締め括ろう。

脚注①
一意キーは、リレーショナル理論でいう「候補キー」とほぼ同じである。ただし候補キーはかならず存在するが、一意キーは存在しないこともある。これは、理論に「エンティティ」概念を導入したために生じた相違である。すなわち、リレーショナル理論では、すべての属性値が等しいタプルは複数存在しえない。そのため、全部の属性項目の組を用いれば必ずタプルを一意に識別できる。だから、候補キーはかならず存在するのである。
一方、理論にエンティティを導入すると、複数のエンティティインスタンスに対して、まったく同じ内容のタプルを対応付けることも可能になる。そのため、タプルの属性値の内容ではエンティティを識別できないことがあり得るのだ。呼び方を「一意キー」としたのは、主キーがない以上、主キーの候補という意味を持つ「候補キー」という呼称は適切でないからである。

脚注②
一意キーは、(候補キーと同様)ひとつのエンティティに複数存在することがある。その場合、IDEF1Xなら、一意キーの記述エリアに横破線で区切りを入れ、複数の一意キーを記入できるようにすればよいかもしれない。