Create Branch

データベースにおけるプロパティ管理:enumとマスターテーブルどっちにしようか

Published on 2025年4月15日

database

はじめに

現在、個人開発のテーブル設計を行なっていく中でユーザーのRoleを管理するため、どのような形で管理するか、考えておりました、

enum型でRoleの管理を行おうと考えておりましたが、アンチパターンであるとなんとなく聞いたことがあったので、一旦調べつことにしました。

enumによるプロパティ管理の問題

enum型を下記のように定義します。

この時点では、DBで管理する状態という点においては、マスターテーブルと変わらないくないか、と感じた。

CREATE TABLE Users (
    id INT PRIMARY KEY,
    status ENUM('active', 'inactive', 'pending')
);

色々記事を見ていく中で下記の記載があり、サービスを運用していく中では一番クリティカルな課題であると感じた。

1. 更新処理が極めて大変

enumの最大の問題点は、後から値の追加や変更が必要になった場合の対応。
例えば新しいステータス'blocked'を追加する場合や、'inactive'を'disabled'に変更したい場合、直接テーブル定義を変更することとなる。

ALTER TABLE Users MODIFY COLUMN status ENUM('active', 'inactive', 'pending', 'blocked');

2. トランザクション管理されない変更

テーブル定義の変更は、通常のデータ操作のようにトランザクションで管理されない。

本番環境への適用時に下記の状態に陥ることが想定できる。

  • デプロイ中に障害が発生した場合、途中状態で止まる可能性
  • ロールバックが容易でない
  • 他の処理と競合する可能性

3. データ不整合のリスク

さらに、ENUM値を変更する場合('inactive'を'disabled'に変更など)は、次のような複雑な手順が必要になるみたい。

  1. 新しい値を含むENUM定義に変更
  2. 既存データを新しい値に更新
  3. 古い値をENUM定義から削除

この手順の間にエラーが発生した場合、データに不整合が生じる可能性が非常に高くなります。

マスターテーブルを活用した場合

マスターテーブルの実装例

-- マスターテーブルの作成
CREATE TABLE StatusTypes (
    status_code VARCHAR(30) PRIMARY KEY,
    display_name VARCHAR(100),
    description TEXT,
    is_active BOOLEAN DEFAULT TRUE
);

-- 初期データ投入
INSERT INTO StatusTypes (status_code, display_name, description) VALUES
('active', 'アクティブ', 'アクティブなユーザー'),
('inactive', '非アクティブ', '一時的に利用停止中のユーザー'),
('pending', '保留中', '承認待ちのユーザー');

-- ユーザーテーブルの作成(マスターテーブルを参照)
CREATE TABLE Users (
    id INT PRIMARY KEY,
    status VARCHAR(30),
    FOREIGN KEY (status) REFERENCES StatusTypes(status_code)
);


マスターテーブルのメリット

  1. 柔軟な値の追加・変更

    • 単純なINSERT/UPDATE文で値の追加や変更が可能
    • トランザクション内で管理できる
  2. データの整合性が保証される

    • 外部キー制約により不正な値が入ることを防止
  3. アプリケーションコードの簡素化

    • 有効な値の一覧をSELECT文で簡単に取得できる
    • 画面表示用のラベルなども同時に管理できる

具体的な例:ステータス変更時の比較

enumの場合

-- 1. 新しいENUM定義に変更
ALTER TABLE Users MODIFY COLUMN status ENUM('active', 'disabled', 'pending');

-- 2. データの更新('inactive'から'disabled'へ)
UPDATE Users SET status = 'disabled' WHERE status = 'inactive';

-- このプロセス中にエラーが発生すると、不整合状態に陥る可能性あり


マスターテーブルの場合

BEGIN TRANSACTION;
    -- 単一のUPDATE文で完結(ON UPDATE CASCADEを設定していれば関連テーブルも更新)
    UPDATE StatusTypes SET status_code = 'disabled' WHERE status_code = 'inactive';
COMMIT;


まとめ

enumは一見便利なデータ型ですが、実際の運用、特に変更管理の観点では多くの問題を含んでいるのかなと感じでおります。

あとがき

私自身は下記の詳細を前提にDB設計を行う際、前提・基準として考えております。
何か設計などを考える際は、その使用している技術の根本の考え方をもとに対応をすることを心がけています。

DBの取り扱い

  • サービスを運用していく中で、直接DBをいじることNG
  • アプリケーションを通してのみ、データの更新を行う。

DB設計の基本的な考え方

  1. データに複数の意味を持たせない
  2. 1つのデータの責務を小さくする
  3. 常に状態が見えるようにするために事実のみを保存する

RDBMSの考え方・性質

  1. 重複がない
  2. 順序は持たない
  3. 実在する要素しかない(NULLは持たせいない。)
hirotobeat