分层表-如何获取项的路径[MySQL中的链表]
我在MySQL中有一个分层表:每个项的父字段指向其父项的id字段。对于每个项目,我可以使用。使用GROUP_CONCAT,我将完整路径作为单个字符串:分层表-如何获取项的路径[MySQL中的链表],mysql,sql,linked-list,hierarchical-data,Mysql,Sql,Linked List,Hierarchical Data,我在MySQL中有一个分层表:每个项的父字段指向其父项的id字段。对于每个项目,我可以使用。使用GROUP_CONCAT,我将完整路径作为单个字符串: SELECT GROUP_CONCAT(_id SEPARATOR ' > ') FROM ( SELECT @r AS _id, ( SELECT @r := parent FROM t_hierarchy WHERE id = _id
SELECT GROUP_CONCAT(_id SEPARATOR ' > ') FROM (
SELECT @r AS _id,
(
SELECT @r := parent
FROM t_hierarchy
WHERE id = _id
) AS parent,
@l := @l + 1 AS lvl
FROM (
SELECT @r := 200,
@l := 0
) vars,
t_hierarchy h
WHERE @r <> 0
ORDER BY lvl DESC
) x
另外,我知道我可以将路径存储在单独的字段中,并在插入/更新时进行更新,但我需要基于链表技术的解决方案
更新:我希望看到一个解决方案,它不会像for和while那样使用递归或构造。上述查找路径的方法不使用任何循环或函数。我想用同样的逻辑找到一个解决方案。或者,如果不可能,请尝试解释原因 定义getPath函数并运行以下查询:
select id, parent, dbo.getPath(id) as path from t_hierarchy
定义getPath函数:
create function dbo.getPath( @id int)
returns varchar(400)
as
begin
declare @path varchar(400)
declare @term int
declare @parent varchar(100)
set @path = ''
set @term = 0
while ( @term <> 1 )
begin
select @parent = parent from t_hierarchy where id = @id
if ( @parent is null or @parent = '' or @parent = @id )
set @term = 1
else
set @path = @path + @parent
set @id = @parent
end
return @path
end
考虑以下两个查询之间的差异:
SELECT @id := id as id, parent, (
SELECT concat(id, ': ', @id)
) as path
FROM t_hierarchy;
SELECT @id := id as id, parent, (
SELECT concat(id, ': ', _id)
FROM (SELECT @id as _id) as x
) as path
FROM t_hierarchy;
它们看起来几乎相同,但结果却截然不同。在我的MySQL版本中,第二个查询中的_id对于结果集中的每一行都是相同的,并且等于最后一行的id。然而,最后一位是正确的,因为我按照给定的顺序执行了两个查询;例如,在SET@id:=1之后,我可以看到_id始终等于SET语句中的值
这是怎么回事?解释产生了一个线索:
mysql> explain SELECT @id := id as id, parent, (
-> SELECT concat(id, ': ', _id)
-> FROM (SELECT @id as _id) as x
-> ) as path
-> FROM t_hierarchy;
+----+--------------------+-------------+--------+---------------+------------------+---------+------+------+----------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+--------------------+-------------+--------+---------------+------------------+---------+------+------+----------------+
| 1 | PRIMARY | t_hierarchy | index | NULL | hierarchy_parent | 9 | NULL | 1398 | Using index |
| 2 | DEPENDENT SUBQUERY | <derived3> | system | NULL | NULL | NULL | NULL | 1 | |
| 3 | DERIVED | NULL | NULL | NULL | NULL | NULL | NULL | NULL | No tables used |
+----+--------------------+-------------+--------+---------------+------------------+---------+------+------+----------------+
3 rows in set (0.00 sec)
此查询使用第二个t_层次结构,与原始查询相同,以确保结果中有足够的行供父子查询循环。它还为每个_id添加一行,该id将自身作为父项;否则,任何在父字段中为NULL的根对象都将无法显示在结果中
奇怪的是,通过GROUP_CONCAT运行结果似乎扰乱了排序。幸运的是,该函数有自己的ORDER BY子句:
公平警告:这些查询隐式地依赖于@c更新之前发生的@r和@l更新。MySQL不保证这种顺序,并且可能会随着服务器的任何版本而改变。我得到一个错误:1064-您的SQL语法有一个错误;检查与您的MySQL服务器版本对应的手册,了解在第1行“@id int将varchar400返回为begin declare@path varchar400 declare@term”附近使用的正确语法,以及没有循环的解决方案如何?!答案是SQLServer语法;我可以帮助您将其转换为MySQL语法。因为它有层次数据,并且您不想在更新/插入/删除中添加任何列,即:需要更新的深度/路径;我看不出有一个没有回路的解。如果MySQL有CTE,您可以在没有用户定义函数和循环的情况下解决相同的问题。感谢您回答这个问题!这个答案为我澄清了很多事情。虽然最终的查询为我返回了空结果[MySQL 5.1.40],但它有很多重要的想法,所以我要奖励它。我会再读几遍,试着理解为什么最后一个查询在我的数据库上不起作用,也许会要求澄清一些事情。再次感谢!奇怪的是它在5.1.40中不起作用;它在Ubuntu11.10Oneiric上进行了5.1.63的测试。你可以试着移动@c线;删除或注释掉HAVING行也可能有助于调试。根据您给出的引用,您的代码未定义。您没有理由认为它会工作,也没有理由声称它在内部选择了任何内容,只是您运行了它并给出了正确的结果。在同一select语句中读取和分配同一变量在MySQL中是未定义的行为,请参阅文档re assignment&variables。在8.0&CTEs之前,在指定的语义下没有非循环解决方案。
mysql> explain SELECT @id := id as id, parent, (
-> SELECT concat(id, ': ', _id)
-> FROM (SELECT @id as _id) as x
-> ) as path
-> FROM t_hierarchy;
+----+--------------------+-------------+--------+---------------+------------------+---------+------+------+----------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+--------------------+-------------+--------+---------------+------------------+---------+------+------+----------------+
| 1 | PRIMARY | t_hierarchy | index | NULL | hierarchy_parent | 9 | NULL | 1398 | Using index |
| 2 | DEPENDENT SUBQUERY | <derived3> | system | NULL | NULL | NULL | NULL | 1 | |
| 3 | DERIVED | NULL | NULL | NULL | NULL | NULL | NULL | NULL | No tables used |
+----+--------------------+-------------+--------+---------------+------------------+---------+------+------+----------------+
3 rows in set (0.00 sec)
SELECT @r := if(
@c = th1.id,
if(
@r is null,
null,
(
SELECT parent
FROM t_hierarchy
WHERE id = @r
)
),
th1.id
) AS parent,
@l := if(@c = th1.id, @l + 1, 0) AS lvl,
@c := th1.id as _id
FROM (
SELECT @c := 0,
@r := 0,
@l := 0
) vars
left join t_hierarchy as th1 on 1
left join t_hierarchy as th2 on 1
HAVING parent is not null
SELECT _id,
GROUP_CONCAT(parent ORDER BY lvl desc SEPARATOR ' > ') as path,
max(lvl) as depth
FROM (
SELECT @r := if(
@c = th1.id,
if(
@r is null,
null,
(
SELECT parent
FROM t_hierarchy
WHERE id = @r
)
),
th1.id
) AS parent,
@l := if(@c = th1.id, @l + 1, 0) AS lvl,
@c := th1.id as _id
FROM (
SELECT @c := 0,
@r := 0,
@l := 0
) vars
left join t_hierarchy as th1 on 1
left join t_hierarchy as th2 on 1
HAVING parent is not null
ORDER BY th1.id
) as x
GROUP BY _id;