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

Eer*_*ero 28 data-migration ruby-on-rails rails-migrations

我需要将一些列从一个现有表移动到另一个表.如何使用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
Run Code Online (Sandbox Code Playgroud)

以上只是创建新列,但值保持为空...

我想避免登录到数据库以手动更新表.

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

Eer*_*ero 35

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

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
Run Code Online (Sandbox Code Playgroud)

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

  • 这不适用于 (PostgreSQL) 9.4.5。第一个逗号有语法错误。请参阅下面的更新版本 (2认同)

Oll*_*ett 11

以下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
Run Code Online (Sandbox Code Playgroud)

  • 请注意,这与[已接受的答案](/sf/answers/429527451/) 非常相似,但将“UPDATE users u,profiles p”替换为仅“UPDATE users u”以避免语法错误在以后的 Postgres 版本上。 (3认同)

and*_*ell 6

我会这样做三次迁移,或三次迁移.第一部分是添加列,第二部分是复制数据,第三部分是删除列.

这听起来像你要问的中间步骤,你可以通过循环所有用户并设置属性来在ruby中执行此操作,如下所示:

Users.each do |user|
   user.someprop = user.profile.some_prop
   user.save
end 
Run Code Online (Sandbox Code Playgroud)

我不喜欢这种做法,因为它非常慢.我建议像这样执行原始的sql:

execute "UPDATE users u, profiles p SET u.someprop=p.someprop WHERE u.id=p.user_id"
Run Code Online (Sandbox Code Playgroud)

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

  • 我刚刚发现*有*是一个非常好的理由将它分成三个独立的迁移.MySQL无法在事务中进行迁移,因此如果它在中途崩溃,那么手动清理就会一团糟.Postgres用户应该没问题. (2认同)
  • "在支持事务与更改架构(如PostgreSQL的或sqlite3的),迁移被包裹在一个事务语句的数据库,如果数据库不支持此(例如MySQL的),那么当迁移失败的它是成功的部分将不会回滚.你将不得不回滚手工制作的更改." (2013年2月15日--- http://guides.rubyonrails.org/migrations.html) (2认同)

pth*_*amm 5

该语法不适用于更高版本的 Postgres。对于@Eero's for Postges 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
Run Code Online (Sandbox Code Playgroud)