使用Rails中的Active Record在一个查询中更新更多记录

时间:2013-08-30 15:59:08

标签: sql ruby-on-rails ruby-on-rails-4 arel

有没有更好的方法在Ruby on Rails中使用不同的值在一个查询中更新更多记录?我在SQL中使用CASE解决了,但是有没有Active Record解决方案?

基本上,当新列表从jquery ajax帖子返回时,我保存了一个新的排序顺序。

#List of product ids in sorted order. Get from jqueryui sortable plugin.
#product_ids = [3,1,2,4,7,6,5]

# Simple solution which generate a loads of queries. Working but slow.
#product_ids.each_with_index do |id, index|
#  Product.where(id: id).update_all(sort_order: index+1)
#end

##CASE syntax example:
##Product.where(id: product_ids).update_all("sort_order = CASE id WHEN 539 THEN 1 WHEN 540 THEN 2 WHEN 542 THEN 3 END")

case_string = "sort_order = CASE id "      

product_ids.each_with_index do |id, index|
  case_string += "WHEN #{id} THEN #{index+1} "
end

case_string += "END"

Product.where(id: product_ids).update_all(case_string)

这个解决方案工作速度很快,只有一个查询,但是我在php中创建了一个查询字符串。 :)你的建议是什么?

1 个答案:

答案 0 :(得分:2)

您应该查看acts_as_list gem。它可以完成您需要的一切,并在幕后使用1-3个查询。它与jquery可排序插件的完美匹配。它依赖于在SQL中直接递增/递减位置(sort_order)字段。

如果您的UI / UX依赖于用户手动保存订单(用户对事物进行排序然后单击更新/保存),这对您来说不是一个好的解决方案。但是我强烈反对这种接口,除非有特定的原因(例如,你不能在新旧订单之间的数据库中有中间状态,因为其他东西依赖于那个顺序)。

如果不是这种情况,那么在用户移动一个元素之后,一定要进行异步更新(并且acts_as_list对于帮助您实现这一点很有帮助)。

退房:

https://github.com/swanandp/acts_as_list/blob/master/lib/acts_as_list/active_record/acts/list.rb#L324

# This has the effect of moving all the higher items down one.
def increment_positions_on_higher_items
  return unless in_list?
  acts_as_list_class.unscoped.where(
    "#{scope_condition} AND #{position_column} < #{send(position_column).to_i}"
  ).update_all(
    "#{position_column} = (#{position_column} + 1)"
  )
end