34

我需要将一些列从一个现有表移动到另一个。如何使用 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

上面只是创建了新列,但值是空的......

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

如果有办法以编程方式移动列值,性能特征是什么?它会逐行进行,还是有办法批量更新?

4

7 回答 7

40

我最终使用了这个迁移(经过测试,它可以工作,并且成功回滚):

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

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

于 2011-05-26T09:04:57.180 回答
13

以下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
于 2016-09-05T12:14:41.097 回答
8

我会将其作为三个迁移或三部分迁移来完成。第一部分是添加列,第二部分是复制数据,第三部分是删除列。

听起来中间步骤就是您要问的,您可以通过遍历所有用户并设置属性在 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"

这些都假设您的个人资料/用户关联,如果我假设错误,您可以调整。

于 2011-05-26T08:25:05.230 回答
5

该语法不适用于更高版本的 Postgres。对于 Postges 9.4.5 的 @Eero 的更新答案,请执行以下操作:

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
于 2016-01-26T16:34:28.730 回答
2

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

于 2012-11-13T16:10:29.773 回答
1

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

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
于 2018-05-31T17:26:41.217 回答
-1

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

" UPDATE users u
  SET someprop = (SELECT p.someprop
                  FROM profiles p
                  WHERE u.id = p.user_id );"
于 2014-11-20T15:09:52.310 回答