如何在 SQL 查询与 PHP 后处理中实现 JSON 数据的结构化归并排序

1次阅读

如何在 SQL 查询与 PHP 后处理中实现 JSON 数据的结构化归并排序

本文讲解如何通过 sql 排序配合 php 逻辑,将多对一关联查询结果(如题目与多个选项)按问题归组、合并选项字段,生成结构清晰的嵌套 json 格式,避免重复问题行。

本文讲解如何通过 sql 排序配合 php 逻辑,将多对一关联查询结果(如题目与多个选项)按问题归组、合并选项字段,生成结构清晰的嵌套 json 格式,避免重复问题行。

在实际开发中,当使用 JOIN 多表查询问卷系统(如 questions、answer_options、answer_test)时,原始 SQL 往往返回“一行一选项”的扁平化结果——同一道题因对应多个答案选项而被重复输出多次。但前端或 API 接口通常需要“一道题 + 所有选项数组”的嵌套结构。单纯依赖 SQL 聚合(如 GROUP_CONCAT)虽可拼接字符串,却难以保持结构化数据(如保留选项 ID、顺序、类型等),因此更推荐 SQL 预排序 + PHP 逻辑归并 的组合方案。

✅ 第一步:SQL 层保证数据局部有序

为便于后续 PHP 归并,需确保相同 questions_text(或更稳健的 question_id)的记录连续排列。推荐在原始查询末尾添加 ORDER BY:

SELECT    q.id,   q.questions_text,   a_o.answer_option,   a_o.id AS answer_option_id,   a_o.question_id,   a_t.answer_test_id,   a_t.answer FROM questions q JOIN answer_options a_o ON q.id = a_o.question_id JOIN answer_test a_t ON a_o.id = a_t.answer_option_id  -- 注意:原文 ON 条件有误,应为 a_o.id = a_t.answer_option_id ORDER BY q.id, a_o.id;  -- 按 question_id 升序,再按选项 ID 保序

? 关键修正说明:原问题中 a_o.answer_test_id = a_t.answer_option_id 存在逻辑错误(字段语义错位),正确关联应为 a_o.id = a_t.answer_option_id(即 answer_test 表外键指向 answer_options.id)。务必先校验表结构,避免 JOIN 结果为空或错乱。

✅ 第二步:PHP 层归并构建嵌套结构

利用有序结果,通过单次遍历完成分组聚合。核心思路是维护一个临时变量记录当前处理的问题 ID,并动态构建结果数组:

立即学习PHP免费学习笔记(深入)”;

<?php // 假设 $pdo 已初始化,执行上述 SQL $stmt = $pdo->query($sql); $rows = $stmt->fetchAll(PDO::FETCH_ASSOC);  $result = []; $currentQuestionId = null; $currentQuestion = null;  foreach ($rows as $row) {     // 若进入新题目,保存上一题并初始化新题     if ($row['question_id'] !== $currentQuestionId) {         if ($currentQuestion !== null) {             $result[] = $currentQuestion;         }          // 初始化新题基础字段(取首行值)         $currentQuestion = [             'id'              => $row['id'],             'questions_text'  => trim($row['questions_text']),             'question_id'     => $row['question_id'],             'answer_test_id'  => $row['answer_test_id'],             'answer'          => $row['answer'],             'answer_options'  => [] // 用于收集所有选项         ];         $currentQuestionId = $row['question_id'];     }      // 将当前选项追加到该题的 answer_options 数组中(保持结构化)     $currentQuestion['answer_options'][] = [         'text' => $row['answer_option'],         'id'   => (int)$row['answer_option_id']     ]; }  // 不要遗漏最后一题 if ($currentQuestion !== null) {     $result[] = $currentQuestion; }  // 输出标准 json(UTF-8 安全) header('Content-Type: application/json; charset=utf-8'); echo json_encode($result, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT); ?>

⚠️ 注意事项与最佳实践

  • 唯一标识优先用 question_id:相比 questions_text(可能含空格、换行、大小写差异),question_id 是稳定主键,避免归并逻辑出错;
  • 避免 GROUP_CONCAT 的局限性:若强行用 GROUP_CONCAT(answer_option),会丢失选项元数据(ID、顺序、状态),且需额外 JSON_EXTRACT 解析,增加复杂度;
  • 内存友好性:本方案仅遍历一次结果集,时间复杂度 O(n),无嵌套循环,适用于千级以内题目;
  • 扩展建议:如需支持多语言题目、选项禁用状态等,只需在 $currentQuestion[‘answer_options’][] 中增加对应字段即可,结构天然可扩展。

通过这一组合策略,你既能利用 SQL 的高效排序能力,又能借助 PHP 灵活控制数据形态,最终输出符合现代 API 规范的嵌套 JSON,兼顾性能、可读性与可维护性。

text=ZqhQzanResources