Symfony查询兼容sql_mode ONLY_FULL_GROUP_BY遇ANY_VALUE识别错误
解决思路
方法一:注册Doctrine自定义DQL函数(推荐)
Doctrine ORM默认不识别MySQL的ANY_VALUE()函数,需手动注册让Doctrine能解析它:
- 创建自定义函数类
在项目中新建类文件src/Doctrine/Function/AnyValue.php,代码如下:
<?php namespace App\Doctrine\Function; use Doctrine\ORM\Query\AST\Functions\FunctionNode; use Doctrine\ORM\Query\Lexer; use Doctrine\ORM\Query\Parser; use Doctrine\ORM\Query\SqlWalker; class AnyValue extends FunctionNode { public $expression = null; public function parse(Parser $parser) { $parser->match(Lexer::T_IDENTIFIER); $parser->match(Lexer::T_OPEN_PARENTHESIS); $this->expression = $parser->SingleValuedPathExpression(); $parser->match(Lexer::T_CLOSE_PARENTHESIS); } public function getSql(SqlWalker $sqlWalker) { return 'ANY_VALUE(' . $this->expression->dispatch($sqlWalker) . ')'; } }
- 配置Doctrine识别该函数
在Symfony的Doctrine配置文件(如config/packages/doctrine.yaml)中添加DQL函数配置:
doctrine: orm: dql: string_functions: ANY_VALUE: App\Doctrine\Function\AnyValue
- 修改QueryBuilder查询
将select中不属于聚合、也不在GROUP BY的字段用ANY_VALUE()包裹,示例:
return $this->createQueryBuilder('m') ->select('m.id, m.tutor, m.allow_tester, m.title, ANY_VALUE(g.title) as groupTitle, ANY_VALUE(gc.color) as color, COUNT(ms.id) as length') ->leftJoin('m.group_modules', 'gm') ->leftJoin('gm.module_group', 'g') ->leftJoin('g.programs', 'p') ->leftJoin('g.color', 'gc') ->leftJoin('m.steps', 'ms') ->where('p = :p') ->setParameter('p', $program) ->groupBy('m.id') ->orderBy('gm.sorting', 'ASC') ->orderBy('g.id', 'ASC') ->getQuery() ->getResult();
方法二:扩展GROUP BY子句(适合字段值唯一场景)
如果每个m.id对应的g.title、gc.color等字段值是唯一的(即一个模块仅属于一个分组),可以把这些字段全部加入GROUP BY,符合ONLY_FULL_GROUP_BY要求:
return $this->createQueryBuilder('m') ->select('m.id, m.tutor, m.allow_tester, m.title, g.title as groupTitle, gc.color, COUNT(ms.id) as length') ->leftJoin('m.group_modules', 'gm') ->leftJoin('gm.module_group', 'g') ->leftJoin('g.programs', 'p') ->leftJoin('g.color', 'gc') ->leftJoin('m.steps', 'ms') ->where('p = :p') ->setParameter('p', $program) ->groupBy('m.id, m.tutor, m.allow_tester, m.title, g.title, gc.color') ->orderBy('gm.sorting', 'ASC') ->orderBy('g.id', 'ASC') ->getQuery() ->getResult();
注意:若存在一个模块对应多个分组的情况,该方法会拆分分组结果,不符合原业务逻辑,不建议使用。
方法三:使用原生SQL查询
绕过Doctrine DQL,直接编写原生MySQL语句,可直接使用ANY_VALUE()函数:
$em = $this->getEntityManager(); $sql = " SELECT m.id, m.tutor, m.allow_tester, m.title, ANY_VALUE(g.title) as groupTitle, ANY_VALUE(gc.color) as color, COUNT(ms.id) as length FROM module m LEFT JOIN group_modules gm ON m.id = gm.module_id LEFT JOIN module_group g ON gm.module_group_id = g.id LEFT JOIN program_module_group pmg ON g.id = pmg.module_group_id LEFT JOIN program p ON pmg.program_id = p.id LEFT JOIN color gc ON g.color_id = gc.id LEFT JOIN module_step ms ON m.id = ms.module_id WHERE p.id = :p GROUP BY m.id ORDER BY gm.sorting ASC, g.id ASC "; $query = $em->createNativeQuery($sql, $this->createResultSetMappingBuilder('m')); $query->setParameter('p', $program); return $query->getResult();
注意:需根据你的实体类名和数据库表名,调整SQL中的表名与关联字段,ResultSetMappingBuilder会自动将结果映射到实体对象。
内容的提问来源于stack exchange,提问作者Melody
相关产品推荐
相关产品推荐

