はじめに
テーブル設計をしていると、こんな誘惑に駆られることはないでしょうか。
status ENUM('draft', 'published', 'archived')
- 値が限定される
- 可読性が高い
- 制約も一緒にかけられる
一見すると色々メリットがありそうなENUMさんですが、
安易に使うと後から確実に痛い目を見ます。
この記事では、ENUMの何が問題なのか、実務で遭遇しがちな事故を整理します。
そもそもENUMとは何か
MySQLのENUMは、あらかじめ定義された文字列の集合から1つだけを選択できるデータ型を指します。
CREATE TABLE posts (
status ENUM('draft', 'published', 'archived')
);
見た感じ文字列に見えますが、
内部的には 整数(インデックス) として管理されています。
ENUMは
- 値の制約が簡単で不正な値が入らない
- 可読性が高い
- 別テーブルを作らずに完結できる
などのメリットがあります。
ここまでは、確かに魅力的見えますが、
しかし、問題は運用が始まってからです。
ENUMのここがアカン①:値の追加・変更がしづらい
先ほどの例のstatusに一つ値を追加しようとするとALTER TABLEを実行する必要があり、
テーブルロックが発生したり、テーブル定義変更に時間がかかったりします
ALTER TABLE posts
MODIFY status ENUM('draft', 'published', 'archived', 'deleted');
ENUMのここがアカン②:enumの定義をDBからだけでは確認できない
enumはDB上では数値として保存されます。
CREATE TABLE posts (
status ENUM('draft', 'published', 'archived')
);
上記のようなテーブル定義の場合は、'draft'は0、'published'は1、'archived'は2として保存されます。
つまりDBのデータを見た際にstatusカラムには0~2の数値のみが入ることになります。
これを見て直感的に「0だから・・・draftか」みたいに連想するのは難しいかと思います。(覚えていないといけない)
ENUMのここがアカン③:値の候補を取得するのが難しい
ENUMは「値が限定されている」こと自体はメリットですが、
その値の一覧をアプリケーション側から扱いづらいという欠点があります。
例えば、フロントエンドでプルダウンを作りたい、管理画面でステータス一覧を表示したい
といったケースです。
ENUMの場合、これらの値は
スキーマ定義の中にしか存在しません。
status ENUM('draft', 'published', 'archived')
問題点
- SQLを書かないと値一覧が分からない
- アプリ側で同じ定義を二重管理しがち
- DBとアプリで定義がズレるリスクがある
どういう時にENUMを使うべきか
ここまでENUMのデメリットを挙げてきましたが、
「ENUMは絶対に使ってはいけない」というわけではありません。
以下の条件を すべて満たす場合 に限り、ENUMは選択肢になります。
- 値の種類が非常に少ない
- 将来的に増減する可能性がほぼない
- 状態遷移やロジックに深く関わらない
- 他のDBに移行する予定がない
例としては、
- 内部的なフラグ
- 本当に固定された分類(曜日など)
- 一時的な小規模システム
などです。
後からビジネスロジックによって値の種類が増える可能性が1mmでもある場合は避けた方が良いかと思います!
おわりに
最後まで読んでいただきありがとうございました!
ENUMは名著「SQLアンチパターン」の「サーティワンフレーバー」としても紹介されていることが有名ですね!
昔は結構「選択肢のいずれかを保存するカラム = ENUM」くらいに思ってました。。。
ENUMは結構アンチパターンがある、とだけでも知っておくと踏みとどまれるかもしれません!