父子排序的MySQL树

标签 mysql sql select hierarchical-data

我正在尝试使用此引用 ( http://mikehillyer.com/articles/managing-hierarchical-data-in-mysql/ ) 在 MYSQL 中显示树结构,但我发现它仅适用于同一表中的 1 个父级。
如果您有另一张 table 要加入,一切都不会顺利。 示例:

表格菜单:

+----+--------+-------+
| 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 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 中按顺序离开他们的 child 。
从我在示例中看到的情况来看,只有当父级在同一个表中时才有效。
有人能帮我吗? 谢谢

已编辑: 预期输出:

+----+--------+-------+
| id |  name  | order |
| 1  | Father |   0   |
| 3  | Son    |   0   |
| 4  | Child  |   1   |
| 5  | Granson|   2   |
| 2  | Father |   1   |
+----+--------+-------+

I.E. Father
       Son
         Child
            Grandson

最佳答案

除了 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 连接到它的每个祖先集合 a,方法是连接到路径,其中 m 是 descandant。从那里,返回到祖先集合中的菜单项 o,您可以访问 order

使用 GROUP_CONCAT() 根据祖先链中每个的 order 生成一个“面包屑”字符串,这将成为一个字符串,您可以根据该字符串进行排序以获得所需的菜单顺序。

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 数字,您将得到不规则的结果。确保您的订单号码都具有相同的位数。


作为替代方案,您可以简单地将面包屑字符串存储在原始菜单表中:

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;

但是如果您更改了菜单项的顺序,则由您手动重新计算所有受影响的面包屑字符串。

关于父子排序的MySQL树,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/18984074/

相关文章:

php - MYSQL n个元素矩阵

mysql - 如何将带有 LOCK TABLE 的 SQL 查询转换为带有 SELECT...FOR UPDATE 的存储过程

尝试创建表时出现 MySQL 错误,

php - 使用 PHP 将 DATE TIMESTAMP 值插入 MySQL

来自连接表的 SQL 计数

mysql - 查询一张表中的项目

PHP MySQL PDO 使用 id 选择数组上的行

mysql - 选择选项数组作为 select 语句的一部分

firefox - 为什么 Firefox 不显示正确的默认选择选项?

php - 将多个文本框条目添加到 mysql 数据库