如何使用Rails ActiveRecord进行LEFT OUTER JOIN?

22

我没有任何想法。你能给我一些线索(例如参考网站)吗?非常感谢。

Model1: GROUP(id, name)
Model2: USER_GROUP_CMB(id, user_id, group_id)

期望的SQL语句:

SELECT * 
FROM groups AS g LEFT OUTER JOIN user_group_cmbs AS cmb 
            ON g.id = cmb.group_id
WHERE cmb.user_id = 1

我尝试设置下面的关联,但不知道之后该怎么做。

class Group < ActiveRecord::Base
  has_many :user_group_cmb
end

class UserGroupCmb < ActiveRecord::Base
  has_many :group
end

Rails版本:3.1.1


使用Squeel(参见Rails 4中的LEFT OUTER JOIN - Yarin
Rails 5有一个开箱即用的解决方案!请参见下面的答案 - Ollie Bennett
8个回答

34

我相信如果您在includes关联表上使用条件语句,它将使用LEFT OUTER JOIN查询:

Group.includes(:user_group_cmb).where(user_group_cmbs: { user_id: 1 })

7
在这里无法工作(Rails 3.2.7):在includes引用的关系上使用条件会引发一个“StatementInvalid”异常,抱怨它缺少一个“FROM-clause entry”。我需要添加一个显式调用join,默认为inner。 - MattiSG
你对 https://dev59.com/l2sz5IYBdhLWcg3wQFYq#14599174 有什么看法? - user191766
1
如果您担心过滤,则可以定义另一个关联,将其链接到相同的类,并为您应用过滤。 - Ryan Bigg
2
如果你真的讨厌字符串,喜欢符号和哈希(不是火箭),你可以使用Group.includes(:user_group_cmb).where(user_group_cmbs: { user_id: 1}) - marksiemers
1
或者:Group.eager_load(:user_group_cmb).where('user_group_cmbs.user_id = ?', 1)eager_load 总是执行左连接(而 includes 默认情况下会执行两个单独的查询)。但是要小心使用由 includeseager_load 生成的左连接,因为它可能会破坏自定义的 select 子句。 - Marek Lipka
@marksiemers 我喜欢ActiveRecord的这个特性。 (问题也被编辑了) - siegy22

20

10

无论您是否在where子句中引用表,都可以在Rails 3.x中完成此操作:

Group.eager_load(:user_group_cmb)

...并且它将执行左外连接


8
被接受的答案存在问题,因为它实际上会执行LEFT INNER JOIN,因为它不会显示user_group_cmbs. user_id为空的条目(这是使用LEFT OUTER JOIN的原因)。
一个可行的解决方案是使用#references方法。
Group.includes(:user_group_cmb).references(:user_group_cmb)

或者更加方便的方法是使用#eager_load
Group.eager_load(:user_group_cmb)

点击这里阅读更加详细的解释。


8

需要提到的是,使用includes可能会产生意想不到的副作用。

如果随后对过滤的关联进行范围限制,则所有原始过滤都会消失。

事实证明,通过在过滤的关联上进行范围限制,我们失去了从includes获得的任何副作用过滤。而且这并不是因为我们搜索的方式。

确保阅读完整的文章,或者可以尝试使用一个gem


7

使用自定义连接语句:

Group.joins("left outer join user_group_cmbs as cmb on groups.id = cmb.group_id")
  .where("cmb.user_id = ?", 1)

2

如果您只需要将用户链接到组,请使用has_and_belongs_to_many。如果您需要存储额外的成员信息,请使用has_many :through

示例:

class User < ActiveRecord::Base
  has_and_belongs_to_many :groups
end

class Group < ActiveRecord::Base
  has_and_belongs_to_many :users      
end

没错,正如你所说,我必须关注用户。我会尝试一下。 - zono

0

由于我没有50个声望,所以无法对先前的答案进行评论。 但是当我遇到以下错误时,以下是它是如何工作的:

includes-referenced关系会引发StatementInvalid异常,抱怨缺少FROM-clause条目。我需要添加一个明确的join调用,默认为inner。

我必须使用.references,并提及表名或将其添加到FROM-clause中。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接