概要
ペアになっているアイテムの表があります。単一のクエリでペアの両側を取得できるように、自己結合したいと思います。これは有効な 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 に似た構文を示唆しています。
私はそのような関係を書いてみました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です。これは一例に過ぎないことに注意してください。関係を介して実行したいので、 's句Half
に加えて ResultSet アクセサーとしても使用できます。search()
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
補足事項
完全に拡張されたケースでも実際に自分自身に参加していますが、それが問題ではないと確信しています。コードサイズを小さく保つのにも役立つため、ここではケースを縮小するためにこの方法を採用しました。
search()
関連付けには複数の用途があり、「1 つのサイズですべてに適合する」検索式が見つからなかったため、複合パスではなく結合/関係パスに固執しています。
更新が遅い