14

概括

我有一张成对的物品表。我想自己加入它,这样我就可以在一个查询中检索这对的两边。这是有效的 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 连接语法在and<>之间获取运算符,但到目前为止还没有成功。dual.half_idme.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

这是我正在寻找的那种 SQL。请注意,这只是一个示例:我真的希望它通过关系完成,因此Half除了search()'join子句之外,我也可以将它用作 ResultSet 访问器。

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

旁注

在我的完整扩展案例中,我真的加入了自我,但我很确定这不是问题。我把它保留在这里是为了减少大小写,因为它也有助于保持代码的大小。

我坚持加入/关系路径而不是复杂路径,search()因为我有多种用途用于关联,而且我没有找到任何“一刀切”的搜索表达式。

延迟更新

两年后回答我自己的问题,它曾经是一个缺失的功能,此后一直在实施。

4

6 回答 6

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" } },
  }
});

引用:fREW Schmidt 的博客上的 DBIx::Class Extended Relationships我第一次读到它。

于 2011-08-05T13:59:27.383 回答
3

我认为你可以通过创建一种新的关系扩展来做到这一点,DBIx::Class::Relationship::Base但它似乎没有很好的记录。您是否考虑过仅在结果集上添加一个便捷方法Half来执行 a->search({}, { join => ... }并将结果集返回给您的可能性?它不像关系那样是内省的,但除此之外它也很有效。它使用 DBIC 将查询链接到您的优势的能力。

于 2009-08-08T11:37:09.620 回答
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 连接表=?”这个问题,但是您展示的示例并不能证明这种需要是合理的。

于 2009-06-11T10:44:38.260 回答
1

你有没有尝试过:

__PACKAGE__->has_one(dual => 'Schema::Half', {
'foreign.whole_id' => 'self.whole_id',
'foreign.half_id' => {'<>' => 'self.half_id'},
});

我相信关系定义中的匹配标准与搜索相同。

于 2009-06-11T14:05:31.443 回答
0

'foreign.half_id' => \'<> self.half_id'

于 2009-06-12T19:01:44.453 回答
0

这是如何做到的:

...
field => 1,                  # =
otherfield => { '>' => 2 },  # >
...
于 2009-11-18T19:45:58.793 回答