MySQL の外部キー制約は何かと便利です。join で繋ぐ必要のあるカラムをすぐに把握できたり、インデックスでもあるため(正確にはインデックス抜きに外部キー制約を作れないので外部キー制約設定の際、必要なインデックスが自動で作られます)検索が高速になったり、親レコードが存在しないのに存在するかのような値を持つ子レコードの作成を防いだり、と色々です。
外部キー制約には参照アクションを設定できます。参照アクションとは参照先の親レコードの値が変化した時、子レコードの値を連動させる動作のことです。members テーブルの id を member_id で参照している posts テーブルがあった時、参照先の members テーブルの id が変化した場合、何が起きるかの設定を参照アクションの設定で行います。
MySQL :: MySQL 5.6 リファレンスマニュアル :: 13.1.17.2 外部キー制約の使用
MySQL :: MySQL 8.0 Reference Manual :: 13.1.20.5 FOREIGN KEY Constraints
これは次の様な構文で設定します。
ALTER TABLE 外部キー制約をつけるテーブル ADD CONSTRAINT ここで命名する外部キー制約の名前 FOREIGN KEY (外部キー制約をつけるテーブルの持つ参照用カラムの名前) REFERENCES 参照先のテーブル名 (参照先のテーブルの持つ参照用カラムの名前) ON UPDATE 参照アクション ON DELETE 参照アクション; # 例 ALTER TABLE posts ADD CONSTRAINT posts_member_id_foreign FOREIGN KEY (member_id) REFERENCES members (id) ON UPDATE CASCADE ON DELETE CASCADE;
例では ALTER TABLE ですが CREATE TABLE 中でも扱えます。
MySQL の参照アクションには次の 4 つ(実質 3 つ)が用意されています。
参照アクションの種類
RESTRICT
デフォルトの設定値で最も操作が手間ですが最も安全です。子レコードがあった場合、親レコードを削除できません。また子レコードがあった場合、参照されている値を変更できません。この設定値のまま値を変更するには一時的に外部キー制約機能を無効化するか紐づいている子レコードが全くない様にする必要があります。
CASCADE
最も扱いやすいですが最も危険です。子レコードがあった場合、削除時には同時に削除、外部から参照されている値の変更時には子レコードの参照に使っているカラムの値も変更、となります。
SET NULL
扱いやすいですが適切なデータベース設計の元で使用しない場合、意図しない値を持つレコードが生まれます。子レコードがあった場合、削除時、更新時ともに子レコードの参照に使っていたカラムに null をセットします。
NO ACTION
MySQL においては RESTRICT と同じです。他のリレーショナルデータベースでは異なったり振る舞いをします。
とりあえず RESTRICT にしておけば安全ですが、適切に CASCADE, SET NULL を用いると快適になります。
親レコードが存在しない場合、存在しないことが約束される子レコード(例えば、ユーザのアレルギーです。ユーザが退会してもユーザのアレルギー情報だけデータベースに残っているのは奇妙です)には CASCADE を設定することでデータベースの整合性を保ったまま楽にレコードを物理削除できます。
逆に親レコードが存在しない場合でも存在することのある子レコードに対しては SET NULL を設定するとよいです。例えば、部署に所属しない社員が在籍しているのことのある会社における社員と部署が挙げられます。部署を削除した時に会社に在籍している社員の情報まで消えるのはまずいです。SET NULLにしておけば部署を削除しても社員は部署に所属していない状態で残ります。もし、部署に所属しない社員がデータベースにいる状態が整合性の破綻している状態であるならば RESTRICT にしておくべきです。RESTRICT にしておけば部署を削除できる状態はその部署に社員が一人も所属していない状態と担保されるからです。
大雑把には、子レコード 1 に対して親レコード 0 があり得るならば SET NULL、子レコードが正規化によって分離されているのみの親レコードの意味に含まれるものならば CASCADE、どちらでもないか判断がつかないならば RESTRICT を選ぶことになります。