如何在 DBIx::Class 中进行带括号的联接

How to do parenthesized joins in DBIx::Class

给定下表,每个表都具有简单的外键关系:

    B - C
  //    |
A       |
  \    |
    D - E

A to B/D = has many; B to C and D to E = has one; C to E = might have?

我需要从 A、B 和 C 中抓取所有行,以及当 E 有一行链接到 D 和 C 时从 D 和 E 中抓取相应的行。我能想到的实现此目的的唯一方法是将D和E加入括号中,如下:

SELECT * 
FROM A 
    JOIN B ON (B.a_id = A.id) 
    JOIN C ON (C.b_id = B.id) 
    LEFT JOIN (D JOIN E ON (E.d_id = D.id AND E.c_id = C.id)) ON (D.a_id = A.id);

没有括号,只有 return B/C 行具有 D/E。如果我将 E 更改为 LEFT JOIN,它会抓取 D 中的第一行,无论是否有 E 中的好行,即使有有效的 D/E 行要抓取。这可能使用 DBIx::Class/SQL::Abstract 还是有更好的方法来完成这个?

我找不到合适的方法来做到这一点,但我能够陪审团安装一个补丁:

diff -rupN DBIx-Class-0.082820/lib/DBIx/Class/ResultSource.pm DBIx-Class-0.082820.new/lib/DBIx/Class/ResultSource.pm
--- DBIx-Class-0.082820/lib/DBIx/Class/ResultSource.pm  2015-03-20 12:31:00.000000000 -0400
+++ DBIx-Class-0.082820.new/lib/DBIx/Class/ResultSource.pm      2015-05-05 17:40:07.646500380 -0400
@@ -1700,6 +1700,12 @@ sub _resolve_join {
       or $self->throw_exception("No such relationship $join on " . $self->source_name);

     my $rel_src = $self->related_source($join);
+
+    my @grouped_joins;
+    if ($rel_info->{attrs}{join}){
+      @grouped_joins = $rel_src->_resolve_join($rel_info->{attrs}{join}, $as, $seen, [@$jpath, {$join => $as}]);
+    }
+
     return [ { $as => $rel_src->from,
                -rsrc => $rel_src,
                -join_type => $parent_force_left
@@ -1714,6 +1720,7 @@ sub _resolve_join {
                 ),
                -alias => $as,
                -relation_chain_depth => ( $seen->{-relation_chain_depth} || 0 ) + 1,
+               @grouped_joins ? (-join => \@grouped_joins) : (),
              },
              scalar $self->_resolve_condition($rel_info->{cond}, $as, $alias, $join)
           ];
diff -rupN DBIx-Class-0.082820/lib/DBIx/Class/SQLMaker.pm DBIx-Class-0.082820.new/lib/DBIx/Class/SQLMaker.pm
--- DBIx-Class-0.082820/lib/DBIx/Class/SQLMaker.pm      2015-03-20 07:54:07.000000000 -0400
+++ DBIx-Class-0.082820.new/lib/DBIx/Class/SQLMaker.pm  2015-05-05 17:38:39.899603508 -0400
@@ -402,6 +402,9 @@ sub _gen_from_blocks {
     if (ref $to eq 'ARRAY') {
       push(@j, '(', $self->_recurse_from(@$to), ')');
     }
+    elsif (ref $to eq 'HASH' and $to->{-join}){
+      push(@j, '(', $self->_recurse_from($to, @{$to->{-join}}), ')');
+    }
     else {
       push(@j, $self->_from_chunk_to_sql($to));
     }

这使您可以使用简单的关系属性对联接进行分组。

package Test::Schema::Result::A;
...
__PACKAGE__->has_many(
  "D" => "Test::Schema::Result::D",
  { "foreign.a_id" => "self.id" },
  { join => 'E' },
);

package Test::Schema::Result::D;
...
__PACKAGE__->has_many(
  "E", "Test::Schema::Result::E",
  { "foreign.d_id" => "self.id" },
);

它似乎适用于更复杂的连接(散列和数组引用),但尚未经过全面测试。我只能说它适用于我的情况,并且不会破坏当前的测试套件。