在使用分页器的查询中组合聚合字段(group by)和排序(order by)时,Doctrine 生成不正确的 SQL
Doctrine generates incorrect SQL when combining aggregate fields (group by) and ordering (order by) in a query with Paginator
我在 Item
和 Valoracion
之间有一个简单的双向一对多关系(评论)。以下查询应按降序获取每个项目的平均分数和评论数:
$itemsQb = $em->getRepository(Item::class)->createQueryBuilder('i')
->select('i as data')
->addSelect('avg(v.score) as avg_score')
->addSelect('count(v.score) as num_reviews')
->leftJoin('i.valoraciones', 'v')
->groupBy('i.id')
->addOrderBy('avg_score', 'DESC')
->addOrderBy('num_reviews', 'DESC');
其中 $em
是工作中的 Doctrine\ORM\EntityManager
实例。使用Doctrine\ORM\Tools\Pagination\Paginator
对上述查询进行分页并使用getIterator()
遍历结果时抛出异常,如下:
$pag = new Paginator($itemsQb);
// first page, up to three results per page
$pag->getQuery()->setFirstResult(0)->setMaxResults(3);
// display results one by one
echo "Name\t\tAvg\tNum\n";
foreach ($pag->getIterator() as $p) {
echo $p['data']->name . "\t" . $p['avg_score'] . "\t" . $p['num_reviews'] . "\n";
}
SQLSTATE[42000]: Syntax error or access violation: 1055 Expression #5 of SELECT list is not in GROUP BY clause and contains nonaggregated column 'test.v1_.score' which is not functionally dependent on columns in GROUP BY clause; this is incompatible with sql_mode=only_full_group_by
生成了以下 SQL 查询
SELECT DISTINCT
id_5
FROM
(SELECT DISTINCT
id_5, sclr_2, sclr_3
FROM
(SELECT
i0_.id AS id_0,
i0_.name AS name_1,
AVG(v1_.score) AS sclr_2,
COUNT(v1_.score) AS sclr_3,
v1_.score AS score_4,
i0_.id AS id_5
FROM
item i0_
LEFT JOIN valoracion v1_ ON i0_.id = v1_.item_id
GROUP BY i0_.id) dctrn_result_inner
ORDER BY sclr_2 DESC , sclr_3 DESC) dctrn_result
LIMIT 3
很明显 v1_.score AS score_4,
行根本不应该出现!
那么,为什么会生成这个无效的 SQL?我做错了什么吗?
备注:
- 如果使用
getQuery()->getResult()
而不是 getIterator()
一切正常。我仍在寻求帮助,因为当 $pag
传递给模板时,Twig 显然 在 for
循环后面使用 getIterator()
。
- 如果删除
ORDER BY
子句,一切也都正常!
- 我正在使用 MySQL 5.7.25,
sql_mode=ONLY_FULL_GROUP_BY
我不想更改它。
在这种情况下,您不会获取加入的 "to-many collection",而是按 项目 ID 分组,并且只是聚合“valoraciones 的值"关系。
根据doctrine pagination documentation,在这种情况下,您可以禁用 $fetchJoinCollection 标志:
$pag = new Paginator($itemsQb, false);
这个问题似乎是由试图将 "missing fields" 从按条件排序的 "missing fields" 添加到 select 子句的学说引起的。 Related issue Pull request
在这种情况下,它认为应该将 "v.score" 添加到 select。
或者,您可以在分页器中禁用输出遍历器以避免上述行为:
$pag->setUseOutputWalkers(false);
我在 Item
和 Valoracion
之间有一个简单的双向一对多关系(评论)。以下查询应按降序获取每个项目的平均分数和评论数:
$itemsQb = $em->getRepository(Item::class)->createQueryBuilder('i')
->select('i as data')
->addSelect('avg(v.score) as avg_score')
->addSelect('count(v.score) as num_reviews')
->leftJoin('i.valoraciones', 'v')
->groupBy('i.id')
->addOrderBy('avg_score', 'DESC')
->addOrderBy('num_reviews', 'DESC');
其中 $em
是工作中的 Doctrine\ORM\EntityManager
实例。使用Doctrine\ORM\Tools\Pagination\Paginator
对上述查询进行分页并使用getIterator()
遍历结果时抛出异常,如下:
$pag = new Paginator($itemsQb);
// first page, up to three results per page
$pag->getQuery()->setFirstResult(0)->setMaxResults(3);
// display results one by one
echo "Name\t\tAvg\tNum\n";
foreach ($pag->getIterator() as $p) {
echo $p['data']->name . "\t" . $p['avg_score'] . "\t" . $p['num_reviews'] . "\n";
}
SQLSTATE[42000]: Syntax error or access violation: 1055 Expression #5 of SELECT list is not in GROUP BY clause and contains nonaggregated column 'test.v1_.score' which is not functionally dependent on columns in GROUP BY clause; this is incompatible with sql_mode=only_full_group_by
生成了以下 SQL 查询
SELECT DISTINCT
id_5
FROM
(SELECT DISTINCT
id_5, sclr_2, sclr_3
FROM
(SELECT
i0_.id AS id_0,
i0_.name AS name_1,
AVG(v1_.score) AS sclr_2,
COUNT(v1_.score) AS sclr_3,
v1_.score AS score_4,
i0_.id AS id_5
FROM
item i0_
LEFT JOIN valoracion v1_ ON i0_.id = v1_.item_id
GROUP BY i0_.id) dctrn_result_inner
ORDER BY sclr_2 DESC , sclr_3 DESC) dctrn_result
LIMIT 3
很明显 v1_.score AS score_4,
行根本不应该出现!
那么,为什么会生成这个无效的 SQL?我做错了什么吗?
备注:
- 如果使用
getQuery()->getResult()
而不是getIterator()
一切正常。我仍在寻求帮助,因为当$pag
传递给模板时,Twig 显然 在for
循环后面使用getIterator()
。 - 如果删除
ORDER BY
子句,一切也都正常! - 我正在使用 MySQL 5.7.25,
sql_mode=ONLY_FULL_GROUP_BY
我不想更改它。
在这种情况下,您不会获取加入的 "to-many collection",而是按 项目 ID 分组,并且只是聚合“valoraciones 的值"关系。
根据doctrine pagination documentation,在这种情况下,您可以禁用 $fetchJoinCollection 标志:
$pag = new Paginator($itemsQb, false);
这个问题似乎是由试图将 "missing fields" 从按条件排序的 "missing fields" 添加到 select 子句的学说引起的。 Related issue Pull request
在这种情况下,它认为应该将 "v.score" 添加到 select。
或者,您可以在分页器中禁用输出遍历器以避免上述行为:
$pag->setUseOutputWalkers(false);