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

如何在Doctrine 2中将子查询结果追加到主查询结果末尾?

在Doctrine 2中将子查询结果追加到主查询结果末尾的实现方法

要实现主查询结果在前、子查询结果在后的合并效果,Doctrine Query Builder本身不直接支持UNION操作,你可以通过以下两种方式解决:

方法一:使用原生SQL结合UNION ALL(推荐,性能更优)

通过给主查询和子查询添加优先级字段,确保主查询结果排在前面,再用UNION ALL合并,同时避免重复数据:

public function findPosts(): array
{
    $ids = [777, 775, 767, 756, 752, 751, 732, 727, 725, 721, 717, 710, 702, 693, 678, 671, 662, 658, 639, 638, 617, 598, 579];
    $presentDate = date('Y-m-d H:i:s');

    // 构建主查询:排除子查询中的ID避免重复,添加优先级字段1
    $qbMain = $this->createQueryBuilder('article')
        ->select('article.id', 'article.title', 'article.slug', 'article.slots_type', 'article.skill', 'article.type', 'article.views', 'article.vk_shares', 'article.fb_shares', 'image.filename AS img', '1 AS priority')
        ->leftJoin('article.img', 'image')
        ->where('article.visible = 1')
        ->andWhere('article.published_at <= :present_date')
        ->andWhere($qbMain->expr()->notIn('article.id', ':ids'))
        ->setParameter('present_date', $presentDate)
        ->setParameter('ids', $ids)
        ->orderBy('article.id', 'DESC')
        ->setMaxResults(30);

    // 构建子查询:添加优先级字段2
    $qbSub = $this->createQueryBuilder('article_sub')
        ->select('article_sub.id', 'article_sub.title', 'article_sub.slug', 'article_sub.slots_type', 'article_sub.skill', 'article_sub.type', 'article_sub.views', 'article_sub.vk_shares', 'article_sub.fb_shares', 'image_sub.filename AS img', '2 AS priority')
        ->leftJoin('article_sub.img', 'image_sub')
        ->where('article_sub.id IN (:ids)')
        ->andWhere('article_sub.visible = 1')
        ->setParameter('ids', $ids)
        ->orderBy('article_sub.id', 'DESC');

    // 拼接UNION ALL SQL并执行
    $mainSql = $qbMain->getQuery()->getSQL();
    $subSql = $qbSub->getQuery()->getSQL();
    $unionSql = "$mainSql UNION ALL $subSql ORDER BY priority ASC, id DESC";

    $conn = $this->getEntityManager()->getConnection();
    $stmt = $conn->prepare($unionSql);
    $stmt->bindValue('present_date', $presentDate);
    $stmt->bindValue('ids', $ids, \Doctrine\DBAL\Types\Type::INTEGER);
    $stmt->execute();

    return $stmt->fetchAllAssociative();
}

关键说明:

  • 给两个查询分别添加priority字段,主查询设为1,子查询设为2,最终通过ORDER BY priority ASC确保主结果在前。
  • 主查询中用notIn排除子查询的ID,避免同一篇文章重复出现。
  • 将ids改为数组并使用参数绑定,避免SQL注入风险,比直接拼接字符串更安全。

方法二:内存中合并两个查询结果(适合数据量较小的场景)

如果数据量不大,可以分别执行两个查询,再在内存中合并并去重:

public function findPosts(): array
{
    $ids = [777, 775, 767, 756, 752, 751, 732, 727, 725, 721, 717, 710, 702, 693, 678, 671, 662, 658, 639, 638, 617, 598, 579];
    $presentDate = date('Y-m-d H:i:s');

    // 执行主查询
    $mainResults = $this->createQueryBuilder('article')
        ->select('article.id', 'article.title', 'article.slug', 'article.slots_type', 'article.skill', 'article.type', 'article.views', 'article.vk_shares', 'article.fb_shares')
        ->leftJoin('article.img', 'image')
        ->addSelect('image.filename AS img')
        ->where('article.visible = 1')
        ->andWhere('article.published_at <= :present_date')
        ->setParameter('present_date', $presentDate)
        ->orderBy('article.id', 'DESC')
        ->setMaxResults(30)
        ->getQuery()
        ->execute();

    // 执行子查询
    $subResults = $this->createQueryBuilder('article')
        ->select('article.id', 'article.title', 'article.slug', 'article.slots_type', 'article.skill', 'article.type', 'article.views', 'article.vk_shares', 'article.fb_shares')
        ->leftJoin('article.img', 'image')
        ->addSelect('image.filename AS img')
        ->where('article.id IN (:ids)')
        ->andWhere('article.visible = 1')
        ->setParameter('ids', $ids)
        ->orderBy('article.id', 'DESC')
        ->getQuery()
        ->execute();

    // 合并结果并去重:保留主查询的项,再追加子查询中未出现的项
    $mainIds = array_column($mainResults, 'id');
    $finalResults = $mainResults;
    foreach ($subResults as $item) {
        if (!in_array($item['id'], $mainIds)) {
            $finalResults[] = $item;
        }
    }

    return $finalResults;
}

关键说明:

  • 先获取主查询结果,再遍历子查询结果,只追加主查询中没有的项,确保顺序和去重。
  • 实现简单,不需要接触原生SQL,但数据量大时会增加内存开销和数据库查询次数。

关于FIELD函数无效的原因

FIELD函数仅能指定单个字段的自定义排序顺序,无法实现“将一组结果整体追加到另一组结果末尾”的分组排序需求。要实现这种分组顺序,必须通过添加优先级字段来区分两组数据,再基于该字段排序。

内容的提问来源于stack exchange,提问作者Michael

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.31 20:15:51