使用 join 从单个查询中获取嵌套数组

GZs*_*dio 0 php mysql arrays multidimensional-array

我有共同的情况,但我找不到共同的解决方案......

想象一下我有两个表,如作者和相应的书籍:

作者

+----+-----------+
| id | name      |
+----+-----------+
|  1 | O.Connor  |
|  2 | F.Myler   |
+----+-----------+
Run Code Online (Sandbox Code Playgroud)

图书

+----+-----------+-----------+----------------+
| id | author_id | title     | number_of_pages|
+----+-----------+-----------+----------------+
|  1 | 1         |Book 1     |315             |
|  2 | 1         |Book 2     |265             |
+----+-----------+-----------+----------------+
Run Code Online (Sandbox Code Playgroud)

我想在一个sql查询中从数据库获取数据(我使用mySQL,但这并不重要,我想要一个独立于数据库的解决方案),使用JOIN,例如:

SELECT A.*, B.title as book_title, B.number_of_pages as book_pages FROM authors A LEFT JOIN books B ON B.author_id=A.id
Run Code Online (Sandbox Code Playgroud)

由于大型数据库的生产力问题,我想避免循环中的多个查询。然后我需要将第二个表中的数据嵌套为子数组,以避免拥有相同数据的多个副本。换句话说,我需要将数据库的平面结果转换为嵌套的多维数组。它应该给出类似的内容:

[
    {
        "id": 3,
        "name": "O.Connor",
        "birth_date": "05.06.1985",
        "from": "England",
        "mother": "",
        "father": "",
        "email": "email@example.com",
        "books": [
            {
                "title": "Some Title",
                "pages": 235
            },
            {
                "title": "Some Title",
                "pages": 267
            },
            {
                "title": "Some Title",
                "pages": 317
            },
            {
                "title": "Some Title",
                "pages": 235
            },
            {
                "title": "Some Title",
                "pages": 298
            }
        ]
    },
    {
        "id": 28,
        "name": "O.Henri",
        "birth_date": "05.06.1300",
        "from": "England",
        "mother": "",
        "father": "",
        "email": "email@example.com",
        "books": [
            {
                "title": "Some Title",
                "pages": 235
            },
            {
                "title": "Some Title",
                "pages": 267
            },
            {
                "title": "Some Title",
                "pages": 317
            },
            {
                "title": "Some Title",
                "pages": 235
            },
            {
                "title": "Some Title",
                "pages": 298
            }
        ]
    }
]
Run Code Online (Sandbox Code Playgroud)

到目前为止我找到的唯一相关资源是:

使用 join 获取嵌套对象数组的 SQL 查询

https://phpdelusions.net/pdo_examples/nested_array

第一个使用多个查询或一些 MySQL 中不可用的 SQL Server 功能。第二个建议分别从结果中获取每一行并即时构建结果数组。也许这不是一个糟糕的解决方案,但也许有更好、更高效的解决方案。

PDO 中有一些获取模式,例如 FETCH_GROUP 等。但对于我的任务来说,没有什么足够的。

我编写了自己的函数,其想法是进一步创建一些数据库类并将此函数作为方法插入(因此对于大多数情况来说它应该是真正通用的)。

function group_by($array, $criterias=NULL, $group_name='group') {
    if ($criterias == NULL) return $array; // add also a verification of type
    // transform input parameters for array_diff_key()
    $criterias = array_flip($criterias);
    $group = array($group_name=>0);

    $result_array = [];
    $push_index = 0;
    foreach ($array as $row) {
        $sup_array = array_diff_key($row, $criterias);
        $sub_array = array_intersect_key($row, $criterias);
        // add verification of not NULL elements in future
        $isInArray = false;

        foreach ($result_array as $index => $grouped_array) {
            // if there is no difference between arrays then this sub-array is already present in a result array.
            $array_without_groups = array_diff_key($grouped_array, $group);
            if ( empty(array_diff_assoc($array_without_groups, $sup_array)) && (count($array_without_groups)==count($sup_array)) ) {
                $isInArray = true;
                $push_index = $index;
                break;
            }
        }
        if ($isInArray) {
            array_push($result_array[$push_index][$group_name], $sub_array);
        } else {
            $sup_array[$group_name] = array();
            array_push($sup_array[$group_name], $sub_array);
            array_push($result_array, $sup_array);
        }
    }

    return $result_array;
}
Run Code Online (Sandbox Code Playgroud)

然后你在 fetchAll 结果上调用它:

$result = group_by($result, array('book_title', 'book_pages'), 'books');
Run Code Online (Sandbox Code Playgroud)

我不认为这是最好的解决方案。我是 PHP 新手,所以我什至不确定我使用的 array_diff 和 array_intersect 是否是从另一个数组中提取一个数组的有效方法。我将不胜感激任何建议。

Bil*_*win 5

这是一个适用于 MySQL 5.7 或更高版本的解决方案。

SELECT A.id, A.name, A.birth_date, A.`from`, A.mother, A.father, A.email, 
  JSON_ARRAYAGG(JSON_OBJECT('title', B.title, 'pages', B.number_of_pages)) AS books
FROM authors A LEFT JOIN books B ON B.author_id=A.id
GROUP BY A.id;
Run Code Online (Sandbox Code Playgroud)