SQL查询将实体的子项与父实体

时间:2017-10-26 19:03:42

标签: mysql sql

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

| 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

我的结果查询中的每一行都应代表其中一个树

4 个答案:

答案 0 :(得分:1)

这有效:http://sqlfiddle.com/#!9/e1127f/27/0

但我觉得它应该更容易,更容易。

我基本上有这个:

SELECT P.entity_id as Parent_id, P.name as Parent_Name, C1.entity_id, C1.Name, 
       C2.entity_id, C2.Name, C3.entity_id, C4.Name, C4.entity_id, C4.Name
FROM entity P
JOIN entity C1 on C1.parent_entity_id = P.entity_id
JOIN entity C2 on C2.parent_entity_id = P.entity_id
JOIN entity C3 on C3.parent_entity_id = P.entity_id
LEFT JOIN entity C4 on C4.parent_entity_id = P.entity_id
WHERE P.parent_entity_id IS NULL
AND C1.entity_id < C2.entity_id
AND C2.entity_id < C3.entity_id
AND C3.entity_id < C4.entity_id

但是当然最后的加入不会像在那里那样工作,因为WHERE子句将它变成INNER连接。也许有人会看到一个简单的方法来处理它一部分。

我最后放弃并使用UNION,一半用于有3个孩子的父母,另一半用于有4个孩子的父母。

编辑:感谢Paul让最后的加入工作!

SELECT P.entity_id as Parent_id, P.name as Parent_Name, C1.entity_id, c1.Name, 
       C2.entity_id, c2.Name, C3.entity_id, c3.Name, C4.entity_id, c4.Name
FROM entity P
JOIN entity C1 on C1.parent_entity_id = P.entity_id
JOIN entity C2 on C2.parent_entity_id = P.entity_id
JOIN entity C3 on C3.parent_entity_id = P.entity_id
LEFT JOIN entity C4 on C4.parent_entity_id = P.entity_id
                   and c3.entity_id < c4.entity_id
WHERE p.parent_entity_id IS NULL
AND C1.entity_id < C2.entity_id
AND C2.entity_id < C3.entity_id
AND (3 = (SELECT COUNT(1)
          FROM entity c
          WHERE c.parent_entity_id = p.entity_id)
     OR c4.entity_id is not null)

答案 1 :(得分:1)

以下是两个孩子的查询:

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

对于child_entity_3,您将使用offset 2,对于child_entity_4,您将使用offset 3

但我宁愿使用以下两个查询

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;

并使用几个简单的循环在应用程序语言中创建所需的表。

答案 2 :(得分:0)

这有点乱,而且可能有更好的存储方式,特别是因为这只是手动扩展。

假设一张桌子:

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

<强> EDITED

使用一些示例数据:

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并使用PARTITION BYROW_NUMBER,上述操作会更容易,但这是一种解决方法。

给我们: enter image description here

然后,您可以加入该表/视图3次,为子号添加第二个JOIN条件。这是在这里演示的,由于SQL Fiddle对数据修改的限制使用派生表,我认为这可以完成3次,尽管你必须研究效率和基准测试。

http://sqlfiddle.com/#!9/5ddef3/3

最终,它给了我们:

enter image description here

答案 3 :(得分:0)

SET @cNum := 0;
SET @prevParent := 0;

SELECT p.id, p.Name
   , GROUP_CONCAT(IF(numberedChildren.childNum = 1, c.Name, NULL)) AS child_entity_1_name 
   , GROUP_CONCAT(IF(numberedChildren.childNum = 1, c.Status, NULL)) AS child_entity_1_status 
   , GROUP_CONCAT(IF(numberedChildren.childNum = 2, c.Name, NULL)) AS child_entity_2_name 
   , GROUP_CONCAT(IF(numberedChildren.childNum = 2, c.Status, NULL)) AS child_entity_2_status 
   , ...
FROM (
   SELECT @cNum := IF(@prevParent <> orderedChildren.parent_id, @cNum + 1, 1) AS childNum
      , orderedChildren.id AS child_id
      , @prevParent := orderedChildren.parent_id AS parent_id
   FROM (
      SELECT parent_id, id
      FROM sometable
      ORDER BY parent_id, id
   ) AS orderedChildren
) AS numberedChildren
INNER JOIN sometable AS p ON numberedChildren.parent_id = p.id
INNER JOIN sometable AS c ON numberedChildren.child_id = c.id
GROUP BY p.id, p.Name
;

认为这个脚本可能有效。它依赖于GROUP_CONCAT,几乎任何其他聚合函数,忽略空值。

您可以通过更改以下行来使其成为单个查询(删除初始SET语句):

) AS orderedChildren

) AS orderedChildren, (SELECT @cNum AS cnInit, @prevParent AS ppInit) As init

但这不是我常用的会话变量init样式。

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