Techioz Blog

ActiveRecord 結合に追加条件を適用する方法

概要

一定期間における部門ごとの事故数を計算してみようとします。したがって、テーブルが2つあります。 ActiveRecordを使用して答えを取得しようとしていますが、次のようになります

class Division < ApplicationRecord
  has_many :accidents

end

class Accident < ApplicationRecord
  belongs_to :division

end


Division.left_joins(:accidents).where('accidents.occurred_at > ?', Time.now - 1.year).group(:name).count

この場合、ActiveRecord はこの SQL を生成します

SELECT COUNT(accidents.id) AS "count_all", "divisions"."name" AS "divisions_name"
FROM "divisions"
LEFT OUTER JOIN "accidents" ON "accidents"."division_id" = "divisions"."id"
WHERE (accidents.occurred_at > '2022-07-30 20:56:10.178153')
GROUP BY "divisions"."name"

ここでの問題は、一部の部門の事故数が 0 の場合、クエリ結果に表示されないため、SQL を次のようにする必要があることです。

SELECT COUNT(accidents.id) AS "count_all", "divisions"."name" AS "divisions_name"
FROM "divisions"
LEFT OUTER JOIN "accidents" ON "accidents"."division_id" = "divisions"."id" and accidents.occurred_at > '2022-07-30 20:56:10.178153'
GROUP BY "divisions"."name"

追加の結合条件を指定することはできますか? has_many 関係に追加の条件を指定できることはわかっていますが、それは静的な条件になります。ユーザーのリクエストパラメータに応じて動的にしたい

結合条件 e.q に生の SQL を使用することを避けようとしています。

Division.joins("LEFT OUTER JOIN accidents ON accidents.division_id = divisions.id
and (accidents.occurred_at > '2022-07-30 20:56:10.178153'").group(:name).count('accidents.id')

解決策

最終的なクエリを簡素化するために JOIN でスコープを使用するとどうなるでしょうか?

class Division < ApplicationRecord
  scope :with_accidents_from, ->(occurred_at) do
    joins_query =
      sanitize_sql([
        'LEFT OUTER JOIN accidents ON accidents.division_id = divisions.id AND accidents.occurred_at > ?',
        occurred_at
      ])

    joins(joins_query)
  end
end

その後

Division.with_accidents_from(1.year.ago).group(:name).count('accidents.id')