Doctrine maxResult关联查询问题:分页结果数量不符
解决Doctrine关联查询中maxResults统计主表记录数的问题
问题描述
在使用Doctrine执行包含内连接的查询时,设置setMaxResults()后,分页逻辑会基于关联后的总记录数计算,而非主表prediction_summary的记录数。例如主表有200条记录,每条关联4条prediction记录,设置maxResults=100时,实际仅返回25条主表记录,无法达到获取100条主表记录的预期。
问题根源
Doctrine的setMaxResults()直接映射到SQL的LIMIT子句,而内连接会让主表每条记录对应多条子表记录,导致最终结果集行数远大于主表行数,分页时自然会截断主表记录数量。
解决方案
方法一:先查询主表ID再关联查询(推荐)
通过子查询先获取符合条件的主表ID并完成分页,再基于这些ID关联子表数据,确保分页逻辑完全基于主表记录数。
修改后的Repository代码:
public function findByForIndex( ?int $client = null, ?int $article = null, ?int $year = null, int $page = 0, int $type = 0, ): mixed { // 第一步:查询符合条件的主表ID并分页 $idQueryBuilder = $this->createQueryBuilder('ps') ->select('ps.id') ->where('ps.type = 0') ->orderBy('ps.client') ->addOrderBy('ps.article') ->addOrderBy('ROW_NUMBER() OVER (PARTITION BY ps.client, ps.article ORDER BY ps.orderYear)') ->addOrderBy('ps.orderYear') ->setMaxResults(self::PAGE_LIMIT) ->setFirstResult($page > 1 ? $page * self::PAGE_LIMIT : 0); // 添加过滤条件(修正原代码中别名错误:pc改为ps) if (!is_null($client)) { $idQueryBuilder->andWhere('ps.client = :clientID')->setParameter('clientID', $client); } if (!is_null($article)) { $idQueryBuilder->andWhere('ps.article = :articleID')->setParameter('articleID', $article); } if (1 === $type) { $idQueryBuilder->andWhere('ps.recurring > 0'); } elseif (2 === $type) { $idQueryBuilder->andWhere('ps.incidental > 0'); } $ids = $idQueryBuilder->getQuery()->getSingleColumnResult(); if (empty($ids)) { return []; } // 第二步:通过ID关联子表查询完整数据 $queryBuilder = $this->createQueryBuilder('ps') ->select('ps', 'p') ->join('ps.predictions', 'p') ->where('ps.id IN (:ids)') ->setParameter('ids', $ids) ->orderBy('ps.client') ->addOrderBy('ps.article') ->addOrderBy('ROW_NUMBER() OVER (PARTITION BY ps.client, ps.article ORDER BY ps.orderYear)') ->addOrderBy('ps.orderYear'); return $queryBuilder->getQuery()->getResult(); }
方法二:使用DISTINCT关键字(简单场景适用)
如果查询逻辑允许,可在select()中添加DISTINCT关键字,让Doctrine先对主表记录去重,再应用分页。注意:该方法在复杂排序或多关联场景下可能存在性能问题,且需确保排序字段均来自主表。
修改示例:
$queryBuilder = $this->createQueryBuilder('ps'); $queryBuilder ->select('DISTINCT ps','p') // 添加DISTINCT关键字 ->join('ps.predictions','p') // 其余查询逻辑保持不变
额外注意
原代码中存在别名错误:条件判断中的pc应为ps(如pc.client实际是ps.client),需修正否则会导致SQL语法错误。
内容的提问来源于stack exchange,提问作者foxoffire33
相关产品推荐
相关产品推荐

