使用邻接表模型在MySQL中管理分层数据
简介:在本教程中,您将学习如何使用邻接表模型在MySQL中管理分层数据。
邻接表模型简介
分层数据无处不在。
它可以是博客类别,产品层次结构或组织结构。
在MySQL中,有许多方法可以管理层次结构数据,而邻接表模型可能是最简单的解决方案。
由于其简单性,邻接表模型是开发人员和数据库管理员的非常普遍的选择。
在邻接列表模型中,每个节点都有一个指向其父节点的指针。
顶层节点没有父节点。
请参阅以下电子产品类别:
在使用邻接表模型之前,您应该熟悉一些术语:
电子是顶部节点或根节点。
笔记本电脑,相机和照片,电话和配件节点是"电子"节点的子级。
反之亦然,电子节点是"笔记本电脑,相机和照片,电话和配件"节点的父级。叶子节点是没有子节点的节点,例如Laptops,PC,Android,iOS等,而非叶子节点是至少有一个孩子的节点。
节点的子孙称为子孙。
节点的父母,祖父母等也称为祖先。
要对该类别树建模,我们可以创建一个名为category的表,该表具有三列:id,title和parent_id,如下所示:
CREATE TABLE category ( id int(10) unsigned NOT NULL AUTO_INCREMENT, title varchar(255) NOT NULL, parent_id int(10) unsigned DEFAULT NULL, PRIMARY KEY (id), FOREIGN KEY (parent_id) REFERENCES category (id) ON DELETE CASCADE ON UPDATE CASCADE );
表中的每一行都是由id列标识的树中的节点。
parent_id列是类别表本身的外键。
它的作用类似于指向id列的指针。
插入资料
树的根节点没有父节点,因此,parent_id设置为NULL。
其他节点必须只有一个父节点。
要插入根节点,可将parent_id设置为NULL,如下所示:
INSERT INTO category(title,parent_id) VALUES('Electronics',NULL);
要插入非根节点,您只需要将其parent_id设置为其父节点的id即可。
例如,"笔记本电脑和PC","相机和照片"以及"电话和配件"节点的parent_id设置为1:
INSERT INTO category(title,parent_id) VALUES('Laptops & PC',1); INSERT INTO category(title,parent_id) VALUES('Laptops',2); INSERT INTO category(title,parent_id) VALUES('PC',2); INSERT INTO category(title,parent_id) VALUES('Cameras & photo',1); INSERT INTO category(title,parent_id) VALUES('Camera',5); INSERT INTO category(title,parent_id) VALUES('Phones & Accessories',1); INSERT INTO category(title,parent_id) VALUES('Smartphones',7); INSERT INTO category(title,parent_id) VALUES('Android',8); INSERT INTO category(title,parent_id) VALUES('iOS',8); INSERT INTO category(title,parent_id) VALUES('Other Smartphones',8); INSERT INTO category(title,parent_id) VALUES('Batteries',7); INSERT INTO category(title,parent_id) VALUES('Headsets',7); INSERT INTO category(title,parent_id) VALUES('Screen Protectors',7);
寻找根节点
根节点是没有父节点的节点。
换句话说,其parent_id为NULL:
SELECT id, title FROM category WHERE parent_id IS NULL;
查找节点的直接子代
以下查询获取根节点的直接子代:
SELECT id, title FROM category WHERE parent_id = 1;
查找叶节点
叶节点是没有子节点的节点。
SELECT c1.id, c1.title FROM category c1 LEFT JOIN category c2 ON c2.parent_id = c1.id WHERE c2.id IS NULL;
查询整棵树
以下递归公用表表达式(CTE)检索整个类别树。
请注意,自MySQL 8.0起,CTE功能已可用
WITH RECURSIVE category_path (id, title, path) AS ( SELECT id, title, title as path FROM category WHERE parent_id IS NULL UNION ALL SELECT c.id, c.title, CONCAT(cp.path, ' > ', c.title) FROM category_path AS cp JOIN category AS c ON cp.id = c.parent_id ) SELECT * FROM category_path ORDER BY path;
查询子树
以下查询获取ID为7的Phone&Accessories子树。
WITH RECURSIVE category_path (id, title, path) AS ( SELECT id, title, title as path FROM category WHERE parent_id = 7 UNION ALL SELECT c.id, c.title, CONCAT(cp.path, ' > ', c.title) FROM category_path AS cp JOIN category AS c ON cp.id = c.parent_id ) SELECT * FROM category_path ORDER BY path;
查询单个路径
要查询从底部到顶部的单个路径(例如,从iOS到电子产品),请使用以下语句:
WITH RECURSIVE category_path (id, title, parent_id) AS ( SELECT id, title, parent_id FROM category WHERE id = 10 -- child node UNION ALL SELECT c.id, c.title, c.parent_id FROM category_path AS cp JOIN category AS c ON cp.parent_id = c.id ) SELECT * FROM category_path;
计算每个节点的级别
假设根节点的级别为0,则下面的每个节点的级别等于其父节点的级别加1。
WITH RECURSIVE category_path (id, title, lvl) AS ( SELECT id, title, 0 lvl FROM category WHERE parent_id IS NULL UNION ALL SELECT c.id, c.title,cp.lvl + 1 FROM category_path AS cp JOIN category AS c ON cp.id = c.parent_id ) SELECT * FROM category_path ORDER BY lvl;
删除节点及其后代
要删除节点及其后代,只需删除节点本身,所有后代将被外键约束的DELETE CASCADE自动删除。
例如,要删除"笔记本电脑和PC"节点及其子节点("笔记本电脑,PC"),请使用以下语句:
DELETE FROM category WHERE id = 2;
删除节点并提升其后代
要删除非叶子节点并提升其后代:
首先,将节点的直接子节点的parent_id更新为新的父节点的id。
然后,删除该节点。
例如,要删除"智能手机"节点并升级其子代,例如Android,iOS,"其他智能手机"节点,请执行以下操作:
首先,为智能手机的所有直接子代更新parent_id:
UPDATE category SET parent_id = 7 -- Phones & Accessories WHERE parent_id = 5; -- Smartphones
其次,删除"智能手机"节点:
DELETE FROM category WHERE id = 8;
这两个语句都应该包装在一个事务中:
BEGIN; UPDATE category SET parent_id = 7 WHERE parent_id = 5; DELETE FROM category WHERE id = 8; COMMIT;
移动子树
要移动子树,只需更新子树顶部节点的parent_id。
例如,要将"照相机和照片"作为"电话和附件"的子项移动,请使用以下语句:
UPDATE category SET parent_id = 7 WHERE id = 5;
在本教程中,您学习了如何使用邻接表模型来管理MySQL中的分层数据。