如何使用除`=`之外的其他运算符来创建DBIx :: Class连接表?

JB.*_*JB. 14 sql perl join dbix-class

摘要

我有一张成对的物品表.我想自己加入它,这样我就可以在一个查询中检索该对的两侧.它是有效的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;
Run Code Online (Sandbox Code Playgroud)

最后一行生成以下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 = ? )
Run Code Online (Sandbox Code Playgroud)

我正在尝试使用DBIx :: Class连接语法来获取和<>之间的运算符,但到目前为止还没有成功.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)
Run Code Online (Sandbox Code Playgroud)

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)
Run Code Online (Sandbox Code Playgroud)

解决方法以及为什么它们对我不够

我可以使用复杂的search()调用生成正确的SQL ,并且没有定义的关系.它非常难看,有太多硬编码的SQL.对于遍历关系的每个特定情况,它必须以不可分解的方式进行模仿.

我可以通过添加一个other_half_id列并加入其中=来解决这个问题.这显然是冗余数据.

我甚至试图通过专用视图(CREATE VIEW AS SELECT *, opposite_of(side) AS dual FROM half...)添加它来逃避冗余,而不是数据库模式,它是冗余和丑陋的代码,而不是search()基于变通的解决方法.最后,我没有勇气让它发挥作用.

希望SQL

这是我正在寻找的那种SQL.请注意它只是一个例子:我真的希望它通过一个关系来完成,所以Half除了一个search()'s 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
Run Code Online (Sandbox Code Playgroud)

旁注

在我的完整扩展案例中,我真正加入了自我,但我很确定这不是问题所在.我这样做是为了减少这种情况,因为它也有助于保持代码大小.

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

延迟更新

两年后回答我自己的问题,它曾经是一个缺失的功能,从那时起已经实施.

JB.*_*JB. 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" } },
  }
});
Run Code Online (Sandbox Code Playgroud)

引用:ff Schmidt博客上的DBIx :: Class扩展关系,我首先阅读了它.

  • @JB你应该接受你自己的答案,它现在肯定比我的好:) (2认同)