在 WHERE 子句中使用子查询的函数值 + 使用 DBIx::Class 获取结果

问题描述 投票:0回答:1
我尝试在 DBIx::Class 中构建一个复杂的查询

    预取相关行
  • 计算子查询中的聚合函数
  • 根据函数值过滤结果
  • 使用行获取函数值
在我的应用程序中,这是行数最多的表之一。因此我想在 1 个查询中进行计算和过滤。

我尝试使用 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 < ?
我尝试通过以下步骤更接近解决方案:

  1. 起初我遇到了 PostgreSQL 不允许我的问题 参考附加列中函数的结果,但是

    这个问题帮助了我。 所以我需要使用另一个子查询。我发现并使用了ResultSet->as_subselect_rs()

    第一次创造这个。

  2. 我需要从外部

    search()

     调用中的子查询中主动选择函数列。通常我使用 
    '+columns' => { result_hashref_key => 'sql_column_name' }
     语法来选择
    额外的列,但我发现下面的语法是必要的,以便定义
    列名称 
    correlated_subquery_col
    。名字必须是
    也在 WHERE 子句中引用。我不知道这方面的文档
    带有空哈希键的奇怪语法
    select => [ { '' => ..., -as => ... } ]
    但受到
    这个问题的启发尝试这个。

  3. 如果没有

    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']

    在子查询中使用
  1. 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']

    或者,我尝试在外部查询的
  1. 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']

    最后我尝试在两个
  1. 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 < ? )

问题:

    如何用DBIC实现我想要的查询?
  • 我发现使用相同 DBIC 语法的 SQLite 和 PostgreSQL 之间存在差异。这可能是一个错误吗?我可能会尝试进一步减少示例,以便将潜在的错误报告发布到 DBIC 邮件列表。
如果您好奇,我真正的疑问是什么:我的应用程序是用于收集食谱和制定饮食计划的。我需要获取所有购买列表项,预取他们的文章,并计算每个列表项的总和,即需要购买该项目的份量。示例行:采购清单项目 100 公斤苹果与文章“苹果”相关,并且两盘菜总共有 25 份,每盘 10 份和 15 份。对于预订,仅显示超过给定份数的购买清单项目。

sql postgresql perl dbix-class
1个回答
1
投票
感谢 Mastodon 上的

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'
    
© www.soinside.com 2019 - 2024. All rights reserved.