我在 Rails 应用程序中有一个包含数十万条记录的表,它们只有一个 created_at
时间戳。我正在添加编辑这些记录的功能,因此我想在表中添加一个 updated_at
时间戳。在添加列的迁移中,我想更新所有行以使新的 updated_at
与旧的 created_at
匹配,因为这是 Rails 中新创建的行的默认设置。我可以执行 find(:all)
并遍历记录,但由于表的大小,这需要几个小时。我真正想做的是:
UPDATE table_name SET updated_at = created_at;
在使用 ActiveRecord 而不是执行原始 SQL 的 Rails 迁移中,有没有更好的方法来做到这一点?
我会创建一个迁移
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
这样你就实现了两件事
这是一个可重复的过程,每次可能的部署(需要时)都会执行
这是有效的。我想不出一个更 rubyesque 的解决方案(同样有效)。
注意:如果查询太难使用 activerecord 编写,您也可以在迁移中运行原始 sql。只需编写以下内容:
Yourmodel.connection.execute("update your_models set ... <complicated query> ...")
您可以使用与原始 SQL 非常相似的 update_all。这就是您拥有的所有选择。
顺便说一句,我个人不太关注迁移。有时原始 SQL 确实是最好的解决方案。通常不会重用迁移代码。这是一次性操作,所以我不关心代码纯度。
update_all
的文章 :-) 您还可以在迁移文件中执行原始 SQL。但是 update_all
更优雅一些。两者的表现完全相同。
update_all
,我不知道如何按照 OP 的要求将列的值设置为另一个列的值。请示范。
正如 gregdan 所写,您可以使用 update_all
。你可以这样做:
Model.where(...).update_all('updated_at = created_at')
第一部分是您的典型条件。最后一部分说明了如何进行分配。这将产生一个 UPDATE
语句,至少在 Rails 4 中是这样。
SET'posts'.'email' = 'options'
,选项是文字字符串
User.update_all('updated_at = created_at')
SQL (0.4ms) UPDATE "users" SET updated_at = created_at
您可以直接对您的 rails console
ActiveRecord::Base.connection.execute("UPDATE TABLE_NAME SET COL2 = COL1")
运行以下命令
例如:我想用 items 表的 remote_id 更新我的 items 表的 sku。命令如下:
ActiveRecord::Base.connection.execute("UPDATE items SET sku = remote_id")
Yourmodel
可能已经被删除。尽量避免在迁移中使用模型。
不要在迁移中使用应用程序模型,除非您在迁移中重新定义它们。如果您使用应用程序模型,您以后更改或删除迁移可能会失败。
当然,您也可以在迁移中使用 SQL 的全部功能。
阅读https://makandracards.com/makandra/15575-how-to-write-complex-migrations-in-rails
这是一种通用的解决方法,无需编写查询,因为查询存在风险。
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
您还可以添加 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
作为一次性操作,我会在 rails console
中执行此操作。真的需要几个小时吗?也许如果有数百万条记录……
records = ModelName.all; records do |r|; r.update_attributes(:updated_at => r.created_at); r.save!; end;`
Yourmodel.update_all 'update_at=created_at'
更好,不是吗?它也适用于范围。up
后跟down
”,则数据库架构应保持不变”。所以只考虑def change
。change
方法还不存在,但在这种情况下,我仍然更喜欢使用显式up
和down
来更明确(如果你想控制什么down
应该这样做)。