LoginSignup
4
4

More than 5 years have passed since last update.

[リファクタリングRails]更新頻度が低いカラムだけ別テーブルに抽出

Last updated at Posted at 2016-06-19

どんなときに有効?

  • 1つのテーブルの中に更新/参照頻度が高いカラムと低いカラムが混在している
  • 1つのmodelに大量のビジネスロジックがあって可読性が低い

カラムを抽出するメリット

  • memcache などキャッシュを使っている場合に無駄が減る
    • 参照頻度の高いデータだけキャッシュされるべき
  • 更新頻度の多い情報のサイズは小さいほうが良い
    • DBのメモリになるべく乗るように

User modelの中に、更新/参照頻度が低い profile 関係の情報がある

  • zipcode
  • prefecture_code
  • city_code
  • address

User model

$ rails g scaffold user name:string mail:string zipcode:string prefecture_code:integer city_code:integer address:string
db/schema.rb
  create_table "users", force: :cascade do |t|
    t.string   "name"
    t.string   "mail"
    t.string   "zipcode"
    t.integer  "prefecture_code"
    t.integer  "city_code"
    t.string   "address"
    t.datetime "created_at",      null: false
    t.datetime "updated_at",      null: false
  end

テーブルを分割する

1. Profile モデル作成

$ rails g model profile zipcode:string prefecture_code:integer city_code:integer address:string user_id:integer

2. Profile モデルと、Userモデルを関連付け

profile.rb
class Profile < ActiveRecord::Base
  belongs_to :user
end

Profile model を delegate することで、既存コードの修正が不要に

user.rb
class User < ActiveRecord::Base
  # delegateすることで、Profile モデルの中身を User から取得できる 
  delegate :zipcode, :prefecture_code, :city_code, :address, to: :profile
  has_one :profile
end

3. マイグレーションを作成

  1. Profileテーブルを作る
  2. Profileテーブルに移行する情報をUserモデルからコピー
  3. Userテーブルから、Profileテーブルに移ったカラムを削除
db/migrate/20160619032503_create_profiles.rb
class CreateProfiles < ActiveRecord::Migration
  def up
    # Profileテーブルを作る
    create_table :profiles do |t|
      t.string :zipcode
      t.integer :prefecture_code
      t.integer :city_code
      t.string :address
      t.integer :user_id

      t.timestamps null: false
    end

    # Profileテーブルに移行する情報をUserモデルからコピー
    User.all.each do |user|
      Profile.create(
        user:            user,
        zipcode:         user.zipcode,
        prefecture_code: user.prefecture_code,
        city_code:       user.city_code,
        address:         user.address
      )
    end

    # Userテーブルから、Profileテーブルに移ったカラムを削除
    remove_column :users, :zipcode
    remove_column :users, :prefecture_code
    remove_column :users, :city_code
    remove_column :users, :address
  end

  def down
    add_column :users, :zipcode
    add_column :users, :prefecture_code
    add_column :users, :city_code
    add_column :users, :address

    # Profileモデルの情報をUserモデルに移行
    Profile.each do |profile|
      profile.user.update(
        zipcode:         profile.zipcode,
        prefecture_code: profile.prefecture_code,
        city_code:       profile.city_code,
        address:         profile.address
      )
    end

    drop_table :profiles
  end
end

Profile情報が必要な時だけ、Profileテーブルを参照するようになった

irb(main):010:0> User.all.first.zipcode
  User Load (0.3ms)  SELECT  "users".* FROM "users"  ORDER BY "users"."id" ASC LIMIT 1
  Profile Load (0.1ms)  SELECT  "profiles".* FROM "profiles" WHERE "profiles"."user_id" = ? LIMIT 1  [["user_id", 1]]
=> "419-4284"

irb(main):011:0> User.all.first.name
  User Load (0.2ms)  SELECT  "users".* FROM "users"  ORDER BY "users"."id" ASC LIMIT 1
=> "木村 優衣"

この後やるべきこと

  • 必然性があまりないのに、Profileの情報を参照している部分を修正する

code

参考

4
4
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
4
4