我在Rails应用程序中有一个包含数十万条记录的表,它们只有created_at
个时间戳。我正在添加编辑这些记录的功能,因此我想在表中添加updated_at
时间戳。在我添加列的迁移中,我想更新所有行以使新updated_at
与旧created_at
匹配,因为这是Rails中新创建的行的默认值。我可以做一个find(:all)
并遍历记录,但由于表的大小,这需要几个小时。我真正想做的是:
UPDATE table_name SET updated_at = created_at;
在使用ActiveRecord进行Rails迁移而不是执行原始SQL时,有没有更好的方法呢?
答案 0 :(得分:112)
我会创建一个迁移
rails g migration set_updated_at_values
并在里面写下类似的内容:
class SetUpdatedAt < ActiveRecord::Migration
def self.up
Yourmodel.update_all("updated_at=created_at")
end
def self.down
end
end
这样你就可以实现两件事
注意:如果查询太难以使用activerecord写入,您也可以在迁移中运行原始sql。请写下以下内容:
Yourmodel.connection.execute("update your_models set ... <complicated query> ...")
答案 1 :(得分:20)
您可以使用与原始SQL非常相似的update_all。这就是你拥有的所有选择。
BTW个人而言,我并不太关注迁移。有时原始SQL是最好的解决方案。通常,迁移代码不会被重用。这是一次性操作,所以我不打扰代码纯度。答案 2 :(得分:10)
正如gregdan所写,你可以使用update_all
。你可以这样做:
Model.where(...).update_all('updated_at = created_at')
第一部分是您的典型条件。最后一部分说明了如何进行分配。这将产生UPDATE
语句,至少在Rails 4中。
答案 3 :(得分:0)
这是一种通用的解决方法,无需编写查询,因为查询会面临风险。
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
答案 4 :(得分:0)
您可以直接对您的rails console
运行以下命令
ActiveRecord::Base.connection.execute("UPDATE TABLE_NAME SET COL2 = COL1")
例如:我想用项目表的remote_id更新项目表的sku。该命令将如下所示:
ActiveRecord::Base.connection.execute("UPDATE items SET sku = remote_id")
答案 5 :(得分:0)
不要在迁移中使用应用程序模型,除非您在迁移中重新定义它们。如果您使用应用程序模型,您以后更改或删除迁移可能会失败。
当然,您也可以在迁移中使用 SQL 的全部功能。
阅读https://makandracards.com/makandra/15575-how-to-write-complex-migrations-in-rails
答案 6 :(得分:0)
您还可以添加 updated_at 列并在一次迁移中更新其值:
class AddUpdatedAtToTableName < ActiveRecord::Migration
def change
add_column :table_name, :updated_at, :datetime
reversible do |dir|
dir.up do
update "UPDATE table_name SET updated_at=created_at"
end
end
end
end
答案 7 :(得分:-3)
作为一次性操作,我只会在rails console
中执行此操作。真的需要几个小时吗?也许如果有数百万条记录......
records = ModelName.all; records do |r|; r.update_attributes(:updated_at => r.created_at); r.save!; end;`