【MySQL】enumの是非について(ハンズオン・動画付き)

今回は、MySQLでenumを使うことの是非について見ていきたいと思います。

 動画はこちらです。

なお、こちらの素晴らしい記事を非常に参考にさせていただきました。

では、見ていきましょう。

現時点では、こちらのpersonテーブルを想定しています。

なお、auto_incrementの設定などについてご不明の場合は、前回のnoteをご参照ください。

1 enumのカラムの追加

まずは、enumを追加してみましょう。

これにより、あらかじめ定めたもの以外の入力を防ぐことができます。

alter table person add sex enum ('man', 'woman', 'unknown');

下のように性別のsexというカラムを追加しました。

2 データの更新

2ー1 enumに存在する場合

enumに存在する値を元に変更してみましょう。

update person set sex = 'man' where id = 1;

id = 1のレコードのsexをmanに設定しました。

2ー2 enumに存在しない場合

仮にsexに存在しない「guy」を設定しようとしたらどうなるでしょう。

update person set sex = 'guy' where id = 2;

下のようにエラーとなりました。

3 enumに値を追加したい場合

こちらは通常のカラムを変更する手順で変更することができます。

alter table person modify sex enum('man','woman','unknown','not_answered');

下のように、新しく「not_answered」を追加しました。

4 すでに使われている値を変更しようとした時

例えばすでにid=1で使われている「man」を変更しようとするとどうなるでしょうか。

alter table person modify column sex enum('guy','woman','unknown','not_answered');

すると、下のようにエラーとなりました。

5 すでに使われている値の修正について

修正するためには次の3つのステップを踏まなければなりません。

一度に変更することができません。

5ー1 enumに変更したい値の追加

まずは次のようにして、変更したい値をenumに追加します。

alter table person modify column sex enum('man','woman','unknown','not_answered','guy');

5ー2 変更前のenumを持っているレコードを全て更新

次に、変更前のenumを持っているレコードを検索し、全て新しい値に変更します。

update person set sex = 'guy' where sex = 'man';

これで、全てのレコードが変わりました。

5ー3 変更前のenumの値を削除

最後に、変更前のenumの値である「man」を除いて更新します。

alter table person modify column sex enum('woman','unknown','not_answered','guy');

以上の3ステップで変更することができます。

6 外部キーによる実装

では、enumを用いずに、外部キーで行った場合はどうなるでしょう?

6ー1 そもそも外部キーとは?

こちらがわかりやすいかと思います。

https://wa3.i-3-i.info/word1992.html

今回であれば「sex」に入る値は必ず「sex」テーブルから取ってくるという制約をつけます。

6ー2 マスターテーブルとトランザクションテーブル

ちなみに、今回のsexテーブルやpersonテーブルなどをマスターテーブルと呼びます。

こちらがわかりやすいと思います。

https://www.katalog.tokyo/?p=5986#:~:text=%E3%83%87%E3%83%BC%E3%82%BF%E3%83%99%E3%83%BC%E3%82%B9%E3%81%AE%E3%83%86%E3%83%BC%E3%83%96%E3%83%AB%E3%81%AF%E7%89%B9%E5%BE%B4,%E5%B1%A5%E6%AD%B4%E3%81%8C%E4%BF%9D%E5%AD%98%E3%81%95%E3%82%8C%E3%81%BE%E3%81%99%E3%80%82

6ー3 参照先のテーブルを作成する

では、まずは参照先となるテーブルを作っていきましょう。

create table sex(type varchar(20), primary key(type));

下のように、sexテーブルができました。

6ー4 レコードの一括インサート

「insert into」でレコードを追加していきます。

今回はせっかくなので、複数レコード一括インサートします。

https://johobase.com/multiple-insert-sql/

では、やっていきましょう。

insert into sex (type) values ('man'),('woman'),('unknown');

次のように、一括インサートができました。

ちなみに、一つでもおかしい値があれば、全て失敗となります。

insert into sex (type) values ('man'),('woman'),(11111111111111111111111111);

6ー5 外部キーの接続を行う

では、参照先のテーブルができましたので、接続していきましょう。

まずはpersonテーブルにカラムを作成します。

alter table person add sex varchar(10);

変更の場合はこちらです。

alter table person modify sex varchar(10);

では、下のコマンドで外部キーをsexカラムに設定していきましょう。

alter table person add foreign key (sex) references sex (type);

これでsexテーブルのtypeカラムに参照できるようになりました。

ちなみに、「MUL」とはなんでしょう。

こちらは外部キー制約があるカラムのうち、重複が可能なものです。

確かに、性別なので重複が可能ですね。(manのレコードなどはたくさんできるはず)

https://www.halu.dev/entry/2014/07/09/000000#:~:text=%E5%90%84%E3%83%86%E3%83%BC%E3%83%96%E3%83%AB%E3%81%AB%E8%A8%AD%E5%AE%9A%E3%81%95%E3%82%8C,%E3%82%92%20MULTIPLE%20KEY%20%E3%81%A8%E5%91%BC%E3%81%B6%E3%80%82

6ー6 外部キーを設定してみよう

では、実際に外部キーを設定してみましょう。

update person set sex = "man" where id = 3;

下のように、テーブル内にある値であれば、設定することができました。

一方、存在しないものを設定しようとするとどうなるでしょう。

update person set sex = "guy" where id = 4;

「guy」はsexテーブルに存在しないため、外部キー制約エラー(a foreign key constraint fails)となりました。

ところで、constraintの次に書いてある、`person_ibfk_1`ってなんでしょう?

6ー7 テーブルの構成を見てみよう

では`person_ibfk_1`を知るためにテーブルを見てみましょう。

show create table person;

下のようになりました。

今回の外部キー制約の名前

と名付けているようです。

そして、今回のエラーは「person_ibfk_1」という外部制約のルールに反するので発生しているようです。

(sexテーブルにないものを持ってこようとしていました)

6ー8 sexテーブルの名前を変えるとどうなる?

では、マスターテーブルの「man」「guy」に変えようとするとどうなるでしょう?

なお、manはすでにpersonテーブルのid=3で使われています。

update sex set type = "guy" where type = "man";

下のように外部キー制約エラーが発生しました。

マスターテーブルは削除更新もできないとなっています。

ここで削除と更新を切り分けて考えたいと思います。

確かにマスターテーブルの「man」が消えてしまったら参照先がなくなって困ってしまいます。

ただ、マスターテーブルの名前の変更が他のテーブルに波及されれば良いケースもあるかもしれません。

7 on句(結合条件)を見てみよう

テーブルを結びつけるときの条件を規定するのがon句になります。

7ー1 on update句について

on update句片方のテーブルがアップデートされた時に、結合されたテーブルにどのように影響するのかを規定します。

7ー1ー1 現在の外部キーの削除

まずは、現在存在している外部キーを削除します。

show create table person;

上のコマンドで、外部キーを確認し、次のコマンドで削除します。

alter table person drop foreign key `person_ibfk_1`;

7ー1ー2 on updateの設定種別について

次のように設定可能な値は5つあります。

そして、デフォルトではrestrictが設定されているようです。

https://www.dbonline.jp/mysql/table/index11.html

今回は、更新が反映されるようにするため、「cascade」を使っていきます。

7ー1ー3 on update cascadeの設定について

次のようにして、外部キーを追加します。

alter table person add foreign key (sex) references sex (type) on update cascade;

下のようにうまく設定ができたようです。

7ー1ー4 更新の反映を確認してみる

では、sexテーブルの更新がpersonテーブルのレコードに反映されるかを見てみましょう。

update sex set type = "guy" where type = "man";

下のように、うまく反映されたようです。

7ー2 on delete句について

では、削除が行われたときの挙動についてのon delete句を見てみましょう。

7ー2ー1 cascadeを設定したらどうなるの?

今回も前回と同様、「cascade」を設定してしまうどうなってしまうでしょうか。

alter table person add foreign key (sex) references sex (type) on update cascade on delete cascade;

下のように、設定してみました。

ではsexテーブルの「guy」を消してみようと思います。

意図としては、「sex」テーブルだけ消したいと仮定します。

delete from sex where type = 'guy';

しかし、意図通りにならず、「guy」を消したことで、personテーブルの「guy」が設定されていたレコードも消えてしまいました。

今回の場合、せめてsexテーブルの「guy」の削除を行う際に、エラーが発生すれば事前に防ぐことができたかもしれません。

7ー2ー2 on delete restrictについて

そのため、今回は下のrestrictを設定したいと思います。

これにより、「guy」を削除しようとした場合、エラーが発生します。

まずは、事前準備として、下のように、「man」がついたレコードをpersonテーブルに作ります。

insert into sex (type) values ('man');
insert into person (first_name, sex) values ('taro', 'man');

7ー2ー3 on delete restrictを設定する

では、on delete restrictを設定してみましょう。

restrictはデフォルトですが、今回は勉強用にあえて設定してみます。

alter table person add foreign key (sex) references sex (type) on update cascade on delete restrict;

 restrictはデフォルト値のため、特に表示はされていなさそうです。

7ー2ー4 削除をしてみる

では、削除をしようとして、適切にエラーが出ることを確認しましょう。

delete from sex where type = "man";

下のように、personテーブルでレコードで設定されている「man」を消そうとしてみます。

このように期待通り、エラーが発生しました。

今回は以上にいたします。

サポートをしていただけたらすごく嬉しいです😄 いただけたサポートを励みに、これからもコツコツ頑張っていきます😊