假设您的树结构如下:
a [Level 0]
/ | \
b c d [Level 1]
/ \ |
e f g [Level 2]
| / \
h i j [Level 3]
我已将其代表如下数据库:
node parent
------------
a null
b a
c a
d a
[...]
h f
i g
我想写一个函数,给定一个级别,它将返回该级别的所有节点及其父级。
例如:
f(0) => { a }
f(1) => { a, b, c, d }
f(2) => { a, b, c, d, e, f, g }
有什么想法吗?
答案 0 :(得分:3)
在这里,我遍历各个级别,将每个级别添加到表中,并使用它所在的级别。
create table mytable (
node varchar(80),
parent varchar(80),
constraint PK_mytable primary key nonclustered (node)
)
-- index for speed selecting on parent
create index IDX_mytable_parent on mytable (parent, node)
insert into mytable values ('a', null)
insert into mytable values ('b', 'a')
insert into mytable values ('c', 'a')
insert into mytable values ('d', 'a')
insert into mytable values ('e', 'b')
insert into mytable values ('f', 'b')
insert into mytable values ('g', 'd')
insert into mytable values ('h', 'f')
insert into mytable values ('i', 'g')
insert into mytable values ('j', 'g')
create function fn_level (@level int) returns @nodes table (Node varchar(80), TreeLevel int)
as begin
declare @current int
set @current = 0
while @current <= @level begin
if (@current = 0)
insert @nodes (Node, TreeLevel)
select node, @current
from mytable
where parent is null
else
insert @nodes (Node, TreeLevel)
select mt.node, @current
from @nodes n
inner join mytable mt on mt.parent = n.Node
where n.TreeLevel = (@current - 1)
set @current = @current + 1
end
return
end
select * from fn_level(2)
答案 1 :(得分:1)
除非你的SQL有一个特殊的非标准函数,否则通常的方法是构建一个包含这些列的路径表:
要填写此表,您可以使用递归或程序循环查找项目列表中每个项目的所有父项,祖父项,曾祖父项等。递归或循环需要继续,直到您停止找到返回新对的较长路径。
最后,你会有一个记录列表,告诉你像(a,b,1),(a,f,2),(a,h,3)等等。然后,获取一切在x及以上级别,你对所有子节点做一个不同的选择,路径长度为&lt; = x(与根联合,除非你在开始递归时包含(null,root,0)的记录/循环。
如果SQL更好地处理有向图(树)会很好,但不幸的是你必须用这样的额外表来欺骗它。
答案 2 :(得分:1)
MySQL的解决方案不太理想。
假设已知树的最大深度:
SELECT
nvl(e.node, nvl(d.node, nvl(c.node, nvl(b.node, a.node)))) item
, nvl2(e.node, 5, nvl2(d.node, 4, nvl2(c.node, 3, nvl2(b.node, 2, 1)))) depth
FROM table t AS a
LEFT JOIN table t AS b ON (a.node = b.parent)
LEFT JOIN table t AS c ON (b.node = c.parent)
LEFT JOIN table t AS d ON (c.node = d.parent)
LEFT JOIN table t AS e ON (d.node = e.parent)
WHERE a.parent IS NULL
这将为您提供每个节点及其深度。之后,选择深度小于X的每个项目都是微不足道的。
如果树的深度未知,或者是非常大,那么解决方案就像另一张海报所说的那样是迭代的。
答案 3 :(得分:1)
从Jason无耻地复制,我做了一个无函数的解决方案,我用postgresql测试(它有功能 - 也许它可以开箱即用)。
create table tree (
node char(1),
parent char(1)
);
insert into tree values ('a', null);
insert into tree values ('b', 'a');
insert into tree values ('c', 'a');
insert into tree values ('d', 'a');
insert into tree values ('e', 'b');
insert into tree values ('f', 'b');
insert into tree values ('g', 'd');
insert into tree values ('h', 'f');
insert into tree values ('i', 'g');
insert into tree values ('j', 'g');
ALTER TABLE tree ADD level int2;
--
-- node parent level
-- a - 1
-- b a a.(level + 1)
-- c a a.(level + 1)
-- e b b.(level + 1)
-- root is a:
UPDATE tree SET level = 0 WHERE node = 'a';
-- every level else is parent + 1:
UPDATE tree tout -- outer
SET level = (
SELECT ti.level + 1
FROM tree ti -- inner
WHERE tout.parent = ti.node
AND ti.level IS NOT NULL)
WHERE tout.level IS NULL;
update语句是纯sql,必须为每个级别重复,以填充表。
kram=# select * from tree;
node | parent | level
------+--------+-------
a | | 1
b | a | 2
c | a | 2
d | a | 2
e | b | 3
f | b | 3
g | d | 3
h | f | 4
i | g | 4
j | g | 4
(10 Zeilen)
我从'level = 1'开始,而不是'0',因此差异。
答案 4 :(得分:0)
SQL并不总能很好地处理这些递归问题。
某些DBMS平台允许您使用Common Table Expressions,它们是有效的自我调用查询,允许您通过数据结构进行递归。 MySQL中没有对此的支持,因此我建议您使用由另一种语言编写的脚本构建和管理的多个查询。
答案 5 :(得分:0)
我对数据库或它们的术语知之甚少,但是如果你为了找到N级的所有元素而自己执行了N次表的联合产品,它会起作用吗?
换句话说,执行一个查询,在该查询中搜索具有父A的所有条目。这将返回所有子项的列表。然后,重复查询以查找每个子项的子项。重复此过程,直到找到所有N级的孩子。
通过这种方式,您无需预先计算每个项目的深度。