如何从单个 MySQL table 中获取按 PHP 中的多列分组的多维数组?

How to get multidimensional array grouped by multiple columns in PHP from single MySQL table?

我有 table 看起来像这样的“guids”:

id |survey_type |guid |guid_type |table_id |created_at |
2 |TYPE 1 |6C7251E3-2151-4754-A413-51899FAAF6C2 |question |2 |2022-03-20 16:14:09 |
3 |TYPE 1 |EF5AFA93-C74D-4920-A13A-17A9B43239CD |question |3 |2022-03-20 16:14:09 |
4 |TYPE 1 |5C059148-94BE-4225-B5C2-551A81B65F16 |question |4 |2022-03-20 16:14:09 |
5 |TYPE 1 |356B8A5C-1072-47A5-A508-D9BDCBA92CCC |answer |5 |2022-03-20 16:14:09 |
6 |TYPE 1 |E0CE4C26-7ABD-4162-9C8C-B4DD540AE268 |answer |6 |2022-03-20 16:14:09 |
7 |TYPE 1 |BFBC50FC-892D-43E9-A235-D76E0D0BEF29 |answer |7 |2022-03-20 16:14:09 |
8 |TYPE 2 |B9DCC5C1-CBFB-4589-98EF-4524F3958968 |survey |8 |2022-03-20 16:14:09 |
9 |TYPE 2 |C98FBFF9-6FE3-414E-BB14-08EDC8281E66 |survey |9 |2022-03-20 16:14:09 |
10 |TYPE 2 |8A780B6E-EAE0-47D6-9D05-F52B795AE617 |question |10 |2022-03-20 16:14:09 |
11 |TYPE 2 |E3818D30-BB69-4F03-B56D-B31691F8007E |question |11 |2022-03-20 16:14:09 |
12 |TYPE 2 |24C81BEF-BFCE-4964-AB01-F3579251313D |answer |12 |2022-03-20 16:14:09 |
13 |TYPE 3 |59381701-AFBC-48F8-AECE-DB3702EE2B15 |answer |13 |2022-03-20 16:14:09 |
14 |TYPE 3 |7F4AC694-74DC-4BEA-ACFB-D8F070769FEE |answer |14 |2022-03-20 16:14:09 |
15 |TYPE 3 |B5C405B9-BA7E-471A-87DD-B69D9757276F |survey |15 |2022-03-20 16:14:09 |

我想以此格式获取数据:

Array
(
    [survey_type] => Array
        (
            [guid_type] => Array
                (
                    [table_id] => guid
                    ...
                )
            ... // all guid types
        )
    ... // all survey types
)

基本上,我需要使用多列来构​​建包含分组数据的嵌套数组。 我知道我可以像这样使用 foreach 循环实现该结构:

// select existing guids
$this->db->select('*')
      ->from('guids');
$query = $this->db->get();
$existing_guids = $query->result_array();
$formated_guids = [];

foreach($existing_guids as $key => $guid_data) {
    $formated_guids[$guid_data['survey_type']][$guid_data['guid_type']][$guid_data['table_id']] = $guid_data['guid'];
}

但如果可能的话,我希望在数据库层有一个更优化的解决方案,因为这个 table 预计会有数百万行并且遍历整个 table 似乎不是最优化的方式。

我尝试了 GROUP_CONCAT mysql 命令,但未能生成所需的结构

SELECT `survey_type`, GROUP_CONCAT(`guid_type`) AS `guid_types` FROM guids GROUP BY `survey_type`

模拟数据的预期结果:

Array
(
    [TYPE 1] => Array // survey type column
        (
            [question] => Array // guid_type column
                (
                    // table_id => guid
                    [1] => C8D21BD8-DA62-43C6-8E0D-0524D4F093B6
                    [2] => 6C7251E3-2151-4754-A413-51899FAAF6C2
                    ...
                )

            [survey] => Array
                (
                    [14] => B8B1F361-34A2-430E-9C0E-EB0C515B4E79
                    ...
                )

            [answer] => Array
                (
                    [2906] => 1A2F133E-0491-4117-AE2E-E3A823B66FBD...
                )

        ),
    [TYPE 2] => Array
        (
        ... // same structure as above
)

首先,我建议您坚持使用您的代码解决方案。 运行 一个简单的 SQL 查询,并处理结果,将它们整理成您想要的嵌套数组结构。你已经有了那个代码,如果你需要改变它,它更容易调试,也更容易改变。

我已经像您描述的那样实现了在 SQL 查询中创建嵌套结构的方法。这非常困难,而且 SQL 查询非常复杂,如果我们需要修改嵌套结构的内容,这将是将来的维护问题。

我在SQL查询中使用的解决方案是使用多级派生table子查询,并在每个级别使用JSON函数生成聚合JSON结果.这需要使用 MySQL 5.7 或更高版本,因为这些 JSON 功能在 MySQL.

的早期版本中未实现

演示测试数据:

create table if not exists mytable ( id int primary key, survey_type varchar(20), guid char(36), guid_type varchar(20), table_id  int, created_at datetime );
insert into mytable values 
(2 ,'TYPE 1','6C7251E3-2151-4754-A413-51899FAAF6C2','question','2','2022-03-20 16:14:09'), 
(3 ,'TYPE 1','EF5AFA93-C74D-4920-A13A-17A9B43239CD','question','3','2022-03-20 16:14:09'), 
(4 ,'TYPE 1','5C059148-94BE-4225-B5C2-551A81B65F16','question','4','2022-03-20 16:14:09'), 
(5 ,'TYPE 1','356B8A5C-1072-47A5-A508-D9BDCBA92CCC','answer','5','2022-03-20 16:14:09'), 
(6 ,'TYPE 1','E0CE4C26-7ABD-4162-9C8C-B4DD540AE268','answer','6','2022-03-20 16:14:09'), 
(7 ,'TYPE 1','BFBC50FC-892D-43E9-A235-D76E0D0BEF29','answer','7','2022-03-20 16:14:09'), 
(8 ,'TYPE 2','B9DCC5C1-CBFB-4589-98EF-4524F3958968','survey','8','2022-03-20 16:14:09'), 
(9 ,'TYPE 2','C98FBFF9-6FE3-414E-BB14-08EDC8281E66','survey','9','2022-03-20 16:14:09'), 
(10 ,'TYPE 2','8A780B6E-EAE0-47D6-9D05-F52B795AE617','question','10','2022-03-20 16:14:09'), 
(11 ,'TYPE 2','E3818D30-BB69-4F03-B56D-B31691F8007E','question','11','2022-03-20 16:14:09'), 
(12 ,'TYPE 2','24C81BEF-BFCE-4964-AB01-F3579251313D','answer','12','2022-03-20 16:14:09'), 
(13 ,'TYPE 3','59381701-AFBC-48F8-AECE-DB3702EE2B15','answer','13','2022-03-20 16:14:09'), 
(14 ,'TYPE 3','7F4AC694-74DC-4BEA-ACFB-D8F070769FEE','answer','14','2022-03-20 16:14:09'), 
(15 ,'TYPE 3','B5C405B9-BA7E-471A-87DD-B69D9757276F','survey','15','2022-03-20 16:14:09');

示例查询:

select json_pretty(json_objectagg(survey_type, g)) as j
from (
  select survey_type, json_objectagg(guid_type, t) as g
  from (
    select survey_type, guid_type, json_objectagg(table_id, guid) as t
    from mytable
    group by survey_type, guid_type
  ) as t
  group by survey_type
) as g;

输出:

{
  "TYPE 1": {
    "answer": {
      "5": "356B8A5C-1072-47A5-A508-D9BDCBA92CCC",
      "6": "E0CE4C26-7ABD-4162-9C8C-B4DD540AE268",
      "7": "BFBC50FC-892D-43E9-A235-D76E0D0BEF29"
    },
    "question": {
      "2": "6C7251E3-2151-4754-A413-51899FAAF6C2",
      "3": "EF5AFA93-C74D-4920-A13A-17A9B43239CD",
      "4": "5C059148-94BE-4225-B5C2-551A81B65F16"
    }
  },
  "TYPE 2": {
    "answer": {
      "12": "24C81BEF-BFCE-4964-AB01-F3579251313D"
    },
    "survey": {
      "8": "B9DCC5C1-CBFB-4589-98EF-4524F3958968",
      "9": "C98FBFF9-6FE3-414E-BB14-08EDC8281E66"
    },
    "question": {
      "10": "8A780B6E-EAE0-47D6-9D05-F52B795AE617",
      "11": "E3818D30-BB69-4F03-B56D-B31691F8007E"
    }
  },
  "TYPE 3": {
    "answer": {
      "13": "59381701-AFBC-48F8-AECE-DB3702EE2B15",
      "14": "7F4AC694-74DC-4BEA-ACFB-D8F070769FEE"
    },
    "survey": {
      "15": "B5C405B9-BA7E-471A-87DD-B69D9757276F"
    }
  }
}

将该结果提取到您的客户端应用程序中。这是一个长字符串,所以我希望您的数据不超过 MySQL 的 max_allowed_packet 长度。

使用 json_decode() 将字符串转换为所需的嵌套数组。