如何在 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" },
);
它似乎适用于更复杂的连接(散列和数组引用),但尚未经过全面测试。我只能说它适用于我的情况,并且不会破坏当前的测试套件。
给定下表,每个表都具有简单的外键关系:
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" },
);
它似乎适用于更复杂的连接(散列和数组引用),但尚未经过全面测试。我只能说它适用于我的情况,并且不会破坏当前的测试套件。