假设我有一个模特:
class Result < ActiveRecord::Base
attr_accessible :x, :y, :sum
end
而不是做
Result.all.find_each do |s|
s.sum = compute_sum(s.x, s.y)
s.save
end
假设compute_sum
是一个可用的方法,并进行一些无法转换为SQL的计算。
def compute_sum(x,y)
sum_table[x][y]
end
有没有办法使用update_all
,可能是这样的:
Result.all.update_all(sum: compute_sum(:x, :y))
我要更新超过80,000条记录。 find_each
中的每条记录都会创建自己的BEGIN
和COMMIT
个查询,并且每个记录都会单独更新。
或者还有其他更快的方法吗?
答案 0 :(得分:3)
update_all
进行sql查询,因此对值进行的任何处理都需要在sql中。因此,您需要在您使用的任何DBMS中找到sql函数,以将两个数字相加。例如,在Postgres中,我相信你会这样做
Sum.update_all(sum: "x + y")
将生成此sql:
update sums set sum = x + y;
将计算每行的x + y值,并将sum字段设置为结果。
编辑 - 对于MariaDB。我从来没有使用过这个,但是一个快速的谷歌建议sql将是
update sums set sum = sum(x + y);
首先在sql控制台中尝试使用单个记录。如果它有效,那么你可以做
Sum.update_all(sum: "sum(x + y)")
在Rails中。
EDIT2:这里有很多名为sum
的东西,这使得这个例子非常混乱。这是一个更通用的例子。
将col_c设置为在类Foo中添加col_a和col_b的结果:
Foo.update_all(col_c: "sum(col_a + col_b)")
我刚注意到我已经从您的问题中复制了(错误的)Sum.all.update_all
。它应该是Sum.update_all
- 我已经更新了我的答案。
答案 1 :(得分:3)
如果compute_sum函数无法转换为sql,则不能一次对所有记录执行update_all。您需要遍历各个实例。但是,如果列中有很多重复的值集,只需对每组输入进行一次计算,然后每次计算进行一次质量更新,就可以加快速度。例如
Result.all.group_by{|result| [result.x, result.y]}.each do |inputs, results|
sum = compute_sum(*inputs)
Result.update_all('sum = #{sum}', "id in (#{results.map(&:id).join(',')})")
end
您可以将result.x,result.y替换为compute_sum函数的实际输入。
编辑 - 忘记在group_by块中的result.x,result.y周围放置方括号。
答案 2 :(得分:1)
我完全是初学者,只是想知道为什么不添加如下所示的自我阻止,而不在db中添加单独的列,你仍然可以从外部访问Sum.sum。
def self.sum
x+y
end