🚂

Rails enumの integer vs string:どちらを選ぶべき?

に公開

はじめに

Rails の enum でステータス管理をする際、integer型とstring型のどちらを選ぶべきか悩んだことはありませんか?

# integer型
enum :status, { published: 0, pending: 1, ban: 2 }

# string型
enum :status, %i[published pending ban]

この記事では、それぞれのメリット・デメリットを比較し、プロジェクトに最適な選択ができるようにまとめました。

Rails バージョンによる違い

Rails 7.0 以前

  • enum は基本的に integer型が前提
  • string型を使う場合は、少し工夫が必要でした
# Rails 7.0以前でstring型を使う場合
enum status: {
  published: "published",
  pending: "pending",
  ban: "ban"
}

Rails 7.1 以降

  • string-backed enum が正式サポート
  • より簡潔な記法で string型 enum を定義可能
# Rails 7.1以降の新しい記法
enum :status, %i[published pending ban], prefix: true

パターン① Integer型 enum

実装例

# model
class Post < ApplicationRecord
  enum :status, { published: 0, pending: 1, ban: 2 }
end

メリット ✅

1. DB効率が良い

  • 整数として保存されるため、データサイズが小さい
  • 大量データを扱うときにパフォーマンス面で有利
  • インデックスサイズも小さく済む

2. 便利なヘルパーメソッドの自動生成

post.published!     # ステータスを変更
post.published?     # 判定
Post.published      # スコープ

3. 順序の明確な定義

  • 並び替えや比較がしやすい
  • Post.statuses.keys.index(post.status) で順序を取得可能

デメリット ❌

1. DBの可読性が低い

  • データベースを直接見ると 0, 1, 2 といった数値のみ
  • 何を表しているか直感的に理解できない

2. マッピング変更に弱い

  • 例:published: 0published: 1 に変更すると既存データが壊れる
  • 新しいステータスを途中に追加するのが困難

パターン② String型 enum

実装例

# Rails 7.1以降
class Post < ApplicationRecord
  enum :status, %i[published pending ban], validate: true
end

# データベースには "published", "pending", "ban" として保存される

メリット ✅

1. DBの可読性が高い

  • データベースを直接見ても "published", "pending" など、意味が明確
  • デバッグやデータ分析が容易

2. 変更に強い

  • 後から値を追加・順序を変えても既存データに影響なし
  • リファクタリングが安全

3. Rails 8以降の方向性と一致

  • 型安全で、ActiveRecord::Enum の今後の推奨パターン
  • 可読性重視の現代的なアプローチ

デメリット ❌

1. データサイズがやや大きい

  • 文字列として保存するため、integer型より容量を使用
  • 数百万件レベルになるとパフォーマンス差が出る可能性

2. インデックスのコスト

  • 文字列インデックスは数値インデックスより大きくなる

比較表

観点 Integer enum String enum
DBの可読性
マッピング変更耐性
パフォーマンス
データサイズ
Railsの推奨トレンド
スコープ/メソッド

結論:どちらを選ぶべき?

String型 enum がおすすめなケース 👍

  • 小〜中規模アプリケーション
  • ステータスが増えたり変更される可能性があるモデル(投稿、予約など)
  • チーム開発でDB可読性を重視したい
  • 長期的な保守性を優先したい

Integer型 enum を検討すべきケース

  • 超大規模データ(数百万件超)
  • バッチ更新が頻繁
  • パフォーマンスが最優先
  • ステータスが固定で変更の可能性が極めて低い

実装のベストプラクティス

String型 enum を使う場合

# migration
class AddStatusToPosts < ActiveRecord::Migration[7.1]
  def change
    add_column :posts, :status, :string, default: "published", null: false
    add_index :posts, :status  # 検索性能を考慮
  end
end

# model (Rails 7.1以降)
class Post < ApplicationRecord
  enum :status, %i[published pending ban], 
       validate: true,  # 不正な値を防ぐ
       prefix: true     # published_status? のようなプレフィックス付きメソッド
end

まとめ

現代的なRailsアプリケーションでは、String型 enum の採用が増えています。特に Rails 7.1 以降では公式サポートも充実し、可読性と保守性の観点から推奨される選択肢となっています。

ただし、パフォーマンスが最重要課題となる大規模システムでは、Integer型 enum も依然として有効な選択肢です。プロジェクトの特性を見極めて、最適な方を選択しましょう。

一般的な推奨:迷ったら String型 enum を選ぶことをおすすめします。後から Integer型への移行は可能ですが、その逆は既存データの移行が複雑になるためです。

Discussion