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