Posted at

複合ユニークインデックスに含まれる外部キー1個を別の外部キーに置き換えるマイグレーション

More than 1 year has passed since last update.

Rails 5.1を前提とします。


やりたいこと

次のようなDBスキーマがあるとします。


schema.rbの抜粋

create_table "categories", force: :cascade do |t|

# ...
end

create_table "products", force: :cascade do |t|
# ...
end

create_table "sub_products", force: :cascade do |t|
# ...
end

create_table "category_products", force: :cascade do |t|
t.bigint "category_id"
t.bigint "product_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.index ["category_id", "product_id"], unique: true
t.index ["product_id"]
end

# ...

add_foreign_key "category_products", "categories"
add_foreign_key "category_products", "products"


このときに、category_products の複合ユニークインデックス [:category_id, :product_id][:category_id, :sub_product_id] に変更するマイグレーションを作りたい場合を考えます。

ここで、マイグレーションはリバーシブル(マイグレーションを up, down したときに正しく前後のDBスキーマに変更できること)になるように気をつけます。


やりかた

change_table を使って、次のようなマイグレーションを作ります。

class ChangeReferenceInCategoryProducts < ActiveRecord::Migration[5.1]

def change
change_table :category_products do |t|
t.remove_index column: [:category_id, :product_id], unique: true
t.remove_references :product, foreign_key: true
t.references :sub_product, foreign_key: true
t.index [:category_id, :sub_product_id], unique: true
end
end
end

まず、remove_index で複合ユニークインデックスを削除します。このときに unique: true を指定しておかないと、db:migrate:down 時に、この複合インデックスをユニーク制約が付いた状態で復元できません。

その後、remove_references で外部キーカラム product_id を削除します。

この後は新しい外部キーカラムと複合ユニークインデックスを作ります。削除とは逆に、カラム作成→複合インデックス作成の順番で実行しており、マイグレーション全体として処理が対称になります。

以上の方法で、db:migrateup, down を行き来できるようになります。


参考