ボイス・コッド正規形への分解は、結局どうすればいいのか(2)
2ヶ月前に書いた1回目の続き。
http://d.hatena.ne.jp/tgk/20060507
「第3正規形だが非ボイスコッド正規形」の表の正規化のしかた。
前回書いたことは
- 「第3正規形だが非ボイスコッド正規形」の表を教科書的に正規化すると、現実世界に存在する制約をデータベースに反映できなくなる。
- そのような表は、分解して正規化すると制約が消滅してしまうから、構造を追加して正規化すればいいのではないか。
ということ。
「正規化とはデータの重複を排除することだ」という立場の人には、そんなの正規化じゃない、と言われるかもしれない。
まあいいじゃないですかということにして、2つのやり方をメモしておく。
渡辺式
渡辺幸三 生産管理・原価管理システムのためのデータモデリング P-46に、非ボイスコッド正規形の表の正規化方法が載っている。
前回の例に当てはめると、こういう形になる。
科目 = { (科目コード), 科目名 }
教官 = { (教官コード), 教官名, 科目コード }
学生 = { (学生コード), 学生名 }
受講 = { (学生コード, 教官コード), (学生コード, 科目コード) }
カッコで括ってあるのはユニークキー。
受講テーブルの二次識別子 (学生コード, 科目コード) がポイント。
これにより { 学生, 科目 } -> { 教官 } という関数従属性を保持している。
確かに問題が解決されているが、無理やり「受講」と命名した最後のテーブルが何を表しているのか、ぱっと見分からない感じがする。
慣れの問題だと思いますが。
T字形ER手法
T字形ER手法の場合は、科目・教官・学生をresourceと見るので、渡辺式のように教官が科目コードを外部キー参照することはない。
代わりに、教官・科目の対照表(=「講座」)を追加する。これはvalidation-ruleの対照表。「受講」に挿入するデータの検証に使える。
科目(R) = { 科目コード, 科目名 }
教官(R) = { 教官コード, 教官名 }
学生(R) = { 学生コード, 学生名 }
講座 = { 教官コード, 科目コード }
受講(E) = { 教官コード, 科目コード, 学生コード }
作図しないと { 教官 } -> { 科目 } の関数従属性がどこで担保されているのか分からないですね。
いいツールが手元にないので作図は省略して、これをPostgreSQLの実装に落とすとこうなる*1。
-- 教官(Resource) create table teacher ( teacher_id integer default nextval('seq_teacher_id') not null primary key, teacher_name text not null ); -- 科目(Resource) create table course ( course_id integer default nextval('seq_course_id') not null primary key, course_name text not null ); -- 学生(Resource) create table student ( student_id integer default nextval('seq_student_id') not null primary key, student_name text not null ); -- 講座(validation-rule) create table class ( -- 教官にはUNIQUEを付ける teacher_id integer not null unique references teacher(teacher_id), course_id integer not null references course(course_id) ); -- 受講(Event) create table class_attending ( teacher_id integer not null references teacher(teacher_id), course_id integer not null references course(course_id), student_id integer not null references student(student_id), -- "{ 教官 } -> { 科目 }" の実装 constraint class_key foreign key (teacher_id, course_id) references class(teacher_id, course_id), -- "{ 学生, 科目 } -> { 教官 }" の実装 constraint class_attending_unique_key unique (course_id, student_id) );
{ 学生, 科目 } -> { 教官 } に違反するデータを挿入すると、こんなエラーになる:
ERROR: Cannot insert a duplicate key into unique index lecture_attending_unique_key
{ 教官 } -> { 科目 } に違反するに違反するデータを挿入すると、こんなエラーになる:
ERROR: lecture_key referential integrity violation - key referenced from lecture_attending not found in lecture
ということで、関数従属性は維持される。
*1:ほんものののT字形ER手法は外部キー制約を使わないらしいので、これはオレ流T字形ER手法ということになります