我将如何加入 MySQL 中的两个表(多对一)并获得特定的 JSON 输出(如下所示)

How would I join two tables in MySQL (many to one) and get a specific JSON output (as shown below)

如何在 MySQL 中连接两个 table(多对一)并获得特定的 JSON 输出(如下所示)

注意:请勿将此标记为重复

之前有一个问题 posted 与此非常相似,但有几点不相同。

不是我要找的,这个问题和这个不一样:

我在这篇 post 的底部放了一个更长的描述和更多的细节,为什么这应该 被标记为重复,以及它有何不同。


要求:

  1. return JSON
  2. 使用:JSON_OBJECT & JSON_ARRAYAGG(并且不使用 CONCAT 或 GROUP_CONCAT)
  3. 仅连接两个 table(通过 primary/foreign 键 | 行标识符)|多对一关系
  4. 在输出中显示来自 parent table 的字段(不仅仅是 id 外键引用
  5. 在输出JSON中,我需要控制每个元素或结构的名称,对应一个table名称,但JSON风格化会有所不同,而且控制每个字段的名称(JSON 程式化名称将不同于数据库字段名称)

输出JSON将是一个数组(teams),数组(struct)的每个元素将包含一个数组(persons)。

顶级数组属于 parent table 'teams'(JSON 数组的每个元素包含单行数据),嵌套数组属于child table 'persons' 与 parent.

存在多对一关系

如果一个简单的 select 仅由一个 table 完成:select * from <child_table>;

我得到了 parent table 的 id(主键),但是在 JSON 结果中,连同 parent 主键,我也想要显示 parent 中的另一个字段(parent 中的字段值,而不仅仅是行 ID/键,这对 JSON 输出没有帮助)。


此处提供:.sql 文件以创建数据库、模式、插入数据(以测试解决方案)

create schema if not exists test;

use test;
create table if not exists team
(
    id          int unsigned auto_increment primary key,
    team_name   varchar(30) unique not null,
    description text
);

use test;
create table if not exists person
(
    id          int unsigned auto_increment primary key,
    id_team     int unsigned not null,
    person_name varchar(40)  not null,
    notes       varchar(40)  not null,
    constraint team_person unique (id, id_team),
    foreign key (id_team) references team (id)
);

-- insert teams (2)
use test;
insert into team(team_name)
values ('team1');

insert into team(team_name)
values ('team2');

insert into team(team_name)
values ('team3');

-- insert persons (x2 per team = 4)
use test;
insert into person(id_team, person_name)
values ((select id from team where team_name = 'team1'),
        'john');

insert into person(id_team, person_name)
values ((select id from team where team_name = 'team1'),
        'tom');

insert into person(id_team, person_name)
values ((select id from team where team_name = 'team1'),
        'marie');

-- team 2
insert into person(id_team, person_name)
values ((select id from team where team_name = 'team2'),
        'scott');

insert into person(id_team, person_name)
values ((select id from team where team_name = 'team2'),
        'mark');

我需要输出 JSON 看起来像这样(完全展开 - 两个数组的值都来自 tables):

您在这里看到的是一组团队,'team' table.

中每行一个 JSON 元素

在每个元素中,一组人,每行一个 JSON 元素来自 'person' table 表示多对一关系。

{
  "team_persons": [
    {
      "team": "team1",
      "id_team": 1,
      "persons": [
        {
          "id_person": 1,
          "personaName": "john"
        },
        {
          "id_person": 2,
          "personaName": "allison"
        }
      ]
    },
    {
      "team": "team2",
      "id_team": 2,
      "persons": [
        {
          "id_person": 3,
          "personaName": "katrina"
        },
        {
          "id_person": 4,
          "personaName": "scott"
        }
      ]
    }
  ]
}

如您在输出中所见,parent table 字段:id_team & team_name 都显示在 JSON 输出的每条记录中。

JSON中有两个数组:

  1. 包含列表或集合的顶级(结构)数组 团队 (parent table)
  2. 在每个团队中,都有一个列表或集合 团队成员

我知道 SELECT JSON_OBJECT / JSON_ARRAYAGG 会涉及。我不确定如何在两个 table 的 SQL 连接中同时使用两者来实现所需的自定义 JSON 输出。

SQL 中两个 table 的连接看起来像这样:

select t.id id_team, p.id id_person, p.person_name, t.team_name
from team t,
     person p
where t.id = p.id_team;

谢谢!


请不要将此标记为重复,它不是:

我只处理两个 table,其中 JSON 输出需要从一个非常顶级的数组开始(与其他 post 不同)。

{
  "team_persons": [

引用的 post 和方法存在问题:

  1. 引用的 post 中的示例没有提供有关 table 和关系的足够详细信息,而且它是一个实际问题,没有分解为最简单的基本要素
  2. 初始方法尝试使用 CONCAT / GRUOP_CONCAT,我想避免使用

有一种更好、更现代的方法可以使用其他 built-in MySQL 函数。

请不要关闭这个问题。要求差别很大

我需要一种使用方法:JSON_OBJECT / JSON_ARRAYAGGNOT CONCAT不是GROUP_CONCAT。还有两个非常简单的 tables。

问问题时,如果尝试解决的最简单(不准确)的事情可以帮助其他人将他们的问题映射到问题上。在下面的问题中,我去除了所有复杂性以提供一个简单的示例。

我希望提供一个更容易理解的问题,其他人可能会从中受益(使用代码复制 table 结构),以及一个更精确的答案,因为我相信那里给出的答案可以为更简单的用例变得更简单,更容易理解。

SELECT 
  JSON_PRETTY(
    JSON_OBJECT(
      'team_persons', JSON_ARRAYAGG(
        JSON_OBJECT(
          'team', team,
          'id_team', id_team,
          'persons', persons
        )
      )
    )
  ) AS _result
FROM (
  SELECT 
    t.id AS id_team,
    t.team_name AS team,
    JSON_ARRAYAGG(
      JSON_OBJECT(
        'id_person', p.id, 
        'personName', p.person_name
      )
    ) AS persons
  FROM team t JOIN person p ON t.id = p.id_team
  GROUP BY id_team
) AS p;

已在 MySQL 8.0.28 上测试,但它也应该适用于 MySQL 5.7.22(或更高版本)。


要包括空队是很棘手的。一开始只能使用 LEFT OUTER JOIN,但随后它会抱怨,除非我将 GROUP BY 更改为引用基列,而不是别名:

  ...
  FROM team t LEFT OUTER JOIN person p ON t.id = p.id_team
  GROUP BY t.id

但是,这可能不会产生您想要的结果,我假设它是一个空的 JSON 数组。因为外部连接的结果确实有一行,但是有 NULL,你会得到一个带有 NULL 的假团队成员:

{
  "team": "team3",
  "id_team": 3,
  "persons": [
    {
      "id_person": null,
      "personName": null
    }
  ]
}

一个解决方法是将当前查询与 JOIN 与另一个 exclusion-join 查询联合:

SELECT
  JSON_PRETTY(
    JSON_OBJECT(
      'team_persons', JSON_ARRAYAGG(
        JSON_OBJECT(
          'team', team,
          'id_team', id_team,
          'persons', persons
        )
      )
    )
  ) AS _result
FROM (
  SELECT 
    t.id AS id_team,
    t.team_name AS team,
    JSON_ARRAYAGG(
      JSON_OBJECT(
        'id_person', p.id,
        'personName', p.person_name
      )
    ) AS persons
  FROM team t JOIN person p ON t.id = p.id_team
  GROUP BY t.id
  UNION
  SELECT 
    t.id,
    t.team_name,
    JSON_ARRAY()
  FROM team t LEFT OUTER JOIN person p ON t.id = p.id_team
  WHERE p.id_team IS NULL
) AS p;

UNION的后半部分returns字面上的空数组,因为反正队伍里没有人。