我尝试使用 DBIx::Class 的 Git 存储库中的 DBICTest 制作一个简单的示例。请参阅
https://github.com/Perl5/DBIx-Class/tree/maint/0.0828xx/t/lib/DBICTest
BEGIN { do "./t/lib/ANFANG.pm" or die( $@ || $! ) }
use strict;
use warnings;
use lib 'lib';
use DBICTest;
$ENV{DBIC_TRACE} = 1;
$ENV{DBIC_TRACE_PROFILE} = 'console';
my $schema = DBICTest->init_schema();
my $rs = $schema->resultset('CD');
my $subquery_rs = $rs->search(
undef,
{
'+select' => [
{
'' => $rs->search_related('tracks') # in reality I use correlate()
->count_rs->as_query,
-as => 'correlated_subquery_col'
}
],
'+as' => ['correlated_subquery_key'],
alias => 'subquery', # from approach number 4 and 6
}
)->as_subselect_rs;
$subquery_rs->search(
{
correlated_subquery_col => { '<' => 3 },
},
{
alias => 'subquery', # from approach number 5 and 6
prefetch => 'artist',
'+columns' => { correlated_subquery_key => 'correlated_subquery_col' },
}
)->all;
像这样运行代码:
$ git clone https://github.com/Perl5/DBIx-Class.git
$ cd DBIx-Class/
$ git checkout master
$ perl test.pl # file shown above
(在我的实际项目中,我使用 DBIx::Class::Helper::Correlate 来汇总相关值,但我无法在示例中使用它。)对于示例模式,所需的 SQL 可以归结为:
-- pseudo code
SELECT * FROM ( -- all columns from main and related table as well as function value
SELECT cd.*, -- main table
artist.*, -- prefetched related table
(SELECT COUNT(*) FROM track WHERE track.cd = cd.cdid) AS track_count -- subquery with COUNT/SUM
FROM cds, -- main table
JOIN artist ON artist.artistid = cd.artist -- related table
)
WHERE track_count < ?
我尝试通过以下步骤更接近解决方案:
这个问题帮助了我。
所以我需要使用另一个子查询。我发现并使用了ResultSet->as_subselect_rs()
第一次创造这个。
search()
调用中的子查询中主动选择函数列。通常我使用
'+columns' => { result_hashref_key => 'sql_column_name' }
语法来选择 额外的列,但我发现下面的语法是必要的,以便定义 列名称
correlated_subquery_col
。名字必须是 也在 WHERE 子句中引用。我不知道这方面的文档 带有空哈希键的奇怪语法
select => [ { '' => ..., -as => ... } ]
但受到这个问题的启发尝试这个。
alias
属性,这实际上在 SQLite 的示例中有效,但在具有我真实模式的 PostgreSQL 中,我收到了错误消息:
DBIx::Class::Storage::DBI::_dbh_execute(): DBI Exception: DBD::Pg::st execute failed:
ERROR: column reference "id" is ambiguous
LINE 1: SELECT "items"."id", "items"."purchase_list_id", "items"."va...
^ [for Statement "SELECT "items"."id", "items"."purchase_list_id", "items"."value", "items"."offset", "items"."unit_id", "items"."article_id", "items"."purchased", "items"."comment", "items"."servings_sum", "items"."preorder_servings" FROM (SELECT "items"."id", "items"."purchase_list_id", "items"."value", "items"."offset", "items"."unit_id", "items"."article_id", "items"."purchased", "items"."comment", ( (SELECT SUM( "dish"."servings" ) FROM "dish_ingredients" "ingredients_alias" JOIN "dishes" "dish" ON "dish"."id" = "ingredients_alias"."dish_id" WHERE ( "ingredients_alias"."item_id" = "items"."id" )) ) AS "servings_sum", "article"."id", "article"."project_id", "article"."shop_section_id", "article"."shelf_life_days", "article"."preorder_servings", "article"."preorder_workdays", "article"."name", "article"."comment" FROM "purchase_lists" "me" JOIN "items" "items" ON "items"."purchase_list_id" = "me"."id" JOIN "articles" "article" ON "article"."id" = "items"."article_id" WHERE ( ( "article"."preorder_servings" IS NOT NULL AND "article"."preorder_workdays" IS NOT NULL AND "me"."project_id" = ? ) )) "items" WHERE ( "servings_sum" >= "preorder_servings" )" with ParamValues: 1='150']
alias
属性
search()
我尝试为子查询提供不同的表别名。这会在 SQLite 中生成以下 SQL:
SELECT subquery.cdid, subquery.artist, subquery.title, subquery.year, subquery.genreid, subquery.single_track, subquery.correlated_subquery_col, artist.artistid, artist.name, artist.rank, artist.charfield FROM (SELECT subquery.cdid, subquery.artist, subquery.title, subquery.year, subquery.genreid, subquery.single_track, ( (SELECT COUNT( * ) FROM cd me JOIN track tracks ON tracks.cd = me.cdid) ) AS correlated_subquery_col FROM cd subquery) subquery JOIN artist artist ON artist.artistid = subquery.artist WHERE ( correlated_subquery_col < ? )
不幸的是,不仅外部查询的子查询别名为 subquery
,而且在子查询内部,DBIC 尝试从
subquery.
进行 SELECT 并使用
subquery.
列进行 JOIN。在 PostgreSQL 中,这会产生以下错误:
DBIx::Class::Storage::DBI::_dbh_execute(): DBI Exception: DBD::Pg::st execute failed:
ERROR: missing FROM-clause entry for table "subquery"
LINE 1: ...d" JOIN "articles" "article" ON "article"."id" = "subquery"...
^ [for Statement "SELECT "subquery"."id", "subquery"."purchase_list_id", "subquery"."value", "subquery"."offset", "subquery"."unit_id", "subquery"."article_id", "subquery"."purchased", "subquery"."comment", "subquery"."servings_sum", "subquery"."preorder_servings" FROM (SELECT "subquery"."id", "subquery"."purchase_list_id", "subquery"."value", "subquery"."offset", "subquery"."unit_id", "subquery"."article_id", "subquery"."purchased", "subquery"."comment", ( (SELECT SUM( "dish"."servings" ) FROM "dish_ingredients" "ingredients_alias" JOIN "dishes" "dish" ON "dish"."id" = "ingredients_alias"."dish_id" WHERE ( "ingredients_alias"."item_id" = "items"."id" )) ) AS "servings_sum", "article"."id", "article"."project_id", "article"."shop_section_id", "article"."shelf_life_days", "article"."preorder_servings", "article"."preorder_workdays", "article"."name", "article"."comment" FROM "purchase_lists" "me" JOIN "items" "items" ON "items"."purchase_list_id" = "me"."id" JOIN "articles" "article" ON "article"."id" = "subquery"."article_id" WHERE ( ( "article"."preorder_servings" IS NOT NULL AND "article"."preorder_workdays" IS NOT NULL AND "me"."project_id" = ? ) )) "subquery" WHERE ( "servings_sum" >= "preorder_servings" )" with ParamValues: 1='150']
alias
中使用
search()
属性。那么别名仅在外部查询中使用,但子查询根本没有别名,并且找不到列。
DBIx::Class::Storage::DBI::_dbh_execute(): DBI Exception: DBD::Pg::st execute failed:
ERROR: missing FROM-clause entry for table "subquery"
LINE 1: SELECT "subquery"."id", "subquery"."purchase_list_id", "subq...
^ [for Statement "SELECT "subquery"."id", "subquery"."purchase_list_id", "subquery"."value", "subquery"."offset", "subquery"."unit_id", "subquery"."article_id", "subquery"."purchased", "subquery"."comment", "subquery"."servings_sum", "subquery"."preorder_servings" FROM (SELECT "items"."id", "items"."purchase_list_id", "items"."value", "items"."offset", "items"."unit_id", "items"."article_id", "items"."purchased", "items"."comment", ( (SELECT SUM( "dish"."servings" ) FROM "dish_ingredients" "ingredients_alias" JOIN "dishes" "dish" ON "dish"."id" = "ingredients_alias"."dish_id" WHERE ( "ingredients_alias"."item_id" = "items"."id" )) ) AS "servings_sum", "article"."id", "article"."project_id", "article"."shop_section_id", "article"."shelf_life_days", "article"."preorder_servings", "article"."preorder_workdays", "article"."name", "article"."comment" FROM "purchase_lists" "me" JOIN "items" "items" ON "items"."purchase_list_id" = "me"."id" JOIN "articles" "article" ON "article"."id" = "items"."article_id" WHERE ( ( "article"."preorder_servings" IS NOT NULL AND "article"."preorder_workdays" IS NOT NULL AND "me"."project_id" = ? ) )) "items" WHERE ( "servings_sum" >= "preorder_servings" )" with ParamValues: 1='150']
alias => 'subquery'
调用中设置
search()
。尽管现在所有 SELECT 列名称中都使用了别名,但这会给出相同的错误消息。但奇怪的是,DBIC 仍然使用默认的表别名
me
并在 PostgreSQL 中写入
FROM "purchase_lists" "me"
:
DBIx::Class::Storage::DBI::_dbh_execute(): DBI Exception: DBD::Pg::st execute failed:
ERROR: missing FROM-clause entry for table "subquery"
LINE 1: ...d" JOIN "articles" "article" ON "article"."id" = "subquery"...
^ [for Statement "SELECT "subquery"."id", "subquery"."purchase_list_id", "subquery"."value", "subquery"."offset", "subquery"."unit_id", "subquery"."article_id", "subquery"."purchased", "subquery"."comment", "subquery"."servings_sum", "subquery"."preorder_servings" FROM (SELECT "subquery"."id", "subquery"."purchase_list_id", "subquery"."value", "subquery"."offset", "subquery"."unit_id", "subquery"."article_id", "subquery"."purchased", "subquery"."comment", ( (SELECT SUM( "dish"."servings" ) FROM "dish_ingredients" "ingredients_alias" JOIN "dishes" "dish" ON "dish"."id" = "ingredients_alias"."dish_id" WHERE ( "ingredients_alias"."item_id" = "items"."id" )) ) AS "servings_sum", "article"."id", "article"."project_id", "article"."shop_section_id", "article"."shelf_life_days", "article"."preorder_servings", "article"."preorder_workdays", "article"."name", "article"."comment" FROM "purchase_lists" "me" JOIN "items" "items" ON "items"."purchase_list_id" = "me"."id" JOIN "articles" "article" ON "article"."id" = "subquery"."article_id" WHERE ( ( "article"."preorder_servings" IS NOT NULL AND "article"."preorder_workdays" IS NOT NULL AND "me"."project_id" = ? ) )) "subquery" WHERE ( "servings_sum" >= "preorder_servings" )" with ParamValues: 1='150']
...但是 FROM cd subquery
在 SQLite 示例中:
SELECT subquery.cdid, subquery.artist, subquery.title, subquery.year, subquery.genreid, subquery.single_track, subquery.correlated_subquery_col, artist.artistid, artist.name, artist.rank, artist.charfield FROM (
SELECT subquery.cdid, subquery.artist, subquery.title, subquery.year, subquery.genreid, subquery.single_track, ( (
SELECT COUNT( * ) FROM cd me JOIN track tracks ON tracks.cd = me.cdid
) ) AS correlated_subquery_col
FROM cd subquery -- uses alias here in SQLite but not in PostgreSQL
) subquery
JOIN artist artist ON artist.artistid = subquery.artist
WHERE ( correlated_subquery_col < ? )
问题:
barubary 帮助我找到了解决方案。
问题出在as
和
alias
键上。这更加简单并且有效:
my $rs = $schema->resultset('CD');
my $subquery_rs = $rs->search(
undef,
{
'+select' => [
{
'' => $rs->search_related('tracks') # in reality I use correlate()
->count_rs->as_query,
-as => 'correlated_subquery_col'
},
],
}
)->as_subselect_rs;
$subquery_rs->search(
{
correlated_subquery_col => { '<' => 3 },
},
{
prefetch => 'artist',
'+columns' => { correlated_subquery_key => 'correlated_subquery_col' },
}
)->all;
它会生成以下 SQL:
SELECT me.cdid, me.artist, me.title, me.year, me.genreid, me.single_track, me.correlated_subquery_col, artist.artistid, artist.name, artist.rank, artist.charfield
FROM (
SELECT me.cdid, me.artist, me.title, me.year, me.genreid, me.single_track, (
SELECT COUNT( * )
FROM cd me
JOIN track tracks
ON tracks.cd = me.cdid
) AS correlated_subquery_col
FROM cd me
) me
JOIN artist artist
ON artist.artistid = me.artist
WHERE correlated_subquery_col < '3'