如何在Rails迁移中将一列(包含内容)移动到另一个表



我需要将一些列从一个现有表移动到另一个表。如何使用rails迁移来完成此操作?

class AddPropertyToUser < ActiveRecord::Migration
  def self.up
    add_column :users, :someprop, :string
    remove_column :profiles, :someprop
  end
  def self.down
    add_column :profiles, :someprop, :string
    remove_column :users, :someprop
  end
end

上面只创建了新列,但值为空。。。

我希望避免登录数据库手动更新表。

如果有一种以编程方式移动列值的方法,那么性能特征是什么?它会一行一行地更新,还是有一种批量更新的方法?

我最终使用了这个迁移(经过测试,它有效,并成功回滚):

class AddPropertyToUser < ActiveRecord::Migration
  def self.up
    add_column :users, :someprop, :string
    execute "UPDATE users u, profiles p SET u.someprop = p.someprop WHERE u.id = p.user_id"
    remove_column :profiles, :someprop
  end
  def self.down
    add_column :profiles, :someprop, :string
    execute "UPDATE profiles p, users u SET p.someprop = u.someprop WHERE p.user_id = u.id"
    remove_column :users, :someprop
  end
end

我喜欢它,因为它避免了在大型数据库中逐行更新。

以下UPDATE语法适用于最新的Postgres版本,并避免了子查询:

class MoveSomePropertyToUser < ActiveRecord::Migration
  def self.up
    add_column :users, :some_property, :string
    execute "UPDATE users u SET some_property = p.some_property FROM profiles p WHERE u.id = p.user_id;"
    remove_column :profiles, :some_property
  end
  def self.down
    add_column :profiles, :some_property, :string
    execute "UPDATE profiles p SET some_property = u.some_property FROM users u WHERE p.user_id = u.id;"
    remove_column :users, :some_property
  end
end

我会将此操作分为三次迁移或三部分迁移。第一部分是添加列,第二部分是复制数据,第三部分是删除列。

听起来中间一步就是你要问的,你可以在ruby中通过循环所有用户并设置属性来做到这一点,比如:

Users.each do |user|
   user.someprop = user.profile.some_prop
   user.save
end 

我不喜欢这种方式,因为它非常慢。我建议执行这样的原始sql:

execute "UPDATE users u, profiles p SET u.someprop=p.someprop WHERE u.id=p.user_id"

这两者都假设了您的个人资料/用户关联,如果我假设错了,您可以进行调整。

该语法不适用于Postgres的后续版本。有关@Eero's对帖子9.4.5的最新回复,请执行以下操作:

class AddPropertyToUser < ActiveRecord::Migration
  def self.up
    add_column :users, :someprop, :string
    execute "UPDATE users u SET someprop = (SELECT p.someprop FROM profiles p WHERE u.id = p.user_id);"
    remove_column :profiles, :someprop
  end
  def self.down
    add_column :profiles, :someprop, :string
    execute "UPDATE profiles p SET someprop = (SELECT u.someprop FROM users u WHERE p.user_id = u.id);"
    remove_column :users, :someprop
  end
end

您可以使用update_all和/或find_each 来避免硬编码的、特定于数据库的sql语句

这就是我在项目中所做的:-

class MoveColumnDataToUsersTable < ActiveRecord::Migration[5.1]
  def up
    add_column :users, :someprop, :string
    User.find_each do |u|
        Profile.create!(user_id: u.id, someprop: someprop)
    end
    remove_column :profiles, :someprop
  end
  def down
    add_column :profiles, :someprop, :someprop_data_type
    Profile.find_each do |p|
      User.find_by(id: p.user_id).update_columns(someprop: p.someprop)   
    end
    Profile.destroy_all
  end
end

对我(postgreSQL 9.1)来说,RAW SQL不起作用。我把它改了:

" UPDATE users u
  SET someprop = (SELECT p.someprop
                  FROM profiles p
                  WHERE u.id = p.user_id );"

相关内容

最新更新