Резюме
У меня есть таблица предметов, которые попадают парами. Я хотел бы присоединиться к нему, чтобы я мог получить обе стороны пары в одном запросе. Это действительный 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)
Прямой SQL за строкойref также не делает этого:
__PACKAGE__->has_one(dual => 'Schema::Half', {
'foreign.whole_id' => 'self.whole_id',
'foreign.half_id' => \'<> self.half_id',
});
# Invalid rel cond val SCALAR(0x96c10b8)
Обходные пути и почему они недостаточны для меня
Я мог бы получить правильный SQL, который будет сгенерирован с помощью сложного вызова search()
, и не будет определено отношение. Это довольно уродливое, с (слишком) сильно жестко запрограммированным SQL. Он должен подражать нефакторизуемому способу для каждого конкретного случая, когда пересечение происходит.
Я мог бы решить проблему, добавив столбец other_half_id
и присоединившись к =
. Это явно избыточные данные.
Я даже попытался уклониться от указанной избыточности, добавив ее через выделенное представление (CREATE VIEW AS SELECT *, opposite_of(side) AS dual FROM half...
). Вместо схемы базы данных это код, получивший избыточность и уродство, более, чем обходное решение search()
. В конце концов, я не был достаточно храбр, чтобы заставить его работать.
Желаемый SQL
Вот такой SQL, который я ищу. Обратите внимание, что это всего лишь пример: я действительно хочу, чтобы это было сделано через отношения, поэтому я могу использовать его в качестве Half
Accessult Access также в дополнение к предложению 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()
, потому что у меня есть несколько применений для ассоциации, и я не нашел выражения поиска "один размер подходит всем".
Позднее обновление
Отвечая на мой вопрос через два года, это была недостающая функциональность, которая с тех пор была реализована.