LoginSignup
3
2

More than 5 years have passed since last update.

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

Posted at

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 を行き来できるようになります。

参考

3
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
2