sumなど集計関数と一緒にselect利用してゴニョゴニョする


はじめに

ちょっと自分のプロジェクトで悩んでいる人がいたのと、それを説明している文書になかなかたどり着けない気がするので書いておく。あまり自分も使い込んでいないので、間違いがあったりしたらツッコミ大歓迎です。

前提

下記のような親子構造をしている。

Project --* ProjectUser --* Work

Workは下記のようなプロパティを持つ前提。

  • work_time 数値
  • extra_cost 数値

課題

Projectに属するproject_userの一覧を取得して、さらにそのそれぞれが持っているWorkwork_timeextra_cost を全て合算したい(本当はもう少し複雑)。

対応

下記のようにgroupselectを使うといい感じになる。

  # Project

  def calc_total
    project_users.joins(:works)
      .group(ProjectUser.arel_table[:id]).select('project_users.*, sum(work_time) as sum_work_times, sum(extra_cost) as sum_extra_costs')
  end

project.project_usersのそれぞれのproject_userに集計結果が入っている形になるので下記のように扱える。

@project_users = project.calc_total
@project_users.each do |project_user|
  # 下記のようなProjectUserの持っているメソッドが呼べる
  project_user.id
  project_user.payment

  # 集計関数で集計した値も呼べる。メソッドでもいけたっけ?
  project_user[:sum_work_times]
  project_user[:sum_extra_costs]
end

おまけ

selectarel_tableを使ってもっと良い感じになる?
http://qiita.com/yamagen0915/items/b1721a9d1ea076f8cdc5

  # Project

  def calc_total
    project_user_table = ProjectUser.arel_table
    work_table = Work.arel_table
    project_users.joins(:works)
      .group(project_user_table[:id])
      .select(project_user_table) # これで * 取れるんだっけ。調べんと。
      .select(work_table[:work_time].sum.as('sum_work_times'))
      .select(work_table[:extra_cost].sum.as('sum_extra_costs'))
  end

なんか squeel よさげ。
http://labs.timedia.co.jp/2013/11/activerecord4sql-squeel.html