0

0

PHP从SQL联接结果构建嵌套数组的有效方法

DDD

DDD

发布时间:2025-10-14 13:51:21

|

264人浏览过

|

来源于php中文网

原创

PHP从SQL联接结果构建嵌套数组的有效方法

本文详细介绍了如何使用php将扁平化的sql联接查询结果转换为具有层级结构的嵌套数组,例如将问卷及其包含的问题组织成一个主从式数据结构。通过一个高效的单次遍历循环,利用父级id作为数组键进行条件判断和数据聚合,避免了重复数据并确保了正确的嵌套格式,最终生成符合预期的json输出。

在Web开发中,我们经常需要从关系型数据库中检索数据并将其组织成更符合前端展示或API接口需求的层级结构。一个常见的场景是,当存在“一对多”关系(如一个问卷包含多个问题)时,通过SQL JOIN 操作获取的数据通常是扁平化的,即每个子记录都会重复父记录的信息。本文将指导您如何高效地将这种扁平数据转换为嵌套的PHP数组结构。

1. 问题描述与数据模型

假设我们有两个表:questionnaires (问卷) 和 questions (问题),它们之间通过一个联接表 questionnaireshasquestions 建立多对多关系。我们希望得到的数据结构是一个包含问卷信息(ID、标题)及其所有相关问题(ID、文本)的嵌套数组,类似以下JSON格式:

[
  {
    "id": "x",
    "title": "x",
    "questions": [
      {"id": "x", "text": "x"},
      {"id": "x2", "text": "x2"}
    ]
  }
]

为了获取所有问卷及其对应的问题,我们通常会使用 INNER JOIN 查询:

SELECT 
    questionnaires.id AS QuestionnaireId, 
    questionnaires.title AS QuestionnaireTitle, 
    questions.id AS QuestionId, 
    questions.text AS Question 
FROM 
    questionnaires 
INNER JOIN 
    questionnaireshasquestions qa ON qa.idQuestionnaire = questionnaires.id 
INNER JOIN 
    questions ON questions.id = qa.idQuestion;

这条查询会返回一个扁平化的结果集,其中每个问卷-问题对都会作为一行数据。例如,如果问卷A有3个问题,那么问卷A的信息会在结果集中出现3次。

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

2. 初始尝试及存在的问题

许多初学者在处理这种扁平数据时,可能会尝试在循环中直接构建数组。一个常见的错误尝试如下:

$data = [];
while ($row = $conn->fetch()) {
    if (!isset($data['questionnaires'][$row['QuestionnaireId']])) {
        $data['questionnaires'][] = [
            'id' => $row['QuestionnaireId'],
            'title' => $row['QuestionnaireTitle'],
            'questions' => [
                'id' => $row['QuestionId'],
                'text' => $row['Question']
            ]
        ]; 
    } else {
        // 这里的逻辑可能导致问题,例如覆盖或不正确的追加
        $data['questionnaires'][$row['QuestionnaireId']][] = [
           'questions' => [
                'id' => $row['QuestionId'],
                'text' => $row['Question']
            ]           
        ];  
    }
}

这种方法通常会导致以下问题:

讯飞智作-讯飞配音
讯飞智作-讯飞配音

讯飞智作是一款集AI配音、虚拟人视频生成、PPT生成视频、虚拟人定制等多功能的AI音视频生产平台。已广泛应用于媒体、教育、短视频等领域。

下载
  • 重复的父级记录: 如果不正确地处理 else 分支,可能会为每个问题都创建一个新的问卷记录。
  • 子记录覆盖: 如果 questions 字段直接被赋值而不是追加到数组中,那么同一个问卷的后续问题会覆盖掉之前的问题。
  • 不正确的数组索引: [] 操作符在非关联数组中使用时会创建新的数字索引,这可能导致最终输出的JSON格式不符合预期。

3. 正确的嵌套数组构建方法

解决上述问题的关键在于,使用父级记录的唯一ID作为临时数组的键,这样可以确保每个父级记录只被初始化一次,并且其子级记录可以正确地被追加到对应的子数组中。

以下是优化的PHP代码实现:

fetch() 方法可以逐行获取查询结果
// 示例数据模拟,实际应用中替换为您的数据库查询循环
$rows = [
    ['QuestionnaireId' => '1', 'QuestionnaireTitle' => 'Are you hungry?', 'QuestionId' => '1', 'Question' => 'How is your passion?'],
    ['QuestionnaireId' => '1', 'QuestionnaireTitle' => 'Are you hungry?', 'QuestionId' => '2', 'Question' => 'Do you drink?'],
    ['QuestionnaireId' => '2', 'QuestionnaireTitle' => 'How are you feeling?', 'QuestionId' => '1', 'Question' => 'How is your passion?'],
    ['QuestionnaireId' => '2', 'QuestionnaireTitle' => 'How are you feeling?', 'QuestionId' => '3', 'Question' => 'Do you like fish?'],
    ['QuestionnaireId' => '5', 'QuestionnaireTitle' => 'Is testing working?', 'QuestionId' => '4', 'Question' => 'How is the testing?']
];

$data = ['questionnaires' => []]; // 初始化一个用于存放最终数据的数组

foreach ($rows as $row) { // 实际应用中替换为 while ($row = $conn->fetch())
    $questionnaireId = $row['QuestionnaireId'];

    // 检查当前问卷ID是否已经存在于 $data['questionnaires'] 中
    if (!isset($data['questionnaires'][$questionnaireId])) {
        // 如果是第一次遇到这个问卷ID,则初始化问卷信息
        // 并为其 'questions' 字段初始化一个空数组,用于后续添加问题
        $data['questionnaires'][$questionnaireId] = [
            'id'        => $row['QuestionnaireId'],
            'title'     => $row['QuestionnaireTitle'],
            'questions' => [], // 初始化一个空数组来收集问题
        ];
    }

    // 将当前行的问题信息添加到对应问卷的 'questions' 数组中
    $data['questionnaires'][$questionnaireId]['questions'][] = [
        'id'   => $row['QuestionId'],
        'text' => $row['Question']
    ];
}

// 由于我们使用了问卷ID作为键,最终的 $data['questionnaires'] 会是一个关联数组
// 如果需要一个索引数组(数字键),可以使用 array_values()
$finalQuestionnaires = array_values($data['questionnaires']);

// 打印最终的JSON格式数据
echo json_encode($finalQuestionnaires, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE);

?>

代码解析:

  1. $data = ['questionnaires' => []];: 初始化一个名为 $data 的数组,其中包含一个 questionnaires 键,它的值是一个空数组。这个空数组将用于存储我们构建的嵌套问卷数据。
  2. $questionnaireId = $row['QuestionnaireId'];: 从当前行中提取问卷ID,作为后续判断的依据。
  3. if (!isset($data['questionnaires'][$questionnaireId])): 这是核心判断逻辑。它检查 $data['questionnaires'] 中是否已经存在以当前 $questionnaireId 为键的元素。
    • 如果不存在(第一次遇到此问卷ID):
      • $data['questionnaires'][$questionnaireId] = [...]: 使用当前 $questionnaireId 作为键,创建一个新的问卷数组。这样做的好处是,同一个问卷ID的记录都会指向同一个内存位置,不会重复创建问卷对象。
      • 'questions' => []: 在创建问卷时,立即为其 questions 字段初始化一个空数组。这是至关重要的一步,它确保了 questions 始终是一个数组,可以接受多个问题。
    • 如果已存在(此问卷ID之前已处理过): if 条件不满足,程序直接跳到 else 块(在优化后的代码中,实际上是跳过 if 块,直接执行后续的追加逻辑)。
  4. $data['questionnaires'][$questionnaireId]['questions'][] = [...]: 无论当前问卷是第一次出现还是已经存在,我们都将当前行的问题信息追加到对应问卷的 questions 数组中。[] 操作符确保了新问题被添加到数组的末尾,而不是覆盖现有问题。
  5. $finalQuestionnaires = array_values($data['questionnaires']);: 在循环结束后,$data['questionnaires'] 是一个关联数组,其键是 QuestionnaireId。如果最终需要一个索引数组(即从0开始的数字键),可以使用 array_values() 函数将其转换为一个普通的索引数组,这通常是JSON数组的期望格式。

4. 最终输出示例

经过上述处理,json_encode($finalQuestionnaires) 将生成如下结构的数据:

[
    {
        "id": "1",
        "title": "Are you hungry?",
        "questions": [
            {
                "id": "1",
                "text": "How is your passion?"
            },
            {
                "id": "2",
                "text": "Do you drink?"
            }
        ]
    },
    {
        "id": "2",
        "title": "How are you feeling?",
        "questions": [
            {
                "id": "1",
                "text": "How is your passion?"
            },
            {
                "id": "3",
                "text": "Do you like fish?"
            }
        ]
    },
    {
        "id": "5",
        "title": "Is testing working?",
        "questions": [
            {
                "id": "4",
                "text": "How is the testing?"
            }
        ]
    }
]

这正是我们期望的、结构清晰的嵌套数组格式。

5. 注意事项与最佳实践

  • 唯一标识符作为键: 确保用于作为父级数组键的字段(如 QuestionnaireId)是唯一的。如果不是唯一的,数据可能会被错误地合并。
  • 单次遍历效率: 这种方法只需对数据库查询结果进行一次遍历,效率较高。避免在循环内部执行额外的数据库查询,那会导致N+1查询问题,严重影响性能。
  • 内存使用: 对于非常大的结果集,一次性将所有数据加载到内存中可能会消耗大量内存。在这种情况下,可以考虑使用流式处理或分页。
  • 错误处理: 在实际应用中,应加入适当的错误处理机制,例如检查数据库连接是否成功,查询是否返回结果等。
  • json_encode 选项: 使用 JSON_PRETTY_PRINT 可以使输出的JSON更易读,JSON_UNESCAPED_UNICODE 确保中文字符不会被转义。

总结

通过利用父级记录的唯一ID作为临时数组的键,并巧妙地在循环中进行条件判断,我们能够高效且准确地将扁平化的SQL联接结果转换为复杂的嵌套PHP数组。这种模式在处理一对多关系数据时非常实用,能够帮助开发者构建出结构清晰、易于消费的数据接口。

相关专题

更多
php文件怎么打开
php文件怎么打开

打开php文件步骤:1、选择文本编辑器;2、在选择的文本编辑器中,创建一个新的文件,并将其保存为.php文件;3、在创建的PHP文件中,编写PHP代码;4、要在本地计算机上运行PHP文件,需要设置一个服务器环境;5、安装服务器环境后,需要将PHP文件放入服务器目录中;6、一旦将PHP文件放入服务器目录中,就可以通过浏览器来运行它。

1989

2023.09.01

php怎么取出数组的前几个元素
php怎么取出数组的前几个元素

取出php数组的前几个元素的方法有使用array_slice()函数、使用array_splice()函数、使用循环遍历、使用array_slice()函数和array_values()函数等。本专题为大家提供php数组相关的文章、下载、课程内容,供大家免费下载体验。

1306

2023.10.11

php反序列化失败怎么办
php反序列化失败怎么办

php反序列化失败的解决办法检查序列化数据。检查类定义、检查错误日志、更新PHP版本和应用安全措施等。本专题为大家提供php反序列化相关的文章、下载、课程内容,供大家免费下载体验。

1212

2023.10.11

php怎么连接mssql数据库
php怎么连接mssql数据库

连接方法:1、通过mssql_系列函数;2、通过sqlsrv_系列函数;3、通过odbc方式连接;4、通过PDO方式;5、通过COM方式连接。想了解php怎么连接mssql数据库的详细内容,可以访问下面的文章。

948

2023.10.23

php连接mssql数据库的方法
php连接mssql数据库的方法

php连接mssql数据库的方法有使用PHP的MSSQL扩展、使用PDO等。想了解更多php连接mssql数据库相关内容,可以阅读本专题下面的文章。

1400

2023.10.23

html怎么上传
html怎么上传

html通过使用HTML表单、JavaScript和PHP上传。更多关于html的问题详细请看本专题下面的文章。php中文网欢迎大家前来学习。

1229

2023.11.03

PHP出现乱码怎么解决
PHP出现乱码怎么解决

PHP出现乱码可以通过修改PHP文件头部的字符编码设置、检查PHP文件的编码格式、检查数据库连接设置和检查HTML页面的字符编码设置来解决。更多关于php乱码的问题详情请看本专题下面的文章。php中文网欢迎大家前来学习。

1439

2023.11.09

php文件怎么在手机上打开
php文件怎么在手机上打开

php文件在手机上打开需要在手机上搭建一个能够运行php的服务器环境,并将php文件上传到服务器上。再在手机上的浏览器中输入服务器的IP地址或域名,加上php文件的路径,即可打开php文件并查看其内容。更多关于php相关问题,详情请看本专题下面的文章。php中文网欢迎大家前来学习。

1303

2023.11.13

php源码安装教程大全
php源码安装教程大全

本专题整合了php源码安装教程,阅读专题下面的文章了解更多详细内容。

7

2025.12.31

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
PHP课程
PHP课程

共137课时 | 8.1万人学习

JavaScript ES5基础线上课程教学
JavaScript ES5基础线上课程教学

共6课时 | 6.9万人学习

PHP新手语法线上课程教学
PHP新手语法线上课程教学

共13课时 | 0.8万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号