如何对 ActiveRecord 联接应用其他条件

How apply additional conditions for ActiveRecord joins

提问人:David Asatryan 提问时间:7/31/2023 最后编辑:mechnicovDavid Asatryan 更新时间:9/13/2023 访问量:58

问:

假设我尝试计算一段时间内每个部门的事故计数。所以我有 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关系指定其他条件,但它将是一个静态条件。我希望它是动态的,具体取决于用户请求参数

我试图避免使用原始 sql 作为连接条件 e.q。

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')
SQL Ruby Rails-ActiveRecord

评论

0赞 Les Nightingill 7/31/2023
附加条件应是条款,而不是条款的一部分wherejoins
0赞 smathy 7/31/2023
不确定你所要求的东西是否会给你你所追求的。with 逻辑仅意味着右表 () 的字段将是 ,因此无论该条件是否存在,组/计数输出都将是相同的。LEFT JOINoccurred_ataccidentsnull
0赞 David Asatryan 7/31/2023
@smathy我纠正了问题,是的,我需要 COUNT(accidents.id)。因此,对于没有发生事故的部门,此查询应返回 0
0赞 David Asatryan 7/31/2023
@LesNightingill如果我使用 where 子句,我不会得到没有事故的除法。我需要单独收集它们
0赞 mechnicov 7/31/2023
我试图避免将原始 sql 用于连接条件——我认为将这种原始 SQL 用于 JOIN 是可以的。或者,您可以使用 Arel

答:

1赞 spickermann 7/31/2023 #1

我会尝试与这样的范围相关联

# in the model
class Division < ApplicationRecord
  has_many :accidents
  has_many :recent_accidents, class_name:  'Accident', 
                              foreign_key: 'division_id',
                              -> { where occurred_at: (1.year.ago..) }

并会像这样使用它:

Division.left_outer_joins(:recent_accidents)
        .group(:name)
        .count('accidents.id')

评论

0赞 David Asatryan 7/31/2023
谢谢,但是如果我想要occurred_at的动态参数,具体取决于用户参数怎么办?
0赞 spickermann 7/31/2023
然后,您将需要将 与较长的字符串一起使用。outer_left_joins
1赞 mechnicov 7/31/2023 #2

使用范围来简化最终查询是怎么回事?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')

评论

0赞 David Asatryan 7/31/2023
哇,有趣,看起来我想要的,谢谢,会尝试这种方法