我有一个包含以下字段的表:
| 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
我的结果查询中的每一行都应代表其中一个树
答案 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 BY
和ROW_NUMBER
,上述操作会更容易,但这是一种解决方法。
然后,您可以加入该表/视图3次,为子号添加第二个JOIN
条件。这是在这里演示的,由于SQL Fiddle对数据修改的限制使用派生表,我认为这可以完成3次,尽管你必须研究效率和基准测试。
http://sqlfiddle.com/#!9/5ddef3/3
最终,它给了我们:
答案 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计算)但是这样使用会话变量可能是......很精细。