我有一张成对的物品表。我想自己加入它,这样我就可以在一个查询中检索该对的两侧。它是有效的SQL(我认为),SQLite引擎确实接受了它,但是我无法让DBIx :: Class咬紧牙关。
package Schema::Half;
use parent 'DBIx::Class';
__PACKAGE__->load_components('Core');
__PACKAGE__->table('half');
__PACKAGE__->add_columns(
whole_id => { data_type => 'INTEGER' },
half_id => { data_type => 'CHAR' },
data => { data_type => 'TEXT' },
);
__PACKAGE__->has_one(dual => 'Schema::Half', {
'foreign.whole_id' => 'self.whole_id',
'foreign.half_id' => 'self.half_id',
# previous line results in a '='
# I'd like a '<>'
});
package Schema;
use parent 'DBIx::Class::Schema';
__PACKAGE__->register_class( 'Half', 'Schema::Half' );
package main;
unlink 'join.db';
my $s = Schema->connect('dbi:SQLite:join.db');
$s->deploy;
my $h = $s->resultset('Half');
$h->populate([
[qw/whole_id half_id data /],
[qw/1 L Bonnie/],
[qw/1 R Clyde /],
[qw/2 L Tom /],
[qw/2 R Jerry /],
[qw/3 L Batman/],
[qw/3 R Robin /],
]);
$h->search({ 'me.whole_id' => 42 }, { join => 'dual' })->first;
最后一行生成以下SQL:
SELECT me.whole_id, me.half_id, me.data
FROM half me
JOIN half dual ON ( dual.half_id = me.half_id AND dual.whole_id = me.whole_id )
WHERE ( me.whole_id = ? )
我正在尝试使用DBIx :: Class连接语法在<>
和dual.half_id
之间获取me.half_id
运算符,但到目前为止尚未成功。
文档提示SQL :: Abstract-like语法。
我尝试编写has_one
关系:
__PACKAGE__->has_one(dual => 'Schema::Half', {
'foreign.whole_id' => 'self.whole_id',
'foreign.half_id' => { '<>' => 'self.half_id' },
});
# Invalid rel cond val HASH(0x959cc28)
stringref后面的直接SQL也没有:
__PACKAGE__->has_one(dual => 'Schema::Half', {
'foreign.whole_id' => 'self.whole_id',
'foreign.half_id' => \'<> self.half_id',
});
# Invalid rel cond val SCALAR(0x96c10b8)
我可以使用复杂的search()
调用生成正确的SQL,并且没有定义的关系。它非常难看,有太多硬编码的SQL。对于遍历关系的每个特定情况,它必须以不可分解的方式进行模仿。
我可以通过添加other_half_id
列并加入=
来解决此问题。这显然是多余的数据。
我甚至试图通过专用视图(CREATE VIEW AS SELECT *, opposite_of(side) AS dual FROM half...
)来避免冗余,而不是数据库模式,它是冗余和丑陋的代码,而不是基于search()
的解决方法。最后,我没有勇气让它发挥作用。
这是我正在寻找的那种SQL。请注意它只是一个例子:我真的希望它通过一个关系来完成,所以除了Half
的{{1}}子句之外,我还可以将它用作search()
ResultSet访问器。
join
我在整个扩展案例中也加入了自我,但我很确定这不是问题。我这样做是为了减少这种情况,因为它也有助于保持代码大小。
我坚持加入/关系路径而不是复杂的sqlite> SELECT *
FROM half l
JOIN half r ON l.whole_id=r.whole_id AND l.half_id<>r.half_id
WHERE l.half_id='L';
1|L|Bonnie|1|R|Clyde
2|L|Tom|2|R|Jerry
3|L|Batman|3|R|Robin
,因为我对关联有多种用途,而且我找不到任何“一刀切”的搜索表达式。< / p>
延迟更新
Answering my own question两年后,它曾经是一个缺失的功能,从那时起已经实施。
答案 0 :(得分:11)
对于那些仍然感兴趣的人,它最终实现了0.08192或更早版本。 (我现在在0.08192)
一个正确的语法是:
__PACKAGE__->has_one(dual => 'Schema::Half', sub {
my $args = shift;
my ($foreign,$self) = @$args{qw(foreign_alias self_alias)};
return {
"$foreign.whole_id" => { -ident => "$self.whole_id" },
"$foreign.half_id" => { '<>' => { -ident => "$self.half_id" } },
}
});
引用:DBIx::Class Extended Relationships on fREW Schmidt's blog我在那里首先阅读了它。
答案 1 :(得分:3)
我认为你可以通过创建一种扩展DBIx::Class::Relationship::Base
的新型关系来实现这一目标,但它似乎并没有得到很好的记录。您是否考虑过只在Half
的{{1}}的结果集集上添加一个方便方法来执行->search({}, { join => ... }
并从中返回结果集的可能性?它不像一段关系那样内省,但除此之外它的作用也很好。它使用DBIC将查询链接到您的优势的能力。
答案 2 :(得分:1)
JB,请注意,而不是:
SELECT *
FROM half l
JOIN half r ON l.whole_id=r.whole_id AND l.half_id<>r.half_id
WHERE l.half_id='L';
您可以使用以下方法编写相同的查询:
SELECT *
FROM half l
JOIN half r ON l.whole_id=r.whole_id
WHERE l.half_id<>r.half_id AND l.half_id='L';
返回相同的数据,使用DBIx :: Class更容易表达。
当然,这并没有回答“我如何使用除=
之外的其他运算符来创建DBIx :: Class连接表?”的问题,但是您展示的示例并不能证明这种需要。
答案 3 :(得分:1)
你试过了吗?
__PACKAGE__->has_one(dual => 'Schema::Half', {
'foreign.whole_id' => 'self.whole_id',
'foreign.half_id' => {'<>' => 'self.half_id'},
});
我认为关系定义中的匹配条件与搜索相同。
答案 4 :(得分:0)
'foreign.half_id' => \'<> self.half_id'
答案 5 :(得分:0)
以下是如何操作:
...
field => 1, # =
otherfield => { '>' => 2 }, # >
...