我正在尝试使用此引用(http://mikehillyer.com/articles/managing-hierarchical-data-in-mysql/)在MYSQL中显示树结构,我发现它只适用于同一个表中的1个父项。
如果你有另一个表加入,没有什么进展顺利。
例如:
表格菜单:
+----+--------+-------+
| id | name | order |
| 1 | Father | 0 |
| 2 | Father | 1 |
| 3 | Son | 0 |
| 4 | Child | 1 |
| 5 | Granson| 2 |
+----+--------+-------+
表关系
+----+---------+-----------+
| id | menu_id | parent_id |
| 1 | 1 | NULL |
| 2 | 2 | NULL |
| 3 | 3 | 1 |
| 4 | 4 | 3 |
| 5 | 5 | 4 |
+----+---------+-----------+
执行SELECT
SELECT child_menu.*, menu.* FROM menu, relations AS child_menu
WHERE menu.id = child_menu.menu_id
GROUP BY menu_id
我有这个:
+----+--------+-------+
| id | name | order |
| 1 | Father | 0 |
| 2 | Father | 1 |
| 3 | Son | 0 |
| 4 | Child | 1 |
| 5 | Granson| 2 |
+----+--------+-------+
我正试图让他们的孩子在同一个SELECT中保持秩序 从我在示例中看到的情况来看,只有在父级位于同一个表中时才有效 有人能帮我吗? 谢谢
已编辑:预期输出:
+----+--------+-------+
| id | name | order |
| 1 | Father | 0 |
| 3 | Son | 0 |
| 4 | Child | 1 |
| 5 | Granson| 2 |
| 2 | Father | 1 |
+----+--------+-------+
I.E. Father
Son
Child
Grandson
答案 0 :(得分:4)
除了Mike Hillyer博客中显示的方法之外,还有其他方法可以组织分层数据。我喜欢使用我称之为传递闭包表或闭包表的方法。在此设计中,您将通过层次结构存储每个路径,作为祖先/后代对。
create table closure (
ancestor int,
descendant int,
length int,
primary key (ancestor,descendant),
key (descendant,ancestor)
);
insert into closure values
(1,1,0),
(1,3,1),
(1,4,2),
(1,5,3),
(2,2,0),
(3,3,0),
(3,4,1),
(3,5,2),
(4,4,0),
(4,5,1),
(5,5,0);
请注意,此集合甚至包括长度为零的“路径”,即菜单项本身就是“父”。
现在,您可以通过加入m
为descandant的路径,将每个菜单项a
加入其每个祖先集m
。从那里返回到祖先集合中的菜单项o
,您可以访问order
。
使用GROUP_CONCAT()从祖先链中的每个order
创建一个“breadcrumbs”字符串,这将成为一个字符串,您可以对其进行排序以获得所需的菜单顺序。
SELECT m.*, GROUP_CONCAT(o.`order` ORDER BY a.length DESC) AS breadcrumbs
FROM menu AS m
INNER JOIN closure AS a ON a.descendant = m.id
INNER JOIN menu AS o ON a.ancestor = o.id
GROUP BY m.id
ORDER BY breadcrumbs;
+----+----------+-------+-------------+
| id | name | order | breadcrumbs |
+----+----------+-------+-------------+
| 1 | Father1 | 0 | 0 |
| 3 | Son | 0 | 0,0 |
| 4 | Child | 1 | 0,0,1 |
| 5 | Grandson | 2 | 0,0,1,2 |
| 2 | Father2 | 1 | 1 |
+----+----------+-------+-------------+
请注意,面包屑排序为字符串,因此如果您有一些带有2或3位数的order
个数字,则会得到不规则的结果。确保您的order
号码都具有相同的位数。
作为替代方案,您只需将面包屑字符串存储在原始菜单表中即可:
ALTER TABLE menu ADD COLUMN breadcrumbs VARCHAR(255);
UPDATE menu SET breadcrumbs = '0,0,1,2' WHERE id = 5;
etc.
然后你可以做一个更简单的查询:
SELECT * FROM menu ORDER BY breadcrumbs;
但是,如果您更改了菜单项的顺序,那么您可以手动重新计算所有受影响的面包屑字符串。
答案 1 :(得分:3)
如果您可以创建MySQL用户定义函数,则可以使用以下内容在执行时动态创建族树:
DELIMITER //
CREATE FUNCTION fnFamilyTree ( id INT ) RETURNS TEXT
BEGIN
SET @tree = '';
SET @qid = id;
WHILE (@qid > 0) DO
SELECT IFNULL(r.parent_id,-1),m.ordr INTO @pid,@ordr FROM Relations r JOIN Menu m ON m.id = r.id WHERE r.id = @qid LIMIT 1;
SET @tree = CONCAT(@ordr,' ',@tree);
SET @qid = @pid;
END WHILE;
RETURN RTRIM(@tree);
END//
DELIMITER ;
然后,下面的SQL应该为您提供所需的序列:
SELECT m.id
,m.name
,r.parent_id
,fnFamilyTree( r.id )
FROM Relations r
JOIN Menu m
ON m.id = r.menu_id
ORDER BY fnFamilyTree( r.id )
;
在http://sqlfiddle.com/#!2/199c25/1处尝试。结果是:
ID NAME PARENT_ID FNFAMILYTREE( R.ID )
1 Father (null) 0
3 Son 1 0 0
4 Child 3 0 0 1
5 Granson 4 0 0 1 2
2 Father (null) 1
至少,我认为这就是你所追求的目标。
实际架构更新
用户定义的功能:
DELIMITER //
CREATE FUNCTION fnFamilyTree ( id INT ) RETURNS TEXT
BEGIN
SET @tree = '';
SET @qid = id;
WHILE (@qid > 0) DO
SELECT IFNULL(r.`menu_master_id`,-1),m.`order` INTO @pid,@order FROM `menu_has_menu_master` r JOIN `menu` m ON m.`id` = r.`menu_id` WHERE r.`menu_id` = @qid LIMIT 1;
SET @tree = CONCAT(LPAD(@order,5,'0'),' ',@tree);
SET @qid = @pid;
END WHILE;
RETURN RTRIM(@tree);
END
//
DELIMITER ;
查询:
SELECT m.id
,m.name
,r.menu_master_id
,fnFamilyTree( r.menu_id )
FROM menu_has_menu_master r
JOIN menu m
ON m.id = r.menu_id
ORDER BY fnFamilyTree( r.menu_id )
;
http://sqlfiddle.com/#!2/cb0384
的结果ID NAME MENU_MASTER_ID FNFAMILYTREE( R.MENU_ID )
8 Dashboard (null) 00001
6 Seções do Site (null) 00002
7 Home 6 00002 00003
14 Catalogos 6 00002 00004
15 Marcas 14 00002 00004 00004
16 Categoria 1 14 00002 00004 00006
9 Arquivos (null) 00007
1 Administração (null) 00127
3 Usuarios 1 00127 00001
5 Secões do iPocket 1 00127 00001
13 Default Setups 1 00127 00002
4 Logs 1 00127 00003
答案 2 :(得分:2)
你需要以某种方式自我加入来展示这种关系。之后...关系作为child_menu,您需要在r2.parent_id = child_menu.id上添加左连接关系为r2 ...