You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何使用Symfony QueryBuilder转换含子查询的SQL语句?

如何用Symfony QueryBuilder实现带子查询的统计查询

问题背景

需要将一段原生SQL转换为Symfony QueryBuilder写法,处理子查询时遇到困难。原生SQL的逻辑是:先获取每个Router_name对应的最大版本号,再统计符合指定cuid和etat条件的最新版本记录数。

原始原生SQL的PHP实现

function stat_etat($cuid, $db, $etat)
{
    $requete = $db->prepare('SELECT count(*)
    FROM fiches_de_production table1,
      (SELECT MAX(Version) AS maxversion, Router_name
      FROM fiches_de_production
      GROUP BY Router_name) table2
    WHERE (table1.LDM_LDM_LDM = ? AND table1.Etat = ?
    AND table1.Router_name = table2.Router_name
    AND table1.version= table2.maxversion)'); 
    $requete->execute(array($cuid,$etat));
    $res = $requete->fetch();
    $requete->closeCursor();
    return $res['count(*)']; 
}

尝试的QueryBuilder代码(存在问题)

public function stat_etat(String $cuid, $etat)
{
    //$scalar = $query->getScalarResult();
    $entityManager = $this->getEntityManager();
    $query = $entityManager -> createQueryBuilder()
        ->select('count(f)')
        ->from('App\Entity\FichesDeProduction', 'f')
            ->addSelect('(SELECT MAX(f.version) AS maxversion
                    FROM App\Entity\FichesDeProduction f
                    GROUP BY f.Router_name) AS table2')
        ->where('f.LDM_LDM_LDM = :cuid')
        ->andWhere ('f.etat = :etat')
        ->andWhere ('f.Router_name = table2.Router_name')
        ->andWhere ('f.version = table2.maxversion')
        ->setParameter('cuid', $cuid)
        ->setParameter('etat', $etat)
        ->getQuery()
        ->getResult();
}

正确实现方式

方式一:将子查询作为关联表JOIN

这种方式和原生SQL逻辑完全对应,先构建子查询获取每个Router_name的最大版本,再将其作为临时表与主表关联:

public function stat_etat(string $cuid, $etat)
{
    $entityManager = $this->getEntityManager();
    
    // 构建子查询:获取每个Router_name对应的最大版本
    $subQueryBuilder = $entityManager->createQueryBuilder()
        ->select('f_sub.Router_name, MAX(f_sub.version) AS maxversion')
        ->from('App\Entity\FichesDeProduction', 'f_sub')
        ->groupBy('f_sub.Router_name');
    
    // 主查询:关联子查询结果,统计符合条件的记录数
    $queryBuilder = $entityManager->createQueryBuilder()
        ->select('COUNT(f.id)') // 使用实体主键统计更高效
        ->from('App\Entity\FichesDeProduction', 'f')
        // 关联子查询生成的临时表
        ->join(
            sprintf('(%s)', $subQueryBuilder->getDQL()),
            't2',
            'WITH',
            'f.Router_name = t2.Router_name AND f.version = t2.maxversion'
        )
        ->where('f.LDM_LDM_LDM = :cuid')
        ->andWhere('f.etat = :etat')
        ->setParameter('cuid', $cuid)
        ->setParameter('etat', $etat);
    
    // 获取单一标量结果并转为整数返回
    return (int) $queryBuilder->getQuery()->getSingleScalarResult();
}

方式二:使用WHERE EXISTS子查询

这种方式通过EXISTS判断当前记录是否为对应Router_name的最新版本,逻辑更简洁:

public function stat_etat(string $cuid, $etat)
{
    $entityManager = $this->getEntityManager();
    
    // 构建EXISTS子查询:判断当前记录是否是对应Router_name的最大版本
    $existsSubQuery = $entityManager->createQueryBuilder()
        ->select('1')
        ->from('App\Entity\FichesDeProduction', 'f_sub')
        ->where('f_sub.Router_name = f.Router_name')
        ->groupBy('f_sub.Router_name')
        ->having('MAX(f_sub.version) = f.version');
    
    $queryBuilder = $entityManager->createQueryBuilder()
        ->select('COUNT(f.id)')
        ->from('App\Entity\FichesDeProduction', 'f')
        ->where('f.LDM_LDM_LDM = :cuid')
        ->andWhere('f.etat = :etat')
        ->andWhere($existsSubQuery->getDQL())
        ->setParameter('cuid', $cuid)
        ->setParameter('etat', $etat);
    
    return (int) $queryBuilder->getQuery()->getSingleScalarResult();
}

问题说明

你之前的写法错误在于用addSelect将子查询作为字段添加,这会导致子查询结果被当作一列值,而非可关联的临时表,因此无法在WHERE条件中引用table2.Router_name这类字段。正确的做法是将子查询作为JOIN的表,或者用EXISTS子查询来实现逻辑。

内容的提问来源于stack exchange,提问作者Shensil

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.19 08:55:19