
本文介绍如何通过 sql 排序配合 php 逻辑,将一对多关联查询结果(如题目与多个答案选项)聚合成每个题目仅一条 json 记录、答案选项以数组形式内嵌的结构,避免重复字段冗余。
本文介绍如何通过 sql 排序配合 php 逻辑,将一对多关联查询结果(如题目与多个答案选项)聚合成每个题目仅一条 json 记录、答案选项以数组形式内嵌的结构,避免重复字段冗余。
在实际开发中,常需将多表 JOIN 查询(如 questions → answer_options → answer_test)的结果按业务语义“折叠”:同一道题目的多个答案选项应合并至单个对象中,而非生成多条重复题干的记录。原始 SQL 返回的是扁平化的笛卡尔式结果,而目标 JSON 要求结构化聚合——这无法单靠 SQL 的 GROUP BY 完全实现(因需保留所有关联字段且 answer_option 需转为数组),需结合后端逻辑协同处理。
✅ 正确解决路径:SQL 排序 + PHP 聚合
1. SQL 层:确保数据有序,为聚合铺路
首先优化查询,添加 ORDER BY q.questions_text, a_o.id(或 q.id),使相同题目的记录连续排列:
$sql = "SELECT
q.id AS question_id,
q.questions_text,
a_o.answer_option,
a_o.id AS answer_option_id,
a_t.answer,
a_t.answer_test_id
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
ORDER BY q.id, a_o.id";? 注意:使用 q.id(而非 questions_text)排序更可靠,避免因文本空格、大小写导致分组错位;同时 a_o.id 保证选项顺序稳定。
2. PHP 层:流式聚合,构建嵌套结构
执行查询后,遍历有序结果集,用临时变量识别题目边界,动态构建聚合数组:
立即学习“PHP免费学习笔记(深入)”;
$result = $pdo->query($sql)->fetchAll(PDO::FETCH_ASSOC);
$grouped = [];
$tempQuestionId = null;
$currentQuestion = [];
foreach ($result as $row) {
// 新题目开始:保存上一个题目,初始化当前题目
if ($row['question_id'] !== $tempQuestionId) {
if ($currentQuestion) {
$grouped[] = $currentQuestion;
}
$currentQuestion = [
'id' => $row['question_id'],
'questions_text' => $row['questions_text'],
'answer_option' => [$row['answer_option']], // 初始化为数组
'question_id' => $row['question_id'],
'answer_test_id' => $row['answer_test_id'],
'answer' => $row['answer'],
'answer_option_id'=> $row['answer_option_id']
];
$tempQuestionId = $row['question_id'];
} else {
// 同一题目:追加 answer_option 到数组
$currentQuestion['answer_option'][] = $row['answer_option'];
// ⚠️ 注意:若其他字段(如 answer_test_id)可能不同,需明确业务规则:
// 此处假设同一题目的 answer_test_id / answer 均一致;否则应改用数组或忽略
}
}
// 添加最后一个题目
if ($currentQuestion) {
$grouped[] = $currentQuestion;
}
// 输出标准化 JSON(answer_option 始终为数组)
echo json_encode($grouped, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT);3. 输出效果示例
输入原始三行数据,输出即为符合要求的聚合结构:
[
{
"id": "1",
"questions_text": "Who you are ? ",
"answer_option": ["Dog", "Cat"],
"question_id": "1",
"answer_test_id": "1",
"answer": "1",
"answer_option_id": "1"
},
{
"id": "2",
"questions_text": "What your car",
"answer_option": ["Audi"],
"question_id": "2",
"answer_test_id": "1",
"answer": "1",
"answer_option_id": "1"
}
]⚠️ 关键注意事项
- 字段一致性:answer_test_id、answer 等字段若在同题下存在差异,需明确聚合策略(取首个?取最大?合并去重?),不可盲目覆盖。
- 性能考量:大数据量时,避免在 PHP 中做复杂逻辑;可考虑用 MySQL 8.0+ 的 JSON_ARRAYAGG() + GROUP_CONCAT() 预聚合(但需注意字符串长度限制与类型转换)。
- 健壮性增强:生产环境建议添加 isset() 检查、异常捕获,并对 answer_option 字段做 array_unique() 去重(如业务允许)。
通过“SQL 保序 + PHP 流式聚合”的组合方案,既保持数据库查询简洁高效,又赋予应用层灵活的数据塑形能力,是处理此类一对多 JSON 结构化需求的经典实践。











