为什么在 DBIx::Class 中 'has_many' 关系得到 'JOIN' 而不是 'LEFT JOIN'?
Why I get 'JOIN' instead of 'LEFT JOIN' for 'has_many' relationship in DBIx::Class?
下一段关系我有3个table:
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'关系(doc说:"This relationship refers to zero or more records in the foreign table (e.g. a LEFT JOIN)")
我希望 'LEFT JOIN' 但是,如您所见,我得到 'JOIN'
因此,如果 'locality_id' 中的 'server' 没有 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'});
没有效果。可能是BUG?
好吧,servers->search_related('addresses_view')
的意思是“我想要这个服务器的地址”而不是“这个服务器的地址”,所以LEFT JOIN
没有意义,它会在结果中保存没有地址的服务器,但您不再关心服务器(search_related
的意思)。
search_related
returns 新类型的结果集: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 的工作方式。
下一段关系我有3个table:
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'关系(doc说:"This relationship refers to zero or more records in the foreign table (e.g. a LEFT JOIN)")
我希望 'LEFT JOIN' 但是,如您所见,我得到 'JOIN'
因此,如果 'locality_id' 中的 'server' 没有 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'});
没有效果。可能是BUG?
好吧,servers->search_related('addresses_view')
的意思是“我想要这个服务器的地址”而不是“这个服务器的地址”,所以LEFT JOIN
没有意义,它会在结果中保存没有地址的服务器,但您不再关心服务器(search_related
的意思)。
search_related
returns 新类型的结果集: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 的工作方式。