« 「有効期間」を含むテーブルとの参照関係 | トップページ | 企業システムの基本構造を理解しよう »

2011.12.20

動的参照関係を「宣言的」に扱えるか

 前回に続いて動的参照関係に関する話題である。システムのコード量を減らすために、動的参照関係を「手続的」ではなく「宣言的」に扱える仕組みが必要だ――これは私の長年の主張なのだが、あらためてその意味を説明したい。

■「手続的」に記述される動的参照関係

 通常の参照関係(静的参照関係)にもとづく外部キー制約は、テーブル上の仕様として、DDL(create table等のデータ記述命令)を用いて次の例のように宣言することで組み込める。

(1)静的参照関係の例

 [A] {a}, b
 +
 |
 └―…[C] {c}, d, a

create table C (c ..., d ..., a ...,
constraint C_PK primary key (c),
constraint C_FK1 foreign key (a) references A(a));

 いっぽう、動的参照関係にともなう外部キー制約については、DDLでは組み込めない。たとえば(2)で、C上の項目aは、dの値を10倍して得られる導出項目(この導出手順のばかばかしさは議論の本質とは関係ない)であって、テーブルC上に物理的には存在しない。物理的に置いてしまえば、推移的な関数従属性をタプル上に置くことになる。DDLでは、こういう項目にもとづいて参照制約を組み込めない。

(2)動的参照関係の例

 [A] {a}, b
 +
 |
 └―…[C] {c}, d, (a)*1

*1. a=F(d)=d*10

 では、どのように対処すればいいのだろう。正規形を崩せばいい。aを物理項目として置いて、Cを保守するプログラムやトリガーにdとaとを整合させるためのコードを組み込めばいい。――これが従来のやり方だった。

 つまり、動的参照関係は正規化崩しの隠然たる動機になっていたということだ。その結果、アプリケーションは「正規化崩しの報い」としての余分なコードをかかえることになった。すなわち、バリデーション(整合性確認)のためのコーディングが、プログラム言語やDML(操作系のSQL文)を用いた形でなされてきた。動的参照関係は「手続的」に扱われてきたのだ。

 正規化崩しの動機の典型は「パフォーマンスを上げるため」と思われがちだが、この動機はマシン性能の向上にともなってどんどん薄弱になっている。いっぽうマシンの性能とは無関係に、動的参照関係を扱うには正規化崩しを甘受しなければいけない。よほど単純なDBでない限り動的参照のデータ要件は出現するため、多くのシステムは正規化崩しをせざるを得ない。結果的に、じゅうぶん強力なマシンを使っていながら「正規化崩しの報い(データの整合性を維持するために余分なコーディングが必要になること)」を受ける羽目になっている。

 その原因は明らかだ。ほとんどの開発基盤で「動的参照を宣言的に扱うための機構」が欠落しているためだ。なるべく正規化を崩さずに実装する。そのために、動的参照を宣言的に扱えるようになっていたほうがいい。

■「宣言的」に記述される動的参照関係

 では、「動的参照を宣言的に扱う」とは具体的にはどういうことなのだろう。(2)の例でいえば、開発基盤上でテーブルCの仕様を登録する際に、項目aが「導出項目」であることを宣言するとともに、その導出手順をテーブル仕様の一部として盛り込めるようになっている。その上で、導出項目を含む外部キーを宣言的に記述できるようであればよい。

 なお、動的参照関係に関連する導出項目の導出過程は、別のテーブルを参照するようなものとして宣言的に記述されるのかもしれないし、スクリプトとして手続的に記述されるものかもしれない。その違いは大きな問題ではない(どうしても手続的にしか定義できない導出手順は存在する)。ただし、導出項目を含んだ外部キーの組み込みそのものについては、手続的でなく宣言的であったほうがよい。従来は手続的記述要素であった動的参照関係を、宣言的記述要素に回収するためだ。

 また、動的であるか静的であるかにかかわらず、宣言される参照関係の「順序」も重要だ。先行する参照関係によって手に入れられる項目が、後続する動的参照関係の外部キーになるかもしれないからだ。もちろんスクリプトによって導出される項目であれば、導出手順の順序も重要で、必要に応じて各参照関係の前後に配置できるようになっていなければならない。これらが「テーブルの仕様(拡張仕様)」として組み込まれるのである。

 動的参照関係の宣言にもとづく自動バリデーションの例を示そう。(3)で示したような値状況で、テーブルAの1件目の削除は許されない。なぜなら、テーブルC上のレコードによって参照(利用)されているからだ。

(3)動的参照関係における更新制約の例1

 [A] {a}, b
 +  100 あ ←削除不可
 |  300 か
 |
 └―…[C] {c}, d, (a)*1
        01 10 (100)

*1. a=F(d)=d*10

 自動バリデーションは次の手順で実行される。まず開発基盤が、テーブルAに対して参照関係を持つテーブルの一覧を手に入れる。その中のテーブルCについてデータを読み出すとともに、導出項目の設定、参照テーブルへの参照を指定順序にしたがって実行する。その過程でテーブルAの当該タプルへの参照が見つかれば、エラーを返す。「導出のためのコード」が必要になったとしても、動的参照にともなうバリデーション実行のためのコードは不要だ。

■複雑な動的参照関係の例

 しかしこの程度であれば、動的参照関係を宣言的に扱うことの意義がわかりにくいかもしれない。もう少し込み入った例を挙げよう。

 (4)は「動的参照関係を手なづける」の記事で使った例だ。Cタプルを起点として、Aの項目bの値を検索したうえで、C上のdと組み合わせてBDのeの値を獲得する、という関係を示している。CにおけるBDへの外部キー値を準備するためにAの読取操作が必要になる。ゆえにこの参照関係は「動的」である。

(4)動的参照関係における更新制約の例2

 [A] {a}, b
 + 100 111 ←変更不可
 | 200 222
 |
 └―…[C] {c}, a, d, (b)
 ┌―…  10 100 m (111)
 |
 +
 [BD] {b, d}, e
    111 m 1234
    222 j 3456

 Aの1件目タプルの項目bの値"111"は、この値状況では変更されてはいけない。なぜなら、その値はCの1件目のタプルによって、項目dの値"m"と複合された外部キー{b,d}としてBDの1件目を参照しているからだ。Aの1件目のdの値"111"をそれ以外の値に変更してしまえば、CとBDとの間に不整合が生じてしまう。

 これを避けるために、正規形を崩してbをC上に物理的に置けばどうなるか。まず、C上でaの値を変更したときに、Aから読み込んだbの値でC上のbを更新するためのコードが必要になる。さらに、A上でbが変更されたときに、関連するCについてその上のbを洗い替えしながらBDとの関連をチェックするためのコードも要る。辛気臭い「正規化崩しの報い」である。

 ところが、この込み入ったバリデーションも、正規形のまま動的参照を宣言することで自動化可能だ。Aのbの値が変更されたとしよう。そのとき開発基盤は、Aに対して参照関係を持つテーブルの一覧を手に入れる。その中のテーブルCについて、Aの当該タプルを参照しているCタプルについて、関連テーブルとの参照を試行する。その過程で不整合(この場合ならばBDへの参照の失敗)があればエラーを返す。

 以上、レコード削除とフィールド値の変更が制約される例を挙げたが、レコード追加が制約されるケースもある。有効期間を伴う動的参照で起こり得るが、似たような話なので省略する。

■サロゲートキーは問題を悪化させる

 蛇足だが、(4)の例から「だから複合主キーなど使ってはいけないのだ。サロゲートキーにすればこんなややこしい問題は生じない」と思われた方がいるかもしれない。そういうことにはならない。残念ながら。

 (4)のモデルをサロゲートキーを用いて修正したものが(5)である。BDの{b, d}が二次識別子に格下げされ、サロゲートキー{f}が導入されている。いっぽうCではBDへの外部キーとしてfが置かれている。

(5)サロゲートキーを導入する

 [A] {a}, b
 + 100 111 ←変更不可
 | 200 222
 |
 └―…[C] {c}, a, d, f
 ┌―…  10 100 m 001
 |
 +
 [BD] {f}, e, {b, d}
   001 1234 111 m

 (4)で可能だった動的参照関係を、(5)では構成できない。ゆえに、Aのb、またはCのdの修正にともなうCとBDとの整合性維持について、手続的にしか扱えない。手続的であっても扱われていればまだいい。多くの場合、データ要件が曖昧になったデータ構造にもとづいて問題が見落とされ、Aの1件目のbの値の変更をむざむざ許してしまうだろう。そうなれば、問題が生じないどころか「潜行」する。つまり、使えば使うほど濁ってゆくデータベースが出来上がる。

 かくのごとく、サロゲートキーの利用にあたっては神経質な配慮が要る。私がサロゲートキーを「上級者の実装上のテクニック」であり「汎用的な設計スタイル」ではないと主張するのはそういう意味だ。

 そして、いちいちサロゲートキーを導入しなければ開発しにくいと感じられるようであれば、それは開発基盤そのものの問題である。複合主キーを許さなければ(つまりサロゲートキーを強制すれば)、「基盤開発者」は楽なのかもしれない。しかし、そのしわ寄せが「案件開発者」やユーザ企業にゆくことを忘れてはならない。

 サロゲートキーを強制しないこと。そして、動的参照関係を宣言的に扱えること。――いずれも、DBを正規形のまま実装するとともに、システム中の手続的記述要素を減らして保守性を高めるための、開発基盤の基本要件である。

本ブログでの関連記事
ナチュラルキーを主キーにしてはいけない

|

« 「有効期間」を含むテーブルとの参照関係 | トップページ | 企業システムの基本構造を理解しよう »

コメント

興味深く読ませて頂きました。導出項目がジョインで取得される場合だけでなく、その他の手法(計算など)で算出される場合も想定されているのですね。応用範囲が広がる半面、動的参照に基づくバリデーションをリアルタイムで実行するとすれば、参照側テーブルのデータ件数が多い場合には、被参照側のデータを変更・削除する際の処理速度に注意が必要な印象を持ちました。タイムアウトされてしまわないかとか。まあ、そうした変更は少ないでしょうけれど。

投稿: keis | 2011.12.21 13:08

keisさん

たしかにデータ状況によっては、正規化されたままの自動バリデーションに変に時間がかかることはあるでしょうね。私の問題意識は、これまでの開発基盤が動的参照関係について「正規化を崩して手続的に扱う」という低級な手段しか提供できていなかった点にあります。「宣言的に扱う」というオプションをこれからの開発基盤は提供できなければいけない。そのうえで、レスポンスに責任を持つ開発者が、データ状況の予測にもとづいてマシンスペックやインデックスや正規化崩しを含めて実装方針を主体的に決めたらいい。そのように考えます。

投稿: わたなべ | 2011.12.21 21:29

そうですね。まず、制約を宣言できることが基本にあって、そのうえで、必要に応じて性能面のチューニングや正規化崩しができる仕掛けがあればよいということですね。今日のマシンスペックでは、たぶん、多くの場合は、何の問題もなく処理できてしまうでしょうから。

投稿: keis | 2011.12.22 11:42

コメントを書く



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




トラックバック

この記事のトラックバックURL:
http://app.cocolog-nifty.com/t/trackback/87674/53529035

この記事へのトラックバック一覧です: 動的参照関係を「宣言的」に扱えるか:

» 渡辺幸三氏の「動的参照関係」について [Hot Heart, Cool Mind.]
関西IT勉強宴会のメーリングリストで、渡辺幸三氏が提案している「動的参照関係」について話題となっていたのだが、この概念についての理解がかならずしも共有されていないような気もしている(勘違いであればご免なさい)。 渡辺氏がブログで挙げている説明では、動的…... [続きを読む]

受信: 2011.12.24 14:09

« 「有効期間」を含むテーブルとの参照関係 | トップページ | 企業システムの基本構造を理解しよう »