Ruby-on-rails 在 Rails 迁移中将一列更新为另一列的值

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/5224881/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-03 00:36:30  来源:igfitidea点击:

Update one column to value of another in Rails migration

ruby-on-railsactiverecordmigrationtimestamp

提问by jrdioko

I have a table in a Rails app with hundreds of thousands of records, and they only have a created_attimestamp. I'm adding the ability to edit these records, so I want to add an updated_attimestamp to the table. In my migration to add the column, I want to update all rows to have the new updated_atmatch the old created_at, since that's the default for newly created rows in Rails. I could do a find(:all)and iterate through the records, but that would take hours because of the size of the table. What I really want to do is:

我在 Rails 应用程序中有一个包含数十万条记录的表,它们只有一个created_at时间戳。我正在添加编辑这些记录的功能,所以我想updated_at在表中添加一个时间戳。在我添加列的迁移中,我想更新所有行以使新行updated_at与旧行匹配created_at,因为这是 Rails 中新创建的行的默认设置。我可以执行 afind(:all)并遍历记录,但由于表的大小,这需要几个小时。我真正想做的是:

UPDATE table_name SET updated_at = created_at;

Is there a nicer way to do that in a Rails migration using ActiveRecord rather than executing raw SQL?

在使用 ActiveRecord 而不是执行原始 SQL 的 Rails 迁移中,有没有更好的方法来做到这一点?

回答by nathanvda

I would create a migration

我会创建一个迁移

rails g migration set_updated_at_values

and inside it write something like:

在里面写一些类似的东西:

class SetUpdatedAt < ActiveRecord::Migration
  def self.up
    Yourmodel.update_all("updated_at=created_at")
  end

  def self.down
  end
end

This way you achieve two things

这样你就可以实现两件事

  • this is a repeatable process, with each possible deploy (where needed) it is executed
  • this is efficient. I can't think of a more rubyesque solution (that is as efficient).
  • 这是一个可重复的过程,每个可能的部署(在需要的地方)都会执行
  • 这是有效的。我想不出更像红宝石的解决方案(效率一样高)。

Note: you could also run raw sql inside a migration, if the query gets too hard to write using activerecord. Just write the following:

注意:如果使用 activerecord 编写查询太难,您也可以在迁移中运行原始 sql。只需编写以下内容:

Yourmodel.connection.execute("update your_models set ... <complicated query> ...")

回答by Greg Dan

You can use update_allwhich works very similar to raw SQL. That's all options you have.

您可以使用与原始 SQL 非常相似的update_all。这就是你拥有的所有选择。

BTW personally I do not pay that much attention to migrations. Sometimes raw SQL is really best solution. Generally migrations code isn't reused. This is one time action so I don't bother about code purity.

顺便说一句,我个人不太关注迁移。有时原始 SQL 确实是最好的解决方案。通常不会重用迁移代码。这是一次性操作,所以我不关心代码纯度。

回答by Martin Streicher

As gregdan wrote, you can use update_all. You can do something like this:

正如 gregdan 所写,您可以使用update_all. 你可以这样做:

Model.where(...).update_all('updated_at = created_at')

The first portion is your typical set of conditions. The last portion says how to make the assignments. This will yield an UPDATEstatement, at least in Rails 4.

第一部分是您的典型条件集。最后一部分说明如何进行分配。这将产生一个UPDATE声明,至少在 Rails 4 中是这样。

回答by Sarwan Kumar

You can directly run following command to your rails consoleActiveRecord::Base.connection.execute("UPDATE TABLE_NAME SET COL2 = COL1")

您可以直接运行以下命令到您的 rails consoleActiveRecord::Base.connection.execute("UPDATE TABLE_NAME SET COL2 = COL1")

For example: I want to update sku of my items table with remote_id of items tables. the command will be as following:
ActiveRecord::Base.connection.execute("UPDATE items SET sku = remote_id")

例如:我想用 items 表的 remote_id 更新我的 items 表的 sku。命令如下:
ActiveRecord::Base.connection.execute("UPDATE items SET sku = remote_id")

回答by Wilson Varghese

This is a General way of solving, without the need for Writing Query, as queries are subjected to risk.

这是一种通用的解决方法,不需要编写查询,因为查询存在风险。

  class Demo < ActiveRecord::Migration
    def change
     add_column :events, :time_zone, :string
     Test.all.each do |p|
       p.update_attributes(time_zone: p.check.last.time_zone)
     end
     remove_column :sessions, :time_zone
    end
  end

回答by ghoppe

As a one time operation, I would just do it in the rails console. Will it really take hours? Maybe if there are millions of records…

作为一次性操作,我只会在rails console. 真的需要几个小时吗?也许如果有数百万条记录......

records = ModelName.all; records do |r|; r.update_attributes(:updated_at => r.created_at); r.save!; end;`