Резюме
У меня есть таблица предметов, которые идут парами. Я хотел бы присоединиться к нему самостоятельно, чтобы получить обе стороны пары в одном запросе. Это действительный 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 :: Абстрактный синтаксис.
Я пытался написать отношение 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 за stringref также не делает это:
__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
ResultSet accessor тоже в дополнение к предложению 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()
, потому что у меня есть многократное использование для ассоциации, и я не нашел никакого поискового выражения "один размер подходит всем".
Позднее обновление
Отвечая на мой собственный вопрос два года спустя, это был отсутствующий функционал, который с тех пор был реализован.