【问题标题】:Doctrine DQL - nested subqueries in WHERE clause throw Syntax ErrorDoctrine DQL - WHERE 子句中的嵌套子查询引发语法错误
【发布时间】:2019-09-09 14:00:06
【问题描述】:

我正在尝试根据两个关联表的 SUMed 和 COALESCEd 结果从一个表中查询数据。 该查询在作为本机 SQL 执行时工作正常,不幸的是,我的应用程序仅限于在此处的 QueryBuilder 对象上使用 Doctrine 的 DQL。

这就是我构建 QueryBuilder 的方式:

$resUnitQuery = $queryBuilder->getEntityManager()->createQueryBuilder();
$resUnitQuery->select('COALESCE(SUM(pc.residentialUnits), 0)')
    ->from(PropertyConnection::class, 'pc')
    ->where("$rootAlias.id = pc.contract");

$comUnitQuery = $queryBuilder->getEntityManager()->createQueryBuilder();
$comUnitQuery->select('COALESCE(SUM(pwa.residential_units), 0)')
    ->from(PropertyWithoutAddress::class, 'pwa')
    ->where("$rootAlias.id = pwa.contract");

$queryBuilder = $this->getEntityManager(Contract::class)->createQueryBuilder();
$queryBuilder
    ->select('id')
    ->from(Contract::class, 'o')
    ->andWhere(
        ':residentialUnits <= ' .
        sprintf(
            '((%s) + (%s))',
            $resUnitQuery->getQuery()->getDQL(),
            $comUnitQuery->getQuery()->getDQL()
        )
    )
    ->setParameter('residentialUnits', $params['rangeFrom']);

生成的DQL代码如下:

SELECT id 
FROM App\Entity\Contract o 
WHERE :residentialUnits <= ((SELECT COALESCE(SUM(pc.residentialUnits), 0) FROM App\Entity\PropertyConnection pc WHERE o.id = pc.contract) + (SELECT COALESCE(SUM(pwa.residential_units), 0) FROM App\Entity\PropertyWithoutAddress pwa WHERE o.id = pwa.contract))

这会导致以下错误:

[Syntax Error] line 0, col 66: Error: Expected Literal, got 'SELECT'

我发现了一个旧的 Doctrine GitHub 问题,指出双括号会导致问题,这就是为什么我在 WHERE 子句中跳过它们并替换了

((%s) + (%s))

(%s) + (%s)

原生 SQL 需要使用括号括起来,这也会导致 Doctrine 出错:

[Syntax Error] line 0, col 174: Error: Expected end of string, got '+'

我可能会遇到 Doctrine 的 DQL 的限制吗? 根据文档,支持 SUM 和 COALESCE 以及嵌套查询。

非常感谢,

雷内

【问题讨论】:

    标签: php sql-server symfony doctrine


    【解决方案1】:

    正确,DBAL 不支持对子查询的算术运算。另外 DBAL 不支持连接子查询,JOIN (SELECT FROM...) ON 不使用本机查询。

    另一个问题是您的子查询的WHERE 语句依赖于根查询,这将导致您的根查询执行全表扫描。每行执行每个子查询,除非将条件添加到根查询 (WHERE o.id ...)。

    由于子查询 SUM 的值取决于根查询 id。您可以重写查询以使用子查询作为隐藏列和 HAVING 子句,以从添加的列结果中过滤 id 结果集。

    $em = $queryBuilder->getEntityManager();
    $expr = $em->getExpressionBuilder();
    
    $qbPC = $em->createQueryBuilder()
        ->select('COALESCE(SUM(pc.residentialUnits), 0)')
        ->from(App\Entity\PropertyConnection::class, 'pc')
        ->where($expr->eq('pc.contract', "$rootAlias.id"));
    
    $qbPWA = $em->createQueryBuilder()
        ->select('COALESCE(SUM(pwa.residential_units), 0)')
        ->from(App\Entity\PropertyWithoutAddress::class, 'pwa')
        ->where($expr->eq('pwa.contract', "$rootAlias.id"));
    
    $qb = $this->getEntityManager(Contract::class)
        ->createQueryBuilder()
        ->select('o.id')
        ->from(App\Entity\Contract::class, 'o')
        ->addSelect('(' . $qbPC->getDQL() . ') AS HIDDEN pc_ru')
        ->addSelect('(' . $qbPWA->getDQL() . ') AS HIDDEN pwa_ru')
        ->having($expr->lte(':v', 'pc_ru + pwa_ru'))
        ->setParameter('v', $params['rangeFrom']);
    
    dump($qb->getDQL());
    

    生成的 DQL

    SELECT 
        o.id, 
        (SELECT 
             COALESCE(SUM(pc.residentialUnits), 0) 
         FROM App\Entity\PropertyConnection pc 
         WHERE pc.contract = o.id
        ) AS HIDDEN pc_ru, 
        (SELECT 
             COALESCE(SUM(pwa.residential_units), 0) 
         FROM App\Entity\PropertyWithoutAddress pwa 
         WHERE pwa.contract = o.id
        ) AS HIDDEN pwa_ru 
    FROM App\Entity\Contract o 
    HAVING :v <= pc_ru + pwa_ru
    

    【讨论】:

    • 感谢您的快速回复。 Doctrine 现在似乎能够解析它,不幸的是我的数据库不知道我们在子查询中定义的两个隐藏别名:SQL-Error [207] [S0001]: Invalid column name 'sclr_46'.sclr_46 是作为第一个子查询的别名生成的变量。如果有帮助,我正在使用 SQLServer。
    • @renecatharsis 不幸的是,MSSQL 不支持 HAVING 子句中的聚合列别名,就像其他 RDBMS 所做的那样,并且 ORM 还没有构建用于解析复杂的查询。要解决此问题,您需要在发出合同查询之前使用本机查询或过滤 ID。有关此问题的详细信息,请参阅:github.com/doctrine/orm/issues/7593
    • 可惜了。那时我将不得不解决它。非常感谢您的支持。由于您的回复对其他 DBMS 有效,我将其标记为已解决!
    猜你喜欢
    • 2020-03-21
    • 1970-01-01
    • 1970-01-01
    • 2023-02-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-08-05
    • 2022-12-01
    相关资源
    最近更新 更多