首页 > 数据库技术 > 详细

MySQL树形结构的数据库表设计和查询

时间:2019-03-07 17:13:05      阅读:181      评论:0      收藏:0      [点我收藏+]

1、邻接表(Adjacency List)

  • 实例:现在有一个要存储一下公司的人员结构,大致层次结构如下:

技术分享图片

那么怎么存储这个结构?并且要获取以下信息:

  • 1.查询小天的直接上司。
  • 2.查询老宋管理下的直属员工。
  • 3.查询小天的所有上司。
  • 4.查询老王管理的所有员工。
 1 方案一、(Adjacency List)只存储当前节点的父节点信息。
 2 -- 2018-8-11 MySQL树结构 --
 3 -- Author: xielong  Email:cnxielong@gmail.com --
 4 
 5  -- 建表 --
 6  DROP TABLE  IF EXISTS Employees;
 7 
 8  CREATE TABLE IF NOT EXISTS Employees (
 9   id INT AUTO_INCREMENT,
10   ename VARCHAR (100),
11   job VARCHAR (100),
12   parent_id INT,
13   PRIMARY KEY(id)
14 ) ENGINE = INNODB DEFAULT CHARSET = UTF8;
15 
16 
17 DESCRIBE Employees
18 
19 
20 -- 插入数据 --
21 INSERT INTO `employees` ( `ename`, `job`, `parent_id`) VALUES(老王,高管,0);
22 INSERT INTO `employees` ( `ename`, `job`, `parent_id`) VALUES(老宋,产品部主管,1);
23 INSERT INTO `employees` ( `ename`, `job`, `parent_id`) VALUES(老牛,高管,1);
24 INSERT INTO `employees` ( `ename`, `job`, `parent_id`) VALUES(小吴,高管,2);
25 INSERT INTO `employees` ( `ename`, `job`, `parent_id`) VALUES(小李,高管,2);
26 INSERT INTO `employees` ( `ename`, `job`, `parent_id`) VALUES(小欢,高管,3);
27 INSERT INTO `employees` ( `ename`, `job`, `parent_id`) VALUES(小小,高管,3);
28 INSERT INTO `employees` ( `ename`, `job`, `parent_id`) VALUES(小天,高管,4);
29 INSERT INTO `employees` ( `ename`, `job`, `parent_id`) VALUES(肖丽,高管,4);
30 INSERT INTO `employees` ( `ename`, `job`, `parent_id`) VALUES(十号,高管,5);
31 INSERT INTO `employees` ( `ename`, `job`, `parent_id`) VALUES(十一号,高管,5);
32 INSERT INTO `employees` ( `ename`, `job`, `parent_id`) VALUES(十二号,高管,6);
33 INSERT INTO `employees` ( `ename`, `job`, `parent_id`) VALUES(十三号,高管,6);
34 INSERT INTO `employees` ( `ename`, `job`, `parent_id`) VALUES(十四号,高管,7);
35 INSERT INTO `employees` ( `ename`, `job`, `parent_id`) VALUES(小黑十五,高管,7);

数据库结构信息:

技术分享图片

  • 好的,现在开始进入回答环节:

  • 1.查询小天的直接上司:
     1    -- 查询小天的上级Id  隐式内连接 关键条件父节点 --
     2     SELECT e2.id, e2.ename, e2.job FROM Employees e1, Employees e2 WHERE e1.`parent_id` = e2.id AND e1.id=8
     3 
     4     -- 查询小天的上级Id  显示内连接 关键条件父节点 --
     5     SELECT e2.id, e2.ename, e2.job FROM Employees e1 INNER JOIN Employees e2 WHERE e1.`parent_id` = e2.id AND e1.id=8
    

    技术分享图片

2.查询老宋管理下的直属员工:

 1  -- 查询老宋管理下的直属员工:隐式内连接 关键条件父子节点 老宋ID=2 --
 2     SELECT e2.id, e2.ename, e2.job FROM Employees e1, Employees e2 WHERE e1.id=2 AND e2.`parent_id` = e1.id
 3 
 4     -- 查询小天的上级Id  显示内连接 关键条件父子节点 老宋.ename=老宋 --
 5     SELECT e2.id, e2.ename, e2.job FROM Employees e1 INNER JOIN Employees e2 WHERE e2.`parent_id` = e1.id AND e1.ename=老宋

技术分享图片

 3.查询小天的所有上司。

  • 这里肯定没法直接查,只能用循环进行循环查询,先查直接上司,再查直接上司的直接上司,依次循环,这样麻烦的事情,还是得先建立一个存储过程:
  • 睁大眼睛看仔细了,接下来是骚操作环节:
 1 --  1、查询小天的所有上级 --
 2 
 3 -- 删除 --
 4    DROP FUNCTION IF EXISTS getSuperiors; 
 5 
 6 -- 创建 -- 
 7 DELIMITER $$
 8 
 9 CREATE DEFINER=`root`@`localhost` FUNCTION `getSuperiors` (`uid` INT) RETURNS VARCHAR(1000) 
10 BEGIN
11     DECLARE superiors VARCHAR(1000) DEFAULT ‘‘;
12     DECLARE sTemp INTEGER DEFAULT uid;
13     DECLARE tmpName VARCHAR(20);
14 
15     WHILE (sTemp>0) DO
16         SELECT parent_id INTO sTemp FROM employees WHERE id = sTemp;
17         SELECT ename INTO tmpName FROM employees WHERE id = sTemp;
18         IF(sTemp>0)THEN
19             SET superiors = CONCAT(tmpName,,,superiors);
20         END IF;
21     END WHILE;
22         SET superiors = LEFT(superiors,CHARACTER_LENGTH(superiors)-1);
23     RETURN superiors;
24 END $$
25 
26 -- 调用 --
27 SELECT   getSuperiors(8) 上司;

 

  • 这一段存储过程可以查询子节点的所有父节点,来试验一下 

  • 好的,骚操作完成。

  • 显然,这样。获取子节点的全部父节点的时候很麻烦。。

技术分享图片

4.查询老王管理的所有员工。

  思路如下:先获取所有父节点为老王id的员工id,然后将员工姓名加入结果列表里,在调用一个神奇的查找函数,即可进行神奇的查找:

 1   --  查询老王管理的所有员工 --
 2 
 3 -- 删除 --
 4    DROP PROCEDURE IF EXISTS getSubordinate 
 5 
 6 -- 创建 -- 
 7    DELIMITER $$
 8 
 9 CREATE DEFINER = `root` @`localhost` FUNCTION `getSubordinate` (`uid` INT) RETURNS VARCHAR (2000)
10 BEGIN
11   DECLARE str VARCHAR (1000);
12   DECLARE cid VARCHAR (100);
13   DECLARE result VARCHAR (1000);
14   DECLARE tmpName VARCHAR (100);
15   SET str = $;
16   SET cid = CAST(uid AS CHAR(10));
17   WHILE
18     cid IS NOT NULL DO 
19     SET str = CONCAT(str, ,, cid);
20     SELECT  GROUP_CONCAT(id) INTO cid  FROM  employees  WHERE FIND_IN_SET(parent_id, cid);
21   END WHILE;
22   SELECT
23     GROUP_CONCAT(ename) INTO result   FROM  employees  WHERE FIND_IN_SET(parent_id, str);
24   RETURN result;
25 END $$
26 
27 -- 调用 --
28 SELECT   getSubordinate(1)

看神奇的结果:

技术分享图片  

  • 虽然搞出来了,但说实话,真是不容易。。。

  • 这种方法的优点是存储的信息少,查直接上司和直接下属的时候很方便,缺点是多级查询的时候很费劲。所以当只需要用到直接上下级关系的时候,用这种方法还是不错的,可以节省很多空间。

2、继承关系驱动的设计表和基于左右值编码的设计

参考链接: https://blog.csdn.net/lj1314ailj/article/details/52074216

 

参考:MySQL 实现树形的遍历

MySQL 实现树形的遍历(关于多级菜单栏以及多级上下部门的查询问题)

参考链接: https://blog.csdn.net/mchdba/article/details/39277301 ---------------------

 

MySQL树形结构的数据库表设计和查询

原文:https://www.cnblogs.com/w2206/p/10490208.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!