【MySQL】 外部キー制約 (restrict / cascade / set null)

mysql エンジニア
記事内に広告が含まれています。

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の設定方法と挙動について確認しました。

必要に応じて外部キー制約を付与して、データの安全性を担保しましょう!

 

コメント