首页 > 解决方案 > 急切加载具有多个聚合的多个关联的“Rails 方式”是什么?

问题描述

我有一个简单的 Rails 应用程序用于与这些模型的比赛:UserTeamTrip。关联是:team has_many usersuser has_many trips。Trips 有一个distance_miles字段。

我正在研究一个简单的视图来显示一个包含多个聚合的统计数据的团队表。

我最初的方法如下,但会导致 N+1 个查询。

@teams.each do |team|
    @team.user.joins(:trips).count()
    @team.user.joins(:trips).sum(:distance_miles)
end

以下工作,但看起来很难看,因为我想最终添加分页、排序和过滤。

@teams = Team.left_joins(users: :trips)
             .select('teams.id, teams.name, SUM(trips.distance_miles) as num_miles, COUNT(trips.id) as num_trips')
             .group(:id)

我一直在阅读预加载和包含但似乎无法让它也获得多个聚合。下面的内容让我走到了那里,但它现在缺少 Team 中的字段,并且仍然需要其他聚合:

@teams = Team.includes(users: :trips).group(:id).sum('trips.distance_miles')

有没有我错过的“铁路方式”?

标签: ruby-on-railsrubyruby-on-rails-6

解决方案


ActiveRecord::Calculations仅在非常简单的情况下才真正有用,因为它总是创建一个单独的数据库查询.sum.count

您在正确的轨道上,.select但可以通过将代码提取到模型中来清理它:

class Team
  def self.with_trips
    trips = Trip.arel_table
    self.left_joins(users: :trips)
        .select(
          self.arel.projections, # adds the existing select clause
          trips[:distance_miles].sum.as(:num_miles),
          trips[:id].count.as(:distance_miles)
       )
       .group(:id)
  end
end

使用.eager_load而不是.left_joins会在 Postgres 上导致 PG::GroupingError,因为结果会模棱两可。相反,您需要使用窗口函数


推荐阅读