本文是我学习MySQL官方教程Managing Hierarchical Data in MySQL的笔记 多层数据结构估计所有的web开发者估计都不会陌生,各种软件的分类都是基于多层结构来设计的。 下面是一个典型的多层数据结构示意图:
相关创建数据语句:
CREATE TABLE category(
category_id INT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(20) NOT NULL,
parent INT DEFAULT NULL);
INSERT INTO category
VALUES(1,'ELECTRONICS',NULL),(2,'TELEVISIONS',1),(3,'TUBE',2),
(4,'LCD',2),(5,'PLASMA',2),(6,'PORTABLE ELECTRONICS',1),
(7,'MP3 PLAYERS',6),(8,'FLASH',7),
(9,'CD PLAYERS',6),(10,'2 WAY RADIOS',6);
SELECT * FROM category ORDER BY category_id;
CREATE TABLE nested_category ( category_id INT AUTO_INCREMENT PRIMARY KEY, name VARCHAR(20) NOT NULL, lft INT NOT NULL, rgt INT NOT NULL, deleted TINYINT(8) NOT NULL DEFAULT '0', );
INSERT INTO nested_category VALUES(1,'ELECTRONICS',1,20),(2,'TELEVISIONS',2,9),(3,'TUBE',3,4), (4,'LCD',5,6),(5,'PLASMA',7,8),(6,'PORTABLE ELECTRONICS',10,19), (7,'MP3 PLAYERS',11,14),(8,'FLASH',12,13), (9,'CD PLAYERS',15,16),(10,'2 WAY RADIOS',17,18);
SELECT * FROM nested_category ORDER BY category_id;
SELECT node.name FROM nested_category AS node, nested_category AS parent WHERE node.lft BETWEEN parent.lft AND parent.rgt AND parent.name = 'ELECTRONICS' ORDER BY node.lft;
查询所有无分支的节点 条件: __ 右边 = 左边L + 1 __
SELECT name FROM nested_category WHERE rgt = lft + 1;
查询某个字节点到根节点的路径
1 2 3 4 5 6
SELECT parent.name FROM nested_category AS node, nested_category AS parent WHERE node.lft BETWEEN parent.lft AND parent.rgt AND node.name = 'FLASH' ORDER BY parent.lft;
查询节点的深度
1 2 3 4 5
SELECT node.name, (COUNT(parent.name) - 1) AS depth FROM nested_category AS node, nested_category AS parent WHERE node.lft BETWEEN parent.lft AND parent.rgt GROUP BY node.name ORDER BY node.lft;
查询子节点的深度
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
SELECT node.name, (COUNT(parent.name) - (sub_tree.depth + 1)) AS depth FROM nested_category AS node, nested_category AS parent, nested_category AS sub_parent, ( SELECT node.name, (COUNT(parent.name) - 1) AS depth FROM nested_category AS node, nested_category AS parent WHERE node.lft BETWEEN parent.lft AND parent.rgt AND node.name = 'PORTABLE ELECTRONICS' GROUP BY node.name ORDER BY node.lft ) AS sub_tree
WHERE node.lft BETWEEN parent.lft AND parent.rgt AND node.lft BETWEEN sub_parent.lft AND sub_parent.rgt AND sub_parent.name = sub_tree.name GROUP BY node.name ORDER BY node.lft;
LOCK TABLE nested_category WRITE; SELECT @myRight := rgt FROM nested_category WHERE name = 'TELEVISIONS'; UPDATE nested_category SET rgt = rgt + 2 WHERE rgt > @myRight; UPDATE nested_category SET lft = lft + 2 WHERE lft > @myRight; INSERT INTO nested_category(name, lft, rgt) VALUES('GAME CONSOLES', @myRight + 1, @myRight + 2); UNLOCK TABLES;
删除新节点 删除节点的算法与添加一个节点的算法相反
删除一个没有子节点的节点
1 2 3 4 5 6
LOCK TABLE nested_category WRITE; SELECT @myLeft := lft, @myRight := rgt, @myWidth := rgt - lft + 1 FROM nested_category WHERE name = 'GAME CONSOLES'; DELETE FROM nested_category WHERE lft BETWEEN @myLeft AND @myRight; UPDATE nested_category SET rgt = rgt - @myWidth WHERE rgt > @myRight; UPDATE nested_category SET lft = lft - @myWidth WHERE lft > @myRight; UNLOCK TABLES;
删除一个分支节点和它所有的子节点
1 2 3 4 5 6
LOCK TABLE nested_category WRITE; SELECT @myLeft := lft, @myRight := rgt, @myWidth := rgt - lft + 1 FROM nested_category WHERE name = 'MP3 PLAYERS'; DELETE FROM nested_category WHERE lft BETWEEN @myLeft AND @myRight; UPDATE nested_category SET rgt = rgt - @myWidth WHERE rgt > @myRight; UPDATE nested_category SET lft = lft - @myWidth WHERE lft > @myRight; UNLOCK TABLES;
删除一个节点后移动其字节点到
1 2 3 4 5 6 7
LOCK TABLE nested_category WRITE; SELECT @myLeft := lft, @myRight := rgt, @myWidth := rgt - lft + 1 FROM nested_category WHERE name = 'PORTABLE ELECTRONICS'; DELETE FROM nested_category WHERE lft = @myLeft; UPDATE nested_category SET rgt = rgt - 1, lft = lft - 1 WHERE lft BETWEEN @myLeft AND @myRight; UPDATE nested_category SET rgt = rgt - 2 WHERE rgt > @myRight; UPDATE nested_category SET lft = lft - 2 WHERE lft > @myRight; UNLOCK TABLES;
补充:移动一个分类
将分类5移动到分类9下面
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
LOCK TABLE nested_category WRITE; SELECT @myLeft := lft, @myRight := rgt, @myWidth := rgt - lft + 1 FROM nested_category WHERE category_id = 5; UPDATE nested_category SET deleted=2 WHERE lft BETWEEN @myLeft AND @myRight; UPDATE nested_category SET rgt = rgt - @myWidth WHERE rgt > @myRight AND deleted=0; UPDATE nested_category SET lft = lft - @myWidth WHERE lft > @myRight AND deleted=0;
SELECT @destLeft := lft,@destRight := rgt FROM nested_category WHERE category_id = 9;
UPDATE nested_category SET rgt = rgt + @myWidth WHERE rgt > @destRight AND deleted=0; UPDATE nested_category SET lft = lft + @myWidth WHERE lft > @destRight AND deleted=0;
UPDATE nested_category SET rgt = rgt -(@myLeft-@destRight) WHERE deleted=2; UPDATE nested_category SET lft = lft -(@myLeft-@destRight) WHERE deleted=2; UPDATE nested_category SET rgt = rgt + @myWidth WHERE category_id = 9; UPDATE nested_category SET deleted=0 WHERE deleted=2; UNLOCK TABLES;
将分类5移动到分类9前面
1 2 3 4 5 6 7 8 9 10 11 12 13 14
SELECT @myLeft := lft, @myRight := rgt, @myWidth := rgt - lft + 1 FROM nested_category WHERE category_id = 5; UPDATE nested_category SET deleted=2 WHERE lft BETWEEN @myLeft AND @myRight; UPDATE nested_category SET rgt = rgt - @myWidth WHERE rgt > @myRight AND deleted=0; UPDATE nested_category SET lft = lft - @myWidth WHERE lft > @myRight AND deleted=0;
SELECT @destLeft := lft,@destRight := rgt FROM nested_category WHERE category_id = 9;
UPDATE nested_category SET rgt = rgt + @myWidth WHERE rgt >= @destLeft AND deleted=0; UPDATE nested_category SET lft = lft + @myWidth WHERE lft >= @destLeft AND deleted=0;
UPDATE nested_category SET rgt = rgt -(@myLeft-@destRight) WHERE deleted=2; UPDATE nested_category SET lft = lft -(@myLeft-@destRight) WHERE deleted=2; UPDATE nested_category SET deleted=0 WHERE deleted=2; UNLOCK TABLES;