前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >SQL 层级查询(一)

SQL 层级查询(一)

作者头像
白日梦想家
发布2020-12-14 09:48:17
2.8K0
发布2020-12-14 09:48:17
举报
文章被收录于专栏:SQL实现SQL实现

相信大家在工作中都遇到过存在层次关系的数据表,典型的例子诸如菜单表(多级菜单)、用户表(拥有上下级关系)、商品类目表(多级类目)。

我们经常用到的案例表 emp 就具有层次结构的数据,字段 empno 是员工编号,字段 mgr 则是员工的上级的编号。emp 表的部分数据如下:

代码语言:javascript
复制
 empno  ename      mgr  
------  ------  --------
  7369  SMITH       7902
  7499  ALLEN       7698
  7521  WARD        7698
  7566  JONES       7839
  7654  MARTIN      7698
  7698  BLAKE       7839
  7782  CLARK       7839
  7788  SCOTT       7566
  7839  KING      (NULL)
  7844  TURNER      7698
  7876  ADAMS       7788
  7900  JAMES       7698
  7902  FORD        7566
  7934  MILLER      7782

其中,mgr 为 NULL 表明该员工没有上级领导。

我们要把每个员工的所有上级领导都找出来,实现的效果如下:

代码语言:javascript
复制
 empno  ename   path                  
------  ------  ----------------------
  7369  SMITH   ->FORD->JONES->KING   
  7499  ALLEN   ->BLAKE->KING         
  7521  WARD    ->BLAKE->KING         
  7566  JONES   ->KING                
  7654  MARTIN  ->BLAKE->KING         
  7698  BLAKE   ->KING                
  7782  CLARK   ->KING                
  7788  SCOTT   ->JONES->KING         
  7839  KING                          
  7844  TURNER  ->BLAKE->KING         
  7876  ADAMS   ->SCOTT->JONES->KING  
  7900  JAMES   ->BLAKE->KING         
  7902  FORD    ->JONES->KING         
  7934  MILLER  ->CLARK->KING    

对于编号为 7369 的 SMITH,他的直属领导的编号是 7902,姓名叫做 FORD;FORD 的直属领导叫做 JONES,编号为 7566;编号为 7566 的直属领导是编号为 7839 的 KING,而 KING 没有直属领导。因此,SMITH 的上级领导的关系链构成:->FORD->JONES->KING 。

对于找到每位员工的直属领导,使用一个自关联就能做到。

代码语言:javascript
复制
SELECT 
  a.empno,
  a.ename,
  b.ename AS leader
FROM
  emp a 
  LEFT JOIN emp b 
    ON b.empno = a.mgr 

由于 emp 表的层次关系的最大深度只有 4,因此,在原来查询父子关系的基础上再增加两个自关联就能把表中的所有关系都连接起来。

代码语言:javascript
复制
SELECT 
  a.empno,
  a.ename,
  CONCAT(
    IFNULL(CONCAT('->', b.ename), ''),
    IFNULL(CONCAT('->', c.ename), ''),
    IFNULL(CONCAT('->', d.ename), '')
  ) AS path 
FROM
  emp a 
  LEFT JOIN emp b 
    ON b.empno = a.mgr 
  LEFT JOIN emp c 
    ON c.empno = b.mgr 
  LEFT JOIN emp d 
    ON d.empno = c.mgr 

使用 IFNULL() 是为了避免当上级不存在时,ename 为 NULL 和所有字符串拼接的结果都为 NULL 。

如果层级太深,或者层次深度不确定,可以使用递归的方式解决。

代码语言:javascript
复制
WITH RECURSIVE leader_path(empno, ename, mgr, path) AS 
(SELECT 
  empno,
  ename,
  mgr,
  CAST('' AS CHAR(100)) AS path 
FROM
  emp 
UNION ALL 
SELECT 
  a.empno,
  a.ename,
  b.mgr,
  CONCAT(
    a.path,
    IFNULL(CONCAT('->', b.ename), '')
  ) 
FROM
  leader_path a 
  LEFT JOIN emp b 
    ON a.mgr = b.empno 
WHERE b.empno IS NOT NULL) 
SELECT 
  empno,
  ename,
  path 
FROM
  leader_path 
WHERE mgr IS NULL 
ORDER BY 1 

哈,使用递归的方案感觉要复杂了许多。使用递归需要注意几个地方:

  1. 在递归中一定要加入终止条件,本 SQL 的终止条件是 WHERE b.empno IS NOT NULL
  2. 遇到字符串拼接需要提前设置该字段的长度,对应到 SQL 中的操作是 CAST('' AS CHAR(100))
  3. 递归会生成中间结果,我们要把中间结果过滤掉,WHERE mgr IS NULL 就是只获取最终的结果。
本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2020-12-01,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 SQL实现 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档