【问题标题】:ActiveRecord multiple join?ActiveRecord 多连接?
【发布时间】:2014-02-16 01:57:15
【问题描述】:

我正在努力解决 Active Record 加入问题。

我有这个数据库表结构:

A - B - C - D - E

A - F - E

如果有以下任一路径,我想从 A 获取所有记录:

A - B - C - D - E
A - F - E

满意。

我用 Active Record 试过这个:

A.joins(:B => {:C => {:D => :E}}, :F => :E)
.where("some constraints on E and …")

但这不起作用,因为所有都是像这样作为内部连接生成的:

SELECT  A.*
FROM A
INNER JOIN B ON A.b_id = B.id
INNER JOIN C ON B.c_id = C.id
INNER JOIN D ON C.d_id = D.id
INNER JOIN E ON D.e_id = E.id
INNER JOIN F ON A.f_id = F.id
INNER JOIN E E_F ON E_F.f_id = F.id
WHERE ("some constraints on E")

我希望我没有在这里输入错误,但核心是,在生成的 SQL 语句中,两个路径都在 ON 子句中。

问题是必须满足两条路径才能从表 A 返回记录,对吧?

我需要一个从 A 为两条路径返回记录的语句。 最后,uniq() 使它们变得独一无二。

我也尝试过merge() 两个单独的选择/加入(路径A - B - C - D - E" and "A - F - E") butmerge()does an intersection. The only solution that I have come up with, is to+` 两个单独的结果集,但问题是我得到了一个数组,因此我不能使用(kaminari)分页。 其次,当结果集变大时,这不是一个好的解决方案。

我会很感激任何提示。 谢谢!

【问题讨论】:

    标签: ruby-on-rails ruby activerecord join


    【解决方案1】:

    我有一种感觉,你最终可能会在这里得到一个熟练的手工加入:

    SELECT A.*
    FROM A
    LEFT JOIN (B ON A.b_id = B.id
      INNER JOIN C ON B.c_id = C.id
      INNER JOIN D ON C.d_id = D.id
      INNER JOIN E ON D.e_id = E.id)
    LEFT JOIN (F ON A.f_id = F.id
      INNER JOIN E E_F ON E_F.f_id = F.id)
    WHERE E_F.id is not null or E.id is not null and
        („some constraints on E“)
    

    当然,最好将其封装在一个作用域中。

    您可能会在通过 F 连接时遇到麻烦,因为它似乎是 A 和 E 的父级 - 如果 F has_many E 那么您将为 A 返回多行。

    您可以通过使用相关子查询(半联接)来解决此问题:

    SELECT A.*
    FROM A
    LEFT JOIN (B ON A.b_id = B.id
      INNER JOIN C ON B.c_id = C.id
      INNER JOIN D ON C.d_id = D.id
      INNER JOIN E ON D.e_id = E.id)
    WHERE E.id is not null and
    EXISTS (SELECT NULL FROM E E_F WHERE E_F.F.id = A.F_ID)
        („some constraints on E“)
    

    请注意,可以从查询中删除 F,因为 A 和 E 中都存在 F_ID。

    为了整洁起见,您可以将两个连接都设为半连接 ...

    SELECT A.*
    FROM A
    WHERE (EXISTS (SELECT NULL FROM E E_F WHERE E_F.F.id = A.F_ID) OR
           EXISTS (SELECT NULL FROM B 
                   INNER JOIN C ON B.c_id = C.id
                   INNER JOIN D ON C.d_id = D.id
                   INNER JOIN E ON D.e_id = E.id
                   WHERE A.b_id = B.id))
        („some constraints on E“)
    

    将其放入作用域...

    scope :relates_to_e, -> {where("(EXISTS (SELECT NULL
                                                FROM E E_F
                                               WHERE E_F.F.id = A.F_ID) OR
                                      EXISTS (SELECT NULL
                                              FROM B 
                                              INNER JOIN C ON B.c_id = C.id
                                              INNER JOIN D ON C.d_id = D.id
                                              INNER JOIN E ON D.e_id = E.id
                                              WHERE A.b_id = B.id))"}
    

    它并不漂亮,但它会有效地工作并将不整洁保持在一个范围内。

    A.relates_to_e.where(„some constraints on E and …“)
    

    【讨论】:

    • 非常感谢您的启示!!!我需要的解决方案的核心是 EXISTS - OR 组合。最后,我将范围定义为带参数的 lambda。 (我不想用所有的细节来打扰你 ;-)
    猜你喜欢
    • 2020-03-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-10-02
    • 2017-01-27
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多