Railsの場合、modelでdependent: :destroy
などで指定できる外部キー制約を
MySQLの場合どのようにすれば指定できるかをまとめてみました。
外部キー制約の種類
まずは外部キー制約の種類です。
外部キー制約 | 親のデータを削除した時の挙動 |
restrict | エラーになる(削除できない) |
cascade | 子のデータも消える |
set null | childrenのparent_idがnullになる |
動作確認
テーブル作成、データ挿入して、親データを削除したときにどのような挙動になるかを確認してみます。
parentテーブルとchildrenテーブルを作成して、
parentデータ1件、それに紐づくchildrenデータを2件挿入します。
restrict
テーブルを作成します。
create table parent (
id bigint not null auto_increment,
name varchar(255),
primary key (id)
);
create table children (
id bigint not null auto_increment,
parent_id bigint not null, -- NOTE: not null制約
name varchar(255),
primary key (id),
foreign key (parent_id) references parent (id) on delete restrict -- NOTE: restrict制約
);
次にデータ挿入します。
insert into parent (name) values ('name');
-- NOTE: parentに紐づくchidrenデータ
insert into children (name, parent_id) values ('children1', 1);
insert into children (name, parent_id) values ('children2', 1);
挿入したデータを確認します。
mysql> select * from parent;
+----+------+
| id | name |
+----+------+
| 1 | name |
+----+------+
mysql> select * from children;
+----+-----------+-----------+
| id | parent_id | name |
+----+-----------+-----------+
| 1 | 1 | children1 |
| 2 | 1 | children2 |
+----+-----------+-----------+
この状態でparentデータを削除してみます。
mysql> delete from parent where id = 1;
ERROR 1451 (23000): Cannot delete or update a parent row:
a foreign key constraint fails (`development`.`children`, CONSTRAINT `children_ibfk_1` FOREIGN KEY (`parent_id`) REFERENCES `parent` (`id`))
restrictの場合は、このように外部キーのエラーが出てデータを削除することができません。
cascade
続いてcascadeです。
-- NOTE: 必要に応じてテーブルを削除してください。
drop table children;
drop table parent;
-- NOTE: テーブル作成
create table parent (
id bigint not null auto_increment,
name varchar(255),
primary key (id)
);
create table children (
id bigint not null auto_increment,
parent_id bigint not null, -- NOTE: not null制約
name varchar(255),
primary key (id),
foreign key (parent_id) references parent (id) on delete cascade -- NOTE: cascade制約
);
こちらも同じくデータを挿入します。
insert into parent (name) values ('name');
-- NOTE: parentに紐づくchidrenデータ
insert into children (name, parent_id) values ('children1', 1);
insert into children (name, parent_id) values ('children2', 1);
この状態でparentデータを削除してみます。
mysql> delete from parent where id = 1;
Query OK, 1 row affected (0.02 sec)
今度はデータを削除することができました。
selectでそれぞれのデータを確認してみます。
mysql> select * from parent;
Empty set (0.00 sec)
mysql> select * from children;
Empty set (0.01 sec)
cascadeの場合、親データを削除するとそれに紐づく子のデータも削除されていることが確認できます。
set null
最後にset nullです。
restrictとcascadeと異なり、parent_idにnullを許容することに注意しましょう。
-- NOTE: 必要に応じてテーブルを削除してください。
drop table children;
drop table parent;
-- NOTE: テーブル作成
create table parent (
id bigint not null auto_increment,
name varchar(255),
primary key (id)
);
create table children (
id bigint not null auto_increment,
parent_id bigint, -- NOTE: null許容
name varchar(255),
primary key (id),
foreign key (parent_id) references parent (id) on delete set null -- NOTE: set null制約
);
データを挿入します。
insert into parent (name) values ('name');
-- NOTE: parentに紐づくchidrenデータ
insert into children (name, parent_id) values ('children1', 1);
insert into children (name, parent_id) values ('children2', 1);
この状態でparentデータを削除してみます。
mysql> delete from parent where id = 1;
Query OK, 1 row affected (0.02 sec)
データを削除することができたのでデータを確認してみます。
mysql> select * from parent;
Empty set (0.01 sec)
mysql> select * from children;
+----+-----------+-----------+
| id | parent_id | name |
+----+-----------+-----------+
| 1 | NULL | children1 |
| 2 | NULL | children2 |
+----+-----------+-----------+
2 rows in set (0.00 sec)
親のデータは削除されており、子のデータは残っています。
set nullを指定しているので、parent_idはnullになっていることが確認できます。
まとめ
外部キー制約のrestrict, cascade, set nullの設定方法と挙動について確認しました。
必要に応じて外部キー制約を付与して、データの安全性を担保しましょう!
コメント