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

关联查询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集合和数量,调整查询逻辑就行:

  1. 把时间条件放到LEFT JOIN的子句里(用WITH),而不是主查询的WHERE,这样不会排除无达标Report的Page
  2. 用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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.23 20:24:52