0

我有以下(过于昂贵的数据库)方法:

    def reorder_area_routes_by_demographics!
      self.area_routes.joins(:route).order(self.demo_criteria, :proximity_rank).readonly(false).each_with_index do |area_route, i|
        area_route.update_attributes(match_rank: i)
      end
    end

但这会导致对每个 area_route 进行 UPDATE 查询。有没有办法在一个查询中做到这一点?

- 编辑 -

最终解决方案,根据 coreyward 的建议:

def reorder_area_routes_by_demographics!
  sorted_ids = area_routes.joins(:route).order(self.demo_criteria, :proximity_rank).pluck(:'area_routes.id')
  AreaRoute.update_all [efficient_sort_sql(sorted_ids), *sorted_ids], {id: sorted_ids}
end

def efficient_sort_sql(sorted_ids, offset=0)
  offset.upto(offset + sorted_ids.count - 1).inject('match_rank = CASE id ') do |sql, i|
    sql << "WHEN ? THEN #{id} "
  end << 'END'
end
4

1 回答 1

1

我使用以下内容来执行类似的任务:根据它们在参数中的顺序更新一组记录的排序位置。您可能需要以不同的方式重构或合并它以适应您正在应用的范围,但我认为这会让您朝着正确的方向前进。

def efficient_sort_sql(sortable_ids, offset = 1)
  offset.upto(offset + sortable_ids.count - 1).reduce('position = CASE id ') do |sql, i|
    sql << "WHEN ? THEN #{i} "
  end << 'END'
end

Model.update_all [efficient_sort_sql(sortable_ids, offset), *sortable_ids], { id: sortable_ids }

sortable_ids是一个整数数组,表示每个对象的 id。生成的 SQL 如下所示:

UPDATE pancakes SET position = CASE id WHEN 5 THEN 1 WHEN 3 THEN 2 WHEN 4 THEN 3 WHEN 1 THEN 4 WHEN 2 THEN 5 WHERE id IN (5,3,4,1,2);

除了丑陋之外,这是一个非常高效的查询,并且(至少在 Postgresql 中)将完全成功或完全失败。

于 2013-05-26T23:55:47.207 回答