Mysql SQL查询以将实体的子实体与父实体放在同一行中

Mysql SQL查询以将实体的子实体与父实体放在同一行中,mysql,sql,Mysql,Sql,我有一个包含以下字段的表: | entity_id | parent_entity_id | name | status | |----------------------------------------------| 我正在尝试编写一个查询,其中显示每个没有父实体的实体,并内联显示其子实体的名称和状态,结果如下: | entity_id | name | child_entity_1_name | child_entity_1_status |...| child_entity_4_na

我有一个包含以下字段的表:

| entity_id | parent_entity_id | name | status |
|----------------------------------------------|
我正在尝试编写一个查询,其中显示每个没有父实体的实体,并内联显示其子实体的名称和状态,结果如下:

| entity_id | name | child_entity_1_name | child_entity_1_status |...| child_entity_4_name | child_entity_4_status |
--------------------------------------------------------------------------------------------------------------------
我知道数据是结构化的,因此每个实体至少有3个子实体,但不是每个实体都有4个子实体(因此,有3个子实体的第4个子实体名称和状态列中有NULL)。此外,我知道没有一个拥有父实体的实体本身就是父实体

从我所学的介绍性数据库类来看,这似乎是一个复杂的查询。让我大吃一惊的是把所有的子实体放在同一行。我可以在与其父实体相同的行中获取一个子实体,但不能获取多个子实体

编辑:数据库基本上是一组树,每个树的高度为2。没有祖父母

PARENT_ENT_1                       PARENT_ENT_2
|  |   |   |                       |  |   |  |
|  |   |   |                       |  |   |  |
C1 C2  C3  C4                      C5 C6  C7 C8

我的结果查询中的每一行都应该表示这些树中的一个

这有点凌乱,可能有更好的存储方法,特别是因为它只能手动伸缩

假设一张桌子:

CREATE TABLE
parents
(
    entity_id INT PRIMARY KEY AUTO_INCREMENT,
    parent_entity_id INT,
    name VARCHAR(15),
    `status` VARCHAR(15)
);
已编辑

使用一些示例数据:

INSERT INTO
 `parents`
(entity_id, parent_entity_id, name, `status`)
VALUES
(1, NULL, 'Parent1', 'sfsd'),
(2, 1, 'Child1A', 'sfsd'),
(3, 1, 'Child1B', 'sfsd'),
(4, 1, 'Child1C', 'sfsd'),
(5, NULL, 'Parent2', 'sfsd'),
(6, 5, 'Child2A', 'sfsd'),
(7, 5, 'Child2B', 'sfsd');
您可以创建存储以下内容的视图、临时表或永久表(取决于最终目标):

SET @row_number = 0;
SET @parent_id = 0;

SELECT
  @row_number:=CASE
        WHEN @parent_id = parent_entity_id THEN @row_number + 1
        ELSE 1
  END AS `child_num`,
  entity_id,
  @parent_id:= parent_entity_id as parent_entity_id,
  name,
  `status`
FROM
  `parents`
WHERE
  `parent_entity_id` IS NOT NULL
ORDER BY
  parent_entity_id ASC,
  entity_id ASC;
使用SQL Server并使用
按分区
行编号
可以更轻松地完成上述操作,但这是一种解决方法

给了我们:

然后,您可以连接该表/视图3次,为子编号添加第二个
join
条件。这是在这里演示的,由于SQL中的限制,它使用了一个派生表来修改数据,我认为这可以全部完成3次,不过您必须研究效率和基准测试

最终,它给了我们:


这有点混乱,可能有更好的存储方法,特别是因为它只能手动扩展

假设一张桌子:

CREATE TABLE
parents
(
    entity_id INT PRIMARY KEY AUTO_INCREMENT,
    parent_entity_id INT,
    name VARCHAR(15),
    `status` VARCHAR(15)
);
已编辑

使用一些示例数据:

INSERT INTO
 `parents`
(entity_id, parent_entity_id, name, `status`)
VALUES
(1, NULL, 'Parent1', 'sfsd'),
(2, 1, 'Child1A', 'sfsd'),
(3, 1, 'Child1B', 'sfsd'),
(4, 1, 'Child1C', 'sfsd'),
(5, NULL, 'Parent2', 'sfsd'),
(6, 5, 'Child2A', 'sfsd'),
(7, 5, 'Child2B', 'sfsd');
您可以创建存储以下内容的视图、临时表或永久表(取决于最终目标):

SET @row_number = 0;
SET @parent_id = 0;

SELECT
  @row_number:=CASE
        WHEN @parent_id = parent_entity_id THEN @row_number + 1
        ELSE 1
  END AS `child_num`,
  entity_id,
  @parent_id:= parent_entity_id as parent_entity_id,
  name,
  `status`
FROM
  `parents`
WHERE
  `parent_entity_id` IS NOT NULL
ORDER BY
  parent_entity_id ASC,
  entity_id ASC;
使用SQL Server并使用
按分区
行编号
可以更轻松地完成上述操作,但这是一种解决方法

给了我们:

然后,您可以连接该表/视图3次,为子编号添加第二个
join
条件。这是在这里演示的,由于SQL中的限制,它使用了一个派生表来修改数据,我认为这可以全部完成3次,不过您必须研究效率和基准测试

最终,它给了我们:

SET@cNum:=0;
设置@prevParent:=0;
选择p.id、p.Name
,GROUP_CONCAT(如果(numberedChildren.childNum=1,c.Name,NULL))作为子实体_1_名称
,组CONCAT(如果(numberedChildren.childNum=1,c.Status,NULL))作为子实体
,GROUP_CONCAT(如果(numberedChildren.childNum=2,c.Name,NULL))作为子实体_2_名称
,将组_CONCAT(IF(numberedChildren.childNum=2,c.Status,NULL))作为子实体_2_Status
, ...
从(
选择@cNum:=IF(@prevParent orderedChildren.parent_id,@cNum+1,1)作为childNum
,orderedChildren.id作为子项id
,@prevParent:=orderedChildren.parent\u id作为parent\u id
从(
选择父项\u id,id
从某处
按父项下单\u id,id
)孩子们
)作为无数的孩子
在numberedChildren.parent\u id=p.id上以p的形式内部连接sometable
在numberedChildren.child\u id=c.id上以c的形式内部连接sometable
按p.id、p.Name分组
;
我认为这个脚本可能有用。它依赖于
GROUP\u CONCAT
,以及几乎所有其他聚合函数,忽略空值

您也可以通过更改此行将其设置为单个查询(删除初始集合语句):

)作为orderedChildren

)作为orderedChildren,(选择@cNum作为cnInit,@prevParent作为ppInit)作为init

但对于会话变量init,这不是我通常的风格

编辑:此外,有序子查询可能不需要是子查询(您可以在同一子查询中进行ORDER BY和childNum计算),但这样使用会话变量可能会非常微妙。

SET@cNum:=0;
设置@prevParent:=0;
选择p.id、p.Name
,GROUP_CONCAT(如果(numberedChildren.childNum=1,c.Name,NULL))作为子实体_1_名称
,组CONCAT(如果(numberedChildren.childNum=1,c.Status,NULL))作为子实体
,GROUP_CONCAT(如果(numberedChildren.childNum=2,c.Name,NULL))作为子实体_2_名称
,将组_CONCAT(IF(numberedChildren.childNum=2,c.Status,NULL))作为子实体_2_Status
, ...
从(
选择@cNum:=IF(@prevParent orderedChildren.parent_id,@cNum+1,1)作为childNum
,orderedChildren.id作为子项id
,@prevParent:=orderedChildren.parent\u id作为parent\u id
从(
选择父项\u id,id
从某处
按父项下单\u id,id
)孩子们
)作为无数的孩子
在numberedChildren.parent\u id=p.id上以p的形式内部连接sometable
在numberedChildren.child\u id=c.id上以c的形式内部连接sometable
按p.id、p.Name分组
;
我认为这个脚本可能有用。它依赖于
GROUP\u CONCAT
,以及几乎所有其他聚合函数,忽略空值

您也可以通过更改此行将其设置为单个查询(删除初始集合语句):

)作为orderedChildren

)作为orderedChildren,(选择@cNum作为cnInit,@prevParent作为ppInit)作为init

但对于会话变量init,这不是我通常的风格

编辑:此外,有序子查询可能不需要是子查询(您可以在同一子查询中执行ORDER BY和childNum计算),但这样使用会话变量可能会非常微妙。

select
    p.entity_id, p.name,
    c1.name   as child_entity_1_name,
    c1.status as child_entity_1_status,
    c2.name   as child_entity_2_name,
    c2.status as child_entity_2_status
from entities p
left join entities c1 on c1.entity_id = (
    select c.entity_id
    from entities c
    where c.parent_entity_id = p.entity_id
    order by c.entity_id asc
    limit 1
    offset 0
)
left join entities c2 on c2.entity_id = (
    select c.entity_id
    from entities c
    where c.parent_entity_id = p.entity_id
    order by c.entity_id asc
    limit 1
    offset 1
)
where p.parent_entity_id is null
select p.entity_id, p.name
from entities p
where p.parent_entity_id is null;

select p.entity_id as parent_id, c.name, c.status
from entities p
join entities c on c.parent_entity_id = p.entity_id
where p.parent_entity_id is null
order by p.entity_id, c.entity_id;