
本文介绍如何将扁平化的 mysql 查询结果(每人多条技能记录)重构为按人员分组、技能与分数键值映射的嵌套 json 格式,避免重复姓名,提升前端数据消费效率。
本文介绍如何将扁平化的 mysql 查询结果(每人多条技能记录)重构为按人员分组、技能与分数键值映射的嵌套 json 格式,避免重复姓名,提升前端数据消费效率。
要实现目标 JSON 结构——即每个对象包含 "name" 字段和一个 "skills" 关联数组(键为技能名,值为对应分数),关键在于在 PHP 层对查询结果进行逻辑聚合,而非依赖 SQL 的复杂 JOIN 或 GROUP_CONCAT。原始代码中使用 [] 追加关联数组,导致每条记录独立;而我们需要以 person_name 为键,动态构建嵌套结构。
以下是优化后的完整函数实现:
function getList() {
$sql = "SELECT DISTINCT
`person`.`person_name` AS name,
`skill`.`skill_name` AS skill,
`skill_linker`.`skill_score` AS skill_score
FROM `person`
INNER JOIN `skill_linker` ON `person`.`person_id` = `skill_linker`.`person_id`
INNER JOIN `skill` ON `skill_linker`.`skill_id` = `skill`.`skill_id`";
$result = $GLOBALS['conn']->query($sql);
if ($result && $result->num_rows > 0) {
$grouped = [];
while ($row = $result->fetch_assoc()) {
$name = $row['name'];
// 初始化该人员条目(若首次出现)
if (!isset($grouped[$name])) {
$grouped[$name] = [
'name' => $name,
'skills' => []
];
}
// 将技能名作为键、分数作为值存入 skills 数组
$grouped[$name]['skills'][$row['skill']] = (int)$row['skill_score'];
}
// 转换为索引数组(移除 name 键,保留顺序)
$output = array_values($grouped);
header('Content-Type: application/json; charset=utf-8');
echo json_encode($output, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT);
} else {
header('Content-Type: application/json; charset=utf-8');
echo json_encode([], JSON_PRETTY_PRINT);
}
}✅ 关键改进说明:
- 使用 $grouped[$name] 作为临时主键,自动合并同一人的多条记录;
- skills 被定义为关联数组(["Dancing" => 3, "Surfing" => 2]),天然支持 JavaScript 对象解构或 PHP foreach ($skills as $skill => $score) 遍历;
- 显式类型转换 (int)$row['skill_score'] 防止字符串型数字(如 "3")被 JSON 编码为字符串,确保前端数值运算可靠;
- array_values() 将关联数组转为纯索引数组,确保最终输出为标准 JSON 列表([{}, {}]),而非带命名键的对象;
- 添加 header() 设置正确 MIME 类型,并启用 JSON_UNESCAPED_UNICODE 支持中文姓名不被转义。
⚠️ 注意事项:
立即学习“PHP免费学习笔记(深入)”;
- 若数据库中存在空值(如 NULL 技能名),需在 while 循环内增加 if (empty($row['name']) || empty($row['skill'])) continue; 过滤,避免索引错误;
- 生产环境应避免全局变量 $GLOBALS['conn'],推荐使用依赖注入或封装数据库实例;
- 如需兼容旧版 PHP(< 7.4),请勿使用 null 合并操作符(??),本例已规避该语法。
此方案简洁高效,完全在应用层完成数据整形,无需修改数据库结构或引入额外扩展,是处理“一对多”关系 JSON 化的典型实践。



















