关联查询Page与Report:如何同时获取过滤后的集合及数量?
问题描述
我的数据模型里,Page和Report是一对多关联,Report有datetime日期字段。我要在指定时间范围内做到三件事:
- 获取所有Page数据
- 拿到每个Page下符合时间条件的Report集合
- 同时得到每个Page对应符合条件的Report数量
我现在用的QueryBuilder代码是这样的:
$this->createQueryBuilder('p') ->leftJoin('p.reports', 'page_reports') ->addSelect('page_reports') ->where('page_reports.datetime > :since') ->setParameter('since', new DateTime('-15 days')) ->getQuery() ->execute();
这段代码能返回带过滤后Report集合的Page实体,但我不知道怎么额外拿到每个Page对应的符合条件的Report数量。想问下当前实现对不对?还是应该先查Page,再用Criteria过滤Report集合,比如:
$criteria = Criteria::create() ->where(Criteria::expr()->gt("datetime", $since)); $reports = $page->getReports()->matching($criteria);
解决方案与分析
先说说你当前QueryBuilder的问题
你现在的写法有问题:因为把page_reports.datetime的条件放到了WHERE里,会直接过滤掉那些没有符合条件Report的Page,而你要的是全部Page数据,不管有没有达标Report。
方案一:修正QueryBuilder,一次查询搞定所有需求
要保留所有Page,同时拿到过滤后的Report集合和数量,调整查询逻辑就行:
- 把时间条件放到
LEFT JOIN的子句里(用WITH),而不是主查询的WHERE,这样不会排除无达标Report的Page - 用
COUNT聚合函数统计数量,加到查询结果里
示例代码:
$query = $this->createQueryBuilder('p') // 左关联并过滤符合时间条件的Report ->leftJoin('p.reports', 'page_reports', 'WITH', 'page_reports.datetime > :since') // 选中过滤后的Report集合 ->addSelect('page_reports') // 统计每个Page的达标Report数量 ->addSelect('COUNT(page_reports.id) AS report_count') // 按Page分组,确保聚合统计生效 ->groupBy('p.id') ->setParameter('since', new DateTime('-15 days')) ->getQuery(); $pages = $query->execute();
使用时:
- 每个Page实体可以直接调用
getReports()拿到过滤后的集合 - 数量
report_count需要你在Page实体里加对应的属性和getReportCount()方法,或者直接从查询返回的数组中读取(如果用数组形式接收结果)
方案二:先查所有Page,再用Criteria过滤(小数据量可用)
如果Page数量不多,先查全量Page,再逐个用Criteria过滤Report并统计数量是可行的,但这种方式会触发N+1查询(每个Page单独查一次Report),数据量大时性能拉胯。
示例代码:
$since = new DateTime('-15 days'); // 查询所有Page $pages = $this->findAll(); foreach ($pages as $page) { $criteria = Criteria::create() ->where(Criteria::expr()->gt("datetime", $since)); // 获取符合条件的Report集合 $filteredReports = $page->getReports()->matching($criteria); // 数量就是集合的长度 $reportCount = count($filteredReports); // 可以把数量存到Page的临时属性里,方便后续使用 $page->setReportCount($reportCount); }
两种方案怎么选
- 方案一:只跑1次SQL,性能最好,适合大数据量场景,但要调整QueryBuilder写法,还要给Page加统计属性
- 方案二:写法简单,但N+1查询问题明显,只适合Page少的情况
内容的提问来源于stack exchange,提问作者James Hall
相关产品推荐
相关产品推荐

