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 結合構文を使用して と の間の演算子を取得しようとしてい<>ますが、これまでのところうまくいきませんでした。dual.half_idme.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 つのサイズですべてに適合する」検索式が見つからなかったため、複合パスではなく結合/関係パスに固執しています。

更新が遅い

2 年後に私自身の質問に答えたところ、以前は欠落していた機能が実装されていました。

4

6 に答える 6

11

これにまだ興味がある人のために、0.08192 またはそれ以前の時点で最終的に実装されました。(私は現在0.08192です)

1 つの正しい構文は次のとおりです。

__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 の fREW Schmidt のブログで、私が最初に読んだところです。

于 2011-08-05T13:59:27.383 に答える
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 に答える
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 に答える
0

方法は次のとおりです。

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