包括与条件的左连接 has_many 关联

Posted

技术标签:

【中文标题】包括与条件的左连接 has_many 关联【英文标题】:Include a left joined has_many association with condition 【发布时间】:2020-06-20 00:45:31 【问题描述】:

如果不触发 N+1 查询,我似乎无法执行关联查询。

假设我举办派对。我有很多朋友,每次有朋友参加聚会,他们都会创建一个 Presence。

所以:

Presence.belongs_to :party
Presence.belongs_to :friend

Friend.has_many :presences
Party.has_many :presences

到目前为止一切顺利。

我想获得我每个朋友的列表,知道他们是否在这个聚会上,而不触发 N+1 查询。

我的数据集如下所示:

friends: [
  name: "Dave Vlopment", presences: [created_at: "8pm", party_id: 2012],
  name: "Brett E. Hardproblem", presences: [nil],
  name: "Ann Plosswan-Quarry", presences: [created_at: "10pm", party_id: 2012],
  ...
]

等等。

当然,我有很多很多朋友,也参加过很多个聚会。 (这当然是一个虚构的例子。)

我愿意:

Friend.all.includes(:presence).map |them| them.parties 

# But then, `them.parties` is not filtered to tonight's party. 

Friend.all.includes(:presence).map |them| them.parties.where(party_id: pid) 

# And there I have an N+1.

我总是可以在 Ruby 层进行过滤:

Friend.all.includes(:presence).map |them| them.parties.select |it| it.party_id = party.id  

但这与as_json(includes: ) 等一起工作得非常糟糕。我发现这很容易出错,因为我将根据结果进行计算。

而且我举办了很多 派对,你知道吗? (仍然是虚构的)

如果我在第一个查询中的位置,我会丢失左连接:

Friend.all.includes(:presence).where(party: party)

我不知道今晚,布雷特和一帮一直在那里的朋友缺席了。 (不保证这是虚构的体验)

我只会看到在场的朋友。

如果我通过party,当然我也不会看到谁缺席。

现在我知道有一些方法可以在 SQL 中做到这一点,还有其他方法我们可以围绕一些 Ruby 将其整合在一起。

但是,我正在寻找一种在 Activerecord 中执行此操作的“一流”方法,而无需获得 N+1。

有没有办法只使用 Activerecord 工具来做到这一点?我还没有找到任何东西。

【问题讨论】:

【参考方案1】:

我不确定这是否符合您对“一流”方式的期望。

但是你可以使用这种方法来避免 N+1

  # fetch all friends
  friends = Friend.all

  # fetch all presences. grouped by friend_id
  grouped_presences = Presence.all.group_by(&:friend_id)

  # arrange data
  data = []
  friends.each do |friend|
    json = friend.as_json
    json["presences"] = grouped_presences[friend.id].as_json
    data << json
  end

  puts data

它只执行 2 个查询

SELECT `friends`.* FROM `friends`
SELECT `presences`.* FROM `presences`

【讨论】:

这只是有点hacky和手动,但它仍然是一个解决方案,所以即使它不是我正在寻找的,我也会将其视为积极贡献。谢谢!

以上是关于包括与条件的左连接 has_many 关联的主要内容,如果未能解决你的问题,请参考以下文章

与 where 子句不工作的左连接

左外连接和右外连接的区别

有条件的左连接

为啥Mysql的左连接条件不被遵守?

如何在 EF / EF Core 中的第二个表上实现具有某些条件的左连接?

外连接查询