首页 > 数据库 >mysql-递归查询

mysql-递归查询

时间:2022-08-15 15:14:37浏览次数:83  
标签:parent recursive 递归 查询 dept mysql test id name

0. 背景

最近接触到的业务中需要通过mysql查询部门的组织架构层级关系,最一开始的思路是想通过自定义函数来完成,但是查询效率真的是“感人”。又另辟蹊径找到mysql的递归查询,最终很好的实现了业务诉求。回过头来记录一下。

1. 公用表表达式-CTE

公用表表达式是一个命名的临时结果集,不作为对象存储,只在执行期间存在。

CTE基本语法:

with cite_name as
(
    query
)
select *
from cite_name
;

2. CTE的递归查询

CTE的递归查询就是需要不断的去“引用”CTE本身。

基本语法:

with recursive cte_name as
(
    initial_query     -- anchor member
    union all
    recursive_query -- 引用CTE名称的递归成员
)
select * 
from cte_name
;

CTE递归查询主要有三部分:

① 初始查询,形成递归查询的初始结果集
② 递归查询部分,引用CTE名称的查询
③ 终止条件,确保查询在不满足条件时终止

WITH RECURSIVE cte_count (n) 
AS (
      SELECT 1
      UNION ALL
      SELECT n + 1 
      FROM cte_count 
      WHERE n < 3
    )
SELECT n 
FROM cte_count; 

输出结果:

+---+
| n |
+---+
| 1 |
| 2 |
| 3 |
+---+
3 rows in set (0.01 sec)

注意:

递归部分,不能使用 聚合函数,distinct ,group by 子句, order by 子句,limit子句。

3. 测试数据

drop table if exists recursive_test;
create table recursive_test(
id          int(8) AUTO_INCREMENT PRIMARY KEY   comment '自增主键',
dept_id			int(8)			comment '部门ID',
dept_name		varchar(40)	        comment '部门名称',
parent_id               int(8)			comment '父级部门ID'
)engine=InnoDB DEFAULT charset=utf8mb4 comment '部门表'
;


insert into recursive_test(dept_id, dept_name, parent_id) values(1, '宇宙总公司', 0);
insert into recursive_test(dept_id, dept_name, parent_id) values(2, '湖北分公司', 1);
insert into recursive_test(dept_id, dept_name, parent_id) values(3, '北京分公司', 1);
insert into recursive_test(dept_id, dept_name, parent_id) values(4, '上海分公司', 1);
insert into recursive_test(dept_id, dept_name, parent_id) values(5, '杭州分公司', 1);
insert into recursive_test(dept_id, dept_name, parent_id) values(123456, '上海黄浦办事处', 4);
insert into recursive_test(dept_id, dept_name, parent_id) values(123457, '上海长宁办事处', 4);
insert into recursive_test(dept_id, dept_name, parent_id) values(123458, '上海杨浦办事处', 4);
insert into recursive_test(dept_id, dept_name, parent_id) values(123459, '上海静安办事处', 4);
insert into recursive_test(dept_id, dept_name, parent_id) values(1234567, '上海黄浦办事处-1', 123456);
insert into recursive_test(dept_id, dept_name, parent_id) values(1234568, '上海黄浦办事处-2', 123456);
insert into recursive_test(dept_id, dept_name, parent_id) values(1234569, '上海黄浦办事处-3', 123456);

表结构如下:

从recursive_test表结构中较难对这些部门的层级关系有清晰的认识,对表中的组织层级关系绘制出来就一目了然了:

graph TB 宇宙总公司 --> 上海分公司 宇宙总公司 --> 湖北分公司 宇宙总公司 --> 北京分公司 宇宙总公司 --> 杭州分公司 上海分公司 --> 上海长宁办事处 上海分公司 --> 上海杨浦办事处 上海分公司 --> 上海黄浦办事处 上海分公司 --> 上海静安办事处 上海黄浦办事处 --> 上海黄浦办事处-1 上海黄浦办事处 --> 上海黄浦办事处-2 上海黄浦办事处 --> 上海黄浦办事处-3

如果我们想将 宇宙总公司——>上海分公司——>上海黄浦办事处——>上海黄浦办事处-1 这些组织层级关系串起来,在mysql中用一个字段来表示该如何处理呢?

4. 解决思路

① 通过left join去关联表实现

当组织架构的层级是固定且层级数较少(建议最多3层)可以通过该方式实现,一旦层级数过多,这种方式不再适用。

② 自定义函数

通过自定义函数,可以先获取当前部门所在链路上的上级部门ID,然后对每个部门ID进行部门名称匹配(可在自定义函数中直接输出部门名称,自行尝试)

但是有个较大的缺点是 自定义函数的查询效率真的很低很低!!!因测试样本数据较少,运行效率还OK。

SET GLOBAL log_bin_trust_function_creators = 1;

delimiter $$

drop function if exists find_parent_list_test $$

create function find_parent_list_test(son_id  varchar(20)) returns varchar(256)

begin

    declare parent_list varchar(256);
       
    select group_concat(a.departmentid) into parent_list
    from
        (
        select 
        (select @dept_id := parent_id from recursive_test where dept_id=@dept_id) as departmentid
        from recursive_test as a, (select @dept_id := son_id) as b
        )as a
    where a.departmentid is not null;
    
    return parent_list;

end

$$

delimiter ;

③ 递归查询

根据根部门ID信息,遍历出所有的组织链路,通过 concat函数拼接起来。整体查询效率和便捷性还是很不错的。

with recursive party_detail as
(
select dept_id, dept_name, dept_name as dept_structure
from recursive_test
where dept_id=1     -- 根部门ID

union

select a.dept_id ,a.dept_name, concat(b.dept_structure, ' > ', a.dept_name) as dept_structure
from party_detail as b 
join recursive_test as a on b.dept_id=a.parent_id
)
select *
from party_detail
;

参考来源:
https://www.begtut.com/mysql/mysql-recursive-cte.html

标签:parent,recursive,递归,查询,dept,mysql,test,id,name
From: https://www.cnblogs.com/wxyz94/p/16577803.html

相关文章

  • 2022-08-15 第六组 Myy 学习笔记_Mysql数据库
    Mysql数据库数据库数据库【按照数据结构来组织、存储和管理数据的仓库】,是一个长期存储载计算机内的、有组织的、可共享的、统一管理的大量数据的集合。数据对于公......
  • Linux锁定、解锁和查询账号锁定状态
    Linux锁定、解锁和查询账号锁定状态passwd-l锁定passwd-u解锁passwd-S查询锁定状态......
  • Mysql 8.0报错
    安装更新源sudoapt-getupdate更新软件`sudoapt-getupgrade下载wgethttps://dev.mysql.com/get/mysql-apt-config_0.8.14-1_all.debsudodpkg-imysql-apt-con......
  • MySQL连接相关
    官网:https://dev.mysql.com/doc/refman/8.0/en/performance-schema-processlist-table.html   interactive_timeout:交互式连接超时时间(mysql工具、mysqldump等)wai......
  • 如何在Excel/WPS表格中实现IP归属地查询?
     给您一个IP地址,您能查出它的归属地是哪座城市吗?今天小编就教给大家如何在Excel/WPS表格中使用IP归属地查询公式。使用公式前需要去腾讯位置服务申请key和Secret key,网......
  • SQL优化这5个极简法则,直接让查询原地起飞!
      SQL作为关系型数据库的标准语言,是IT从业人员必不可少的技能之一。SQL本身并不难学,编写查询语句也很容易,但是想要编写出能够高效运行的查询语句却有一定的难度。......
  • 搞定面试官 - 可以介绍一下在 MySQL 中你平时是怎么使用 COUNT() 的嘛?
    大家好,我是程序员啊粥。相信在大家的工作中,有很多的功能都需要用到count(*)来统计表中的数据行数。同时,对于一些大数据的表,用count都是瑟瑟发抖,往往会结合缓存等进行......
  • MySQL使用Explain查看执行计划
    概述Explain模拟优化器执行SQL语句,在5.6以及以后的版本中,除了select,其他insert,update和delete均可以使用explain查看执行计划,从而知道mysql是如何处理sql语句,分析查询语句......
  • MySQL IF CASE
    IF函数语法如果expr1是TRUE,则IF()的返回值为expr2;否则返回值则为expr3。IF(expr1,expr2,expr3)样例--简单SELECTIF(t.gender=0,'男','女')SEXFROM......
  • MySQL事务执行一半,连接被kill掉会怎样?
     官方文档解释: https://dev.mysql.com/doc/refman/8.0/en/kill.html优秀博客:https://cloud.tencent.com/developer/article/1815896         ......