「Railsでデータベースにテーブルを追加したいけど、どうすればいい?」
「間違えてカラムを削除してしまった…元に戻せる?」
Ruby on Railsでアプリケーションを開発していると、データベースの構造を変更する必要が頻繁に出てきます。
Rails Migrations(レイルス・マイグレーション)は、データベースの変更を管理するための仕組みです。直接SQLを書かなくても、Rubyのコードでデータベースを変更でき、変更履歴も自動的に記録されます。この記事では、Rails Migrationsの基本から実践的な使い方まで、初心者の方にも分かりやすく解説します。
データベースの変更を、安全かつ効率的に管理しましょう。
Rails Migrationsとは何か

Rails Migrationsは、データベースのスキーマ(構造)を変更するための仕組みです。
マイグレーションの基本概念
マイグレーション(Migration)とは、英語で「移行」という意味です。
Railsでは、データベースの状態をある状態から別の状態へ「移行」させることを指します。例えば、「usersテーブルにemailカラムを追加する」といった変更が、1つのマイグレーションになります。
なぜマイグレーションが必要なのか
直接SQLでデータベースを変更することもできますが、マイグレーションを使うと以下のメリットがあります。
バージョン管理ができる
すべての変更が履歴として残り、いつでも過去の状態に戻せます。
チーム開発がしやすい
マイグレーションファイルをGitで共有すれば、チームメンバー全員が同じデータベース構造を持てます。
データベースに依存しない
Railsのコードで書くので、MySQL、PostgreSQL、SQLiteなど、どのデータベースでも同じマイグレーションファイルが使えます。
間違いを元に戻せる
ロールバック機能で、変更を取り消せます。
マイグレーションの基本構造
マイグレーションファイルがどんな構造になっているか見ていきましょう。
マイグレーションファイルの場所
マイグレーションファイルは、db/migrate/フォルダに保存されます。
ファイル名は以下のような形式です:
20250115123456_create_users.rb
最初の数字はタイムスタンプ(作成日時)で、マイグレーションの実行順序を決めます。
基本的なマイグレーションの構造
典型的なマイグレーションファイルはこんな感じです:
class CreateUsers < ActiveRecord::Migration[7.0]
def change
create_table :users do |t|
t.string :name
t.string :email
t.integer :age
t.timestamps
end
end
end
クラス名
CreateUsersのように、何をするマイグレーションかが分かる名前を付けます。
changeメソッド
データベースに対して行う変更を記述します。Railsは自動的に、変更を元に戻す方法も理解してくれます。
create_table
新しいテーブルを作成する命令です。
t.timestamps
created_at(作成日時)とupdated_at(更新日時)の2つのカラムを自動的に追加します。
マイグレーションの作成方法
新しいマイグレーションを作成する方法を説明します。
ジェネレーターを使う
Railsには、マイグレーションファイルを自動生成する便利なコマンドがあります。
テーブルを作成するマイグレーション
rails generate migration CreateUsers name:string email:string age:integer
または短縮形:
rails g migration CreateUsers name:string email:string age:integer
これで、db/migrate/フォルダに新しいマイグレーションファイルが作成されます。
カラムを追加するマイグレーション
既存のテーブルにカラムを追加する場合:
rails g migration AddPhoneToUsers phone:string
AddXxxToYyyという命名規則を使うと、Railsが自動的に適切なコードを生成してくれます。
カラムを削除するマイグレーション
rails g migration RemovePhoneFromUsers phone:string
RemoveXxxFromYyyという命名規則で、カラム削除のコードが生成されます。
データ型の指定
カラムを作成する時、様々なデータ型を指定できます。
主なデータ型
- string:短い文字列(255文字まで)
- text:長い文字列
- integer:整数
- bigint:大きな整数
- float:浮動小数点数
- decimal:正確な小数(金額など)
- boolean:真偽値(true/false)
- date:日付
- datetime:日時
- time:時刻
- binary:バイナリデータ
- json:JSON形式のデータ
- jsonb:JSON形式のデータ(PostgreSQL)
マイグレーションの実行
作成したマイグレーションを実行して、実際にデータベースを変更します。
マイグレーションの実行コマンド
すべての未実行マイグレーションを実行
rails db:migrate
このコマンドで、まだ実行されていないマイグレーションがすべて実行されます。
実行状況の確認
rails db:migrate:status
どのマイグレーションが実行済みで、どれが未実行かを確認できます。
実行の流れ
マイグレーションを実行すると、以下のことが起こります:
db/migrate/フォルダ内のマイグレーションファイルを確認- まだ実行されていないものを、古い順に実行
- 実行済みのマイグレーションを
schema_migrationsテーブルに記録
ロールバック(変更の取り消し)
間違えたり、やり直したい時は、ロールバックで元に戻せます。
基本的なロールバック
直前のマイグレーションを取り消す
rails db:rollback
最後に実行したマイグレーションが取り消されます。
複数のマイグレーションを取り消す
rails db:rollback STEP=3
STEP=3で、直近3つのマイグレーションを取り消します。
特定のバージョンまで戻る
rails db:migrate:down VERSION=20250115123456
特定のバージョン番号のマイグレーションだけを取り消します。
ロールバックの注意点
ロールバックは、データを失う可能性があります。
例えば、カラムを削除するマイグレーションをロールバックしても、削除されたデータは復活しません。本番環境では特に注意が必要です。
テーブル操作のマイグレーション
テーブルに関する様々な操作を見ていきましょう。
テーブルの作成
class CreateProducts < ActiveRecord::Migration[7.0]
def change
create_table :products do |t|
t.string :name, null: false
t.text :description
t.decimal :price, precision: 10, scale: 2
t.integer :stock, default: 0
t.boolean :available, default: true
t.timestamps
end
end
end
オプションの説明
null: false:NULL値を許可しない(必須項目)default: 0:デフォルト値を設定precision: 10, scale: 2:小数の桁数を指定(10桁中、小数点以下2桁)
テーブルの削除
class DropProducts < ActiveRecord::Migration[7.0]
def change
drop_table :products
end
end
注意:drop_tableはロールバックできません。データも完全に失われます。
テーブル名の変更
class RenameProductsToItems < ActiveRecord::Migration[7.0]
def change
rename_table :products, :items
end
end
カラム操作のマイグレーション
カラムの追加、削除、変更の方法を説明します。
カラムの追加
class AddCategoryToProducts < ActiveRecord::Migration[7.0]
def change
add_column :products, :category, :string
add_column :products, :published_at, :datetime
end
end
カラムの削除
class RemoveCategoryFromProducts < ActiveRecord::Migration[7.0]
def change
remove_column :products, :category, :string
end
end
注意:カラムを削除すると、そのカラムのデータも失われます。
カラム名の変更
class RenameProductNameToTitle < ActiveRecord::Migration[7.0]
def change
rename_column :products, :name, :title
end
end
カラムのデータ型変更
class ChangeProductPriceType < ActiveRecord::Migration[7.0]
def change
change_column :products, :price, :integer
end
end
注意:データ型を変更すると、既存のデータに影響が出る可能性があります。
カラムのオプション変更
class ChangeProductsColumns < ActiveRecord::Migration[7.0]
def change
change_column_null :products, :name, false # NOT NULL制約を追加
change_column_default :products, :stock, from: 0, to: 1 # デフォルト値を変更
end
end
インデックスの追加
インデックスを追加すると、データの検索が速くなります。
単一カラムのインデックス
class AddIndexToUsersEmail < ActiveRecord::Migration[7.0]
def change
add_index :users, :email
end
end
一意制約付きインデックス
class AddUniqueIndexToUsersEmail < ActiveRecord::Migration[7.0]
def change
add_index :users, :email, unique: true
end
end
unique: trueで、同じ値が重複して登録されるのを防ぎます。
複数カラムのインデックス
class AddIndexToOrders < ActiveRecord::Migration[7.0]
def change
add_index :orders, [:user_id, :created_at]
end
end
インデックスの削除
class RemoveIndexFromUsersEmail < ActiveRecord::Migration[7.0]
def change
remove_index :users, :email
end
end
外部キー制約
テーブル間の関連を強制する外部キー制約を追加できます。
外部キーの追加
class AddForeignKeyToOrders < ActiveRecord::Migration[7.0]
def change
add_foreign_key :orders, :users
end
end
これで、ordersテーブルのuser_idが、usersテーブルのidを参照するようになります。
リファレンスカラムの追加
外部キーとインデックスを同時に追加する便利な方法:
class AddUserToOrders < ActiveRecord::Migration[7.0]
def change
add_reference :orders, :user, foreign_key: true, index: true
end
end
これで、user_idカラム、インデックス、外部キー制約がすべて追加されます。
高度なマイグレーション
より複雑な操作が必要な場合の方法を紹介します。
upとdownメソッドを使う
changeメソッドで自動的にロールバックできない場合は、upとdownを明示的に定義します。
class AddCheckConstraintToProducts < ActiveRecord::Migration[7.0]
def up
execute <<-SQL
ALTER TABLE products
ADD CONSTRAINT check_price_positive
CHECK (price > 0)
SQL
end
def down
execute <<-SQL
ALTER TABLE products
DROP CONSTRAINT check_price_positive
SQL
end
end
データの変更
マイグレーションでデータを変更することもできます(ただし推奨されません)。
class UpdateExistingUserRoles < ActiveRecord::Migration[7.0]
def up
User.where(role: nil).update_all(role: 'member')
end
def down
# ロールバック時の処理
end
end
注意:大量のデータを扱う場合は、マイグレーションではなく、Rakeタスクを使う方が安全です。
可逆的でないマイグレーション
一部のマイグレーションは、自動的にロールバックできません。
class RemoveOldColumn < ActiveRecord::Migration[7.0]
def change
remove_column :users, :old_field
end
end
このような場合、ロールバック時にエラーが出ます。元に戻す必要がある場合は、upとdownを使います。
schema.rbの理解
マイグレーションを実行すると、db/schema.rbが自動的に更新されます。
schema.rbとは
schema.rbは、現在のデータベース構造の全体像を示すファイルです。
すべてのマイグレーションの結果を1つのファイルにまとめたものです。
schema.rbの例
ActiveRecord::Schema[7.0].define(version: 2025_01_15_123456) do
create_table "users", force: :cascade do |t|
t.string "name", null: false
t.string "email", null: false
t.integer "age"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.index ["email"], name: "index_users_on_email", unique: true
end
end
schema.rbの使い方
新しい環境でデータベースを構築
rails db:schema:load
このコマンドで、schema.rbを元にデータベースを一気に構築できます。すべてのマイグレーションを1つずつ実行するより高速です。
よくあるパターンと実例
実際の開発でよく使うマイグレーションのパターンを紹介します。
ユーザーテーブルの作成
class CreateUsers < ActiveRecord::Migration[7.0]
def change
create_table :users do |t|
t.string :name, null: false
t.string :email, null: false
t.string :password_digest
t.boolean :admin, default: false
t.timestamps
end
add_index :users, :email, unique: true
end
end
多対多の関連テーブル
class CreateUserGroups < ActiveRecord::Migration[7.0]
def change
create_table :user_groups do |t|
t.references :user, null: false, foreign_key: true
t.references :group, null: false, foreign_key: true
t.timestamps
end
add_index :user_groups, [:user_id, :group_id], unique: true
end
end
既存カラムへのNOT NULL制約追加
class AddNotNullToUserName < ActiveRecord::Migration[7.0]
def change
# まず、NULL値を持つレコードを更新
User.where(name: nil).update_all(name: 'Unknown')
# その後、NOT NULL制約を追加
change_column_null :users, :name, false
end
end
ベストプラクティス
マイグレーションを書く時の推奨される方法を紹介します。
1つのマイグレーションで1つのことをする
マイグレーションは小さく、明確にしましょう。
悪い例
class UpdateEverything < ActiveRecord::Migration[7.0]
def change
add_column :users, :phone, :string
remove_column :products, :old_field
create_table :categories do |t|
# ...
end
end
end
良い例
別々のマイグレーションに分ける:
AddPhoneToUsersRemoveOldFieldFromProductsCreateCategories
マイグレーションは削除しない
一度コミットしたマイグレーションは削除せず、新しいマイグレーションで修正します。
特に、他の開発者が既に実行している場合は絶対に削除してはいけません。
本番環境での注意
本番環境でマイグレーションを実行する前に:
- データのバックアップを取る
- ステージング環境で先にテストする
- ダウンタイム(サービス停止時間)を考慮する
- 大量データがある場合は、処理時間を見積もる
インデックスを忘れずに
外部キーカラムには、必ずインデックスを追加しましょう。
class AddUserToOrders < ActiveRecord::Migration[7.0]
def change
add_reference :orders, :user, foreign_key: true, index: true
end
end
よくあるトラブルと解決方法
マイグレーションで困った時の対処法を紹介します。
マイグレーションが実行できない
症状
rails db:migrateを実行してもエラーが出る。
原因と解決方法
構文エラー
マイグレーションファイルのRubyコードに間違いがあります。エラーメッセージを読んで、該当箇所を修正しましょう。
データベース接続エラー
database.ymlの設定を確認します。データベースサーバーが起動しているかも確認しましょう。
既にカラムが存在する
既に存在するカラムを追加しようとしている場合は、マイグレーションをロールバックしてから修正します。
ロールバックできない
症状
rails db:rollbackを実行してもエラーが出る。
解決方法
upとdownメソッドを明示的に定義します。
class MyMigration < ActiveRecord::Migration[7.0]
def up
# マイグレーション実行時の処理
end
def down
# ロールバック時の処理
end
end
マイグレーションの順序が狂った
症状
マイグレーションが期待した順序で実行されない。
原因
タイムスタンプが正しくない可能性があります。
解決方法
ファイル名の先頭にあるタイムスタンプを確認し、必要に応じて手動で調整します。ただし、これは最終手段です。
本番環境でマイグレーションが失敗
症状
本番環境でマイグレーションを実行したら、データが壊れた。
対処法
- すぐにサービスを停止
- バックアップからデータベースを復元
- 問題を修正してから再度実行
予防策
- 本番環境での実行前に、必ずバックアップを取る
- ステージング環境で先にテストする
- できれば、メンテナンス時間を設けて実行する
テストでのマイグレーション
テスト環境でマイグレーションを扱う方法です。
テストデータベースのセットアップ
rails db:test:prepare
このコマンドで、テスト用データベースがschema.rbを元に構築されます。
マイグレーションのテスト
マイグレーション自体をテストすることもできます。
require 'test_helper'
class CreateUsersTest < ActiveSupport::TestCase
def setup
@migration = CreateUsers.new
end
test "creates users table" do
@migration.migrate(:up)
assert ActiveRecord::Base.connection.table_exists?(:users)
end
end
まとめ:マイグレーションでデータベースを安全に管理
Rails Migrationsは、データベースの変更を安全かつ効率的に管理する強力な仕組みです。
この記事のポイント
- マイグレーションはデータベース変更の履歴管理システム
rails g migrationでマイグレーションファイルを作成rails db:migrateで実行、rails db:rollbackで取り消し- テーブル、カラム、インデックス、外部キーを管理できる
- 1つのマイグレーションで1つのことをする
- 本番環境では慎重に実行し、必ずバックアップを取る
- schema.rbで現在の構造を確認できる
マイグレーションを使えば、データベースの変更をコードとして管理でき、チーム開発もスムーズになります。最初は戸惑うかもしれませんが、慣れればとても便利な機能です。
Rails開発では、マイグレーションはほぼ毎日使う基本的なツールです。この記事を参考に、安全にデータベースを変更する方法を身につけてください。間違えてもロールバックで戻せるので、恐れずに試してみましょう。

コメント