update_all
如果我想用各种不同的值更新包含 300,000 条记录的列,我该如何使用?
我想做的是:
Model.update_all(:column => [2,33,94,32]).where(:id => [22974,22975,22976,22977])
但不幸的是,这不起作用,对于 300,000 个条目来说情况更糟。
update_all
如果我想用各种不同的值更新包含 300,000 条记录的列,我该如何使用?
我想做的是:
Model.update_all(:column => [2,33,94,32]).where(:id => [22974,22975,22976,22977])
但不幸的是,这不起作用,对于 300,000 个条目来说情况更糟。
people = { 1 => { "first_name" => "David" }, 2 => { "first_name" => "Jeremy" } }
Person.update(people.keys, people.values)
所以在你的情况下:
updates = {22974 => {column: 2}, 22975 => {column: 33}, 22976 => {column: 94}, 22977 => {column: 32}}
Model.update(updates.keys, updates.values)
编辑:刚刚查看了源代码,这也生成了n 个SQL 查询......所以可能不是最好的解决方案
我发现这样做的唯一方法是生成带有更新值的 INSERT INTO 请求。我为此使用gem“activerecord-import”。
例如,我有一个带有val值的表
+--------+--------------+---------+------------+-----+-------------------------+-------------------------+
| pkey | id | site_id | feature_id | val | created_at | updated_at |
+--------+--------------+---------+------------+-----+-------------------------+-------------------------+
| 1 | | 125 | 7 | 88 | 2016-01-27 10:25:45 UTC | 2016-02-05 11:18:14 UTC |
| 111765 | 0001-0000024 | 125 | 7 | 86 | 2016-01-27 11:33:22 UTC | 2016-02-05 11:18:14 UTC |
| 111766 | 0001-0000062 | 125 | 7 | 15 | 2016-01-27 11:33:22 UTC | 2016-02-05 11:18:14 UTC |
| 111767 | 0001-0000079 | 125 | 7 | 19 | 2016-01-27 11:33:22 UTC | 2016-02-05 11:18:14 UTC |
| 111768 | 0001-0000086 | 125 | 7 | 33 | 2016-01-27 11:33:22 UTC | 2016-02-05 11:18:14 UTC |
+--------+--------------+---------+------------+-----+-------------------------+-------------------------+
products = CustomProduct.limit(5)
products.each_with_index{|p, i| p.val = i}
CustomProduct.import products.to_a, :on_duplicate_key_update => [:val]
您的所有记录将在单个请求中更新。请查看gem "activerecord-import"文档以获取更多详细信息。
+--------+--------------+---------+------------+-----+-------------------------+-------------------------+
| pkey | id | site_id | feature_id | val | created_at | updated_at |
+--------+--------------+---------+------------+-----+-------------------------+-------------------------+
| 1 | | 125 | 7 | 0 | 2016-01-27 10:25:45 UTC | 2016-02-05 11:19:49 UTC |
| 111765 | 0001-0000024 | 125 | 7 | 1 | 2016-01-27 11:33:22 UTC | 2016-02-05 11:19:49 UTC |
| 111766 | 0001-0000062 | 125 | 7 | 2 | 2016-01-27 11:33:22 UTC | 2016-02-05 11:19:49 UTC |
| 111767 | 0001-0000079 | 125 | 7 | 3 | 2016-01-27 11:33:22 UTC | 2016-02-05 11:19:49 UTC |
| 111768 | 0001-0000086 | 125 | 7 | 4 | 2016-01-27 11:33:22 UTC | 2016-02-05 11:19:49 UTC |
+--------+--------------+---------+------------+-----+-------------------------+-------------------------+
你的问题的简短回答是,你不能。
update_all 的要点是为所有记录的列分配相同的值(如果提供,则匹配条件)。有用的原因是它在单个 SQL 语句中完成。
我同意 Shime 的正确答案。虽然这会产生n 个SQL 调用。所以,也许你的问题还有更多你没有告诉我们的东西。也许您可以遍历每个可能的值,为应该使用该值更新的对象调用 update_all。然后是构建适当的哈希,或者更好的是,如果条件基于模型本身的某些内容,您可以将条件传递给 update_all。
这是我 2020 年的回答:
投票最多的答案是错误的;正如作者自己所说,它将触发n
SQL 查询,每行一个。
第二个最受好评的答案建议 gem "activerecord-import",这是要走的路。但是,它是通过实例化 ActiveRecord 模型来实现的,如果您正在为这样的 gem 做生意,那么您可能正在寻找极端的性能(无论如何都是我们的情况)。
所以这就是我们所做的。首先,您构建一个哈希数组,每个哈希包含id
您要更新的记录和任何其他字段。
例如:
records = [{ id: 1, name: 'Bob' }, { id: 2, name: 'Wilson' },...]
然后你像这样调用 gem:
YourModelName.import(records, on_duplicate_key_update: [:name, :other_columns_whose_keys_are_present_in_the_hash], validate: false, timestamps: false)
解释:
on_duplicate_key_update
这意味着,如果数据库在主键上发现冲突(并且它会在每一行上,因为我们正在讨论更新现有记录),它不会失败,而是更新您在该数组上传递的列。
如果您不这样做validate false
(默认为 true),它将尝试为每一行实例化一个新的模型实例,并且可能由于验证而失败(因为您的哈希仅包含部分信息)。
timestamp false
也是可选的,但很高兴知道它在那里。