超電磁砲でデータベース(テーブル作成編2)

はじめに

今回は外部キーがテーマなので、まずは外部キーを有効化する。また、前回定義したテーブルを利用するが、念のため一旦破棄して再び定義する。もし存在しなければエラーとなるが、それはそれで問題はない。次の命令をコピペして実行して欲しい。

PRAGMA foreign_keys = ON;
DROP TABLE A_TEST ;
DROP TABLE S_TEST ;

CREATE TABLE S_TEST(
 NAME TEXT PRIMARY KEY,
 LEVEL INTEGER NOT NULL DEFAULT 0,
 SKILL TEXT,
 CHECK( 0 <= LEVEL ),
 CHECK( LEVEL <= 5 ) 
) ;

CREATE TABLE A_TEST(
 NAME TEXT,
 TEAM TEXT,
 PRIMARY KEY ( NAME, TEAM ),
 FOREIGN KEY ( NAME ) REFERENCES S_TEST( NAME )
) ;

外部キー

外部キーの動作を実際 SQL を実行して確認していこう。次の命令で テーブル S_TEST にデータを登録する。

INSERT INTO S_TEST VALUES ( '上条当麻', 0, 'イマジンブレイカー' ) ;
INSERT INTO S_TEST VALUES ( '初春飾利', 1, 'サーマルハンド' ) ;

テーブル A_TEST の属性 NAME はテーブル S_TEST の属性 NAME を参照している。そのため A_TEST.NAME に対応する S_TEST.NAME が存在しなければならない。

課題
(1) 次の2命令の結果を予想し、実際に動作を確認せよ。
INSERT INTO A_TEST VALUES ( ‘初春飾利’, ‘柵川中’ ) ;
INSERT INTO A_TEST VALUES ( ‘神裂火織’, ‘天草式十字凄教’ ) ;
(2) 次の2命令の結果を予想し、実際に動作を確認せよ。
INSERT INTO S_TEST( NAME ) VALUES ( ‘神裂火織’ ) ;
INSERT INTO A_TEST VALUES ( ‘神裂火織’, ‘天草式十字凄教’ ) ;

解説
(1) 1つ目の命令は問題なく成功する。2つ目の命令はテーブル S_TEST に対応するデータが存在しないためエラーになる。
(2) テーブル S_TEST に対応するデータを追加したので成功する。

続けて、次の命令がエラーになることを確認しよう。
DROP TABLE S_TEST ;
DELETE FROM S_TEST WHERE NAME = ‘神裂火織’ ;

これらの削除命令がエラーになるのは外部キーの参照制約が満たされなくなってしまうからだ。実際、DELETE FROM S_TEST WHERE NAME = ‘上条当麻’ ; はテーブル A_TEST に参照しているデータが存在しないため成功する。参照制約が損なわれない仕組みがあれば削除可能になる。ここでは、参照しているデータも連動して削除する方法を解説しよう。そのため、A_TEST を削除して作成し直す。データも再登録しておく。

DROP TABLE A_TEST ;

CREATE TABLE A_TEST(
 NAME TEXT,
 TEAM TEXT,
 PRIMARY KEY ( NAME, TEAM ),
 FOREIGN KEY ( NAME ) REFERENCES S_TEST( NAME ) ON DELETE CASCADE
) ;

INSERT INTO A_TEST VALUES ( '初春飾利', '柵川中' ) ;
INSERT INTO A_TEST VALUES ( '神裂火織', '天草式十字凄教' ) ;

ON DELETE CASCADE を追加した。これは親テーブル(参照先)のデータを削除した時に連動して削除することを表している。ON UPDATE CASCADE もあり、これは連動して属性値を変更する。他にはON DELETE SET NULL や ON DELETE SET DEFAULT が使えることもある。

課題 次の命令の結果を予想し、実際に確認せよ。
DELETE FROM S_TEST WHERE NAME = ‘神裂火織’ ;
UPDATE S_TEST SET NAME = ‘御坂美琴’ WHERE NAME = ‘初春飾利’ ;

解説
 DELETE 時の動作は設定してあるので1つ目の命令は成功し、 テーブル S_TEST, A_TEST の両方から「神裂火織」のデータは削除される。UPDATE 時の動作は設定されていないので、2つ目の命令は参照制約違反でエラーとなる。

ついでに ON UPDATE CASCADE の動作も確認しておこう。再度、次の命令でテーブルを作成し直す。

DROP TABLE A_TEST ;

CREATE TABLE A_TEST(
 NAME TEXT,
 TEAM TEXT,
 PRIMARY KEY ( NAME, TEAM ),
 FOREIGN KEY ( NAME ) REFERENCES S_TEST( NAME ) ON UPDATE CASCADE
) ;

INSERT INTO A_TEST VALUES ( '初春飾利', '柵川中' ) ;

課題 次の命令の結果を予想し、実際に確認せよ。
UPDATE S_TEST SET NAME = ‘御坂美琴’ WHERE NAME = ‘初春飾利’ ;

解説
 今度は2つのテーブルで「初春飾利」が「御坂美琴」に書き換わっている。

テーブル設計

テーブルの設計について簡単に補足しておく。設計については、関数従属性、正規形、ER図などが関係してくるので、ここで詳しく解説するつもりはない。設計に関心がある方は情報処理技術者試験データベーススペシャリストの勉強をされると良いだろう。

関数従属性を知らない方にはさっぱり分からないと思うが、属性 NAME, LEVEL, SKILL, TEAM の間には、関数従属 NAME \(\longrightarrow\) { LEVEL, SKILL } がある。これは NAME が決まると LEVEL, SKILL が一意に決まることを含んでいて、各人物に1つしか SKILL がないという前提となっている。この前提が崩れると設計を見直す必要がある。本来は ER図で書くべきだろうが1行で簡単に記載すると、次の多対多の関係が得られる。ここで、主キーを太字で表した。

( NAME, LEVEL, SKILL ) \( \longleftrightarrow \) ( TEAM )

多対多の関係は次のように1対多の関係に書き換える。中央のテーブルは中間テーブルと呼ばれ、外部キーからなる。

( NAME, LEVEL, SKILL ) \( \longrightarrow \) ( NAME, TEAM ) \( \longleftarrow \)( TEAM )

このような理由で本来は右端のテーブルが存在するべきであるが、SQLの演習としては煩雑になるだけなので省略した。