Почему я получаю "JOIN" вместо "LEFT JOIN" для отношения "has_many" в DBIx::Class?

У меня есть три таблицы в следующих отношениях:

package SafeVPN::DB::Result::Locality;
__PACKAGE__->has_many( servers        => 'SafeVPN::DB::Result::Server', 'locality_id', {cascade_delete => 0});
__PACKAGE__->has_many( addresses_view => 'SafeVPN::DB::Result::Pool::Address_view', 'locality_id', {cascade_delete => 0});

package SafeVPN::DB::Result::Server;
__PACKAGE__->has_many('addresses_view', 'SafeVPN::DB::Result::Pool::Address_view', 'server_id', {cascade_delete => 0});
__PACKAGE__->belongs_to('locality', 'SafeVPN::DB::Result::Locality', 'locality_id', {cascade_delete => 0});


package SafeVPN::DB::Result::Pool::Address_view;
__PACKAGE__->belongs_to( server   => 'SafeVPN::DB::Result::Server',       'server_id'  );
__PACKAGE__->belongs_to( locality => 'SafeVPN::DB::Result::Locality',     'locality_id');
__PACKAGE__->belongs_to( subnet   => 'SafeVPN::DB::Result::Pool::Subnet', 'subnet_id'  );

в Locality.pm работает
этот: $self->search_related('servers')->search_related('addresses_view')->as_query
или это: $self->servers->search_related('addresses_view')->as_query

генерирует следующий запрос:

SELECT 
  "addresses_view"."id", "addresses_view"."subnet_id", 
  "addresses_view"."ip", "addresses_view"."usage",
  "addresses_view"."notes", "addresses_view"."locality_id", 
  "addresses_view"."server_id" 
FROM "servers" "me"  
JOIN "pool_addresses_view" "addresses_view" 
  ON "addresses_view"."server_id" = "me"."id" 
WHERE ( "me"."locality_id" = ? )

Но полагаясь на отношение has_many (в документе сказано: "Это отношение относится к нулю или нескольким записям во внешней таблице (например, к левому соединению)")

Я ожидаю "LEFT JOIN", но, как вы видите, я получаю "JOIN"

Поэтому, если у 'server' из 'locality_id' нет IP-адресов, я теряю этот 'server' из результатов

ОБНОВИТЬ
добавление атрибута join_type => 'left' как:

__PACKAGE__->has_many( addresses_view => 'SafeVPN::DB::Result::Pool::Address_view', 'locality_id', {cascade_delete => 0,join_type => 'left'});
__PACKAGE__->has_many('addresses_view', 'SafeVPN::DB::Result::Pool::Address_view', 'server_id', {cascade_delete => 0,join_type => 'left'});

не имеет никакого эффекта Может быть, ошибка?

1 ответ

Решение

Что ж, servers->search_related('addresses_view') означает " я хочу адреса этих серверов ", а не " эти серверы с адресами ", поэтому нет смысла делать LEFT JOIN в результате можно было бы сохранить серверы без адресов, но вы больше не заботитесь о серверах (вот что search_related средства).

search_related возвращает набор результатов нового типа: SafeVPN::DB::ResultSet::Pool::Address_view вместо SafeVPN::DB::ResultSet::Pool::Server,

Если вы хотите получить серверы с адресами (т.е. сохранить тип набора результатов, но добавить дополнительную информацию), то вы можете использовать prefetch не search_related,

my @servers = $self->servers->search({}, prefetch => 'addresses_view')->all();
foreach my $server (@servers) {
    print $server->addresses_view(); # Doesn't make a SQL query, data is *prefetched*.
}

Вы не получаете необработанные объединенные отношения, все адреса хранятся в server объект результата, это то, как работает ORM.

Другие вопросы по тегам