MySQL通过自定义函数以及存储过程实现递归查询父级ID

摘要:
1.存储过程定义存储过程是存储在数据库中的一组SQL语句,用于完成大型数据库系统中的特定功能。它在第一次编译后调用,不需要再次编译。2.为什么使用存储过程来实现树结构数据查询父节点。1.可以完成更复杂的操作和判断。2.编程强大灵活。3.SQL编程代码可以重用。4.预编译机制。5减少网络之间的数据传输并节省开销3.使用存储过程和临时表完成服务(当参数为多个子节点ID时

1.存储过程定义

 存储过程(Stored Procedure)是在大型数据库系统中,一组为了完成特定功能的SQL 语句集,存储在数据库中,经过第一次编译后调用不需要再次编译

2.为什么用存储过程实现树结构数据查询父级节点
  1、能完成较复杂的运算与判断
    2、可编程行强,灵活
    3、SQL编程的代码可重复使用
    4、预编译机制
    5、减少网络之间的数据传输,节省开销    

3.使用存储过程和临时表完成业务(当参数为多个子节点id,都要去查找父级节点,并返回一棵完整的树结构给前端,需要做复杂的递归与数据拼接)

3.1需要用到的函数

# 计算传入字符串的总length
DELIMITER $$
DROP function IF EXISTS `func_split_TotalLength` $$

CREATE FUNCTION `func_split_TotalLength`

(f_string varchar(1000),f_delimiter varchar(5)) RETURNS int(11)

BEGIN
    # 计算传入字符串的总length
    return 1+(length(f_string) - length(replace(f_string,f_delimiter,'')));
END$$
DELIMITER ;
3.2 拆分字符串成数组
# 拆分字符串成数组
DELIMITER $$
DROP function IF EXISTS `func_split` $$
CREATE FUNCTION `func_split`
(f_string varchar(1000),f_delimiter varchar(5),f_order int) RETURNS varchar(255) CHARSET utf8
BEGIN
    # 拆分传入的字符串,返回拆分后的新字符串
    declare result varchar(255) default '';
    set result = reverse(substring_index(reverse(substring_index(f_string,f_delimiter,f_order)),f_delimiter,1));
    return result;
END$$
DELIMITER ;

3.3使用临时表存储父子节点信息(通过动态改变角色菜单权限,完成菜单列表的动态渲染)

# 传入参数roleId,字符串数组的子节点id
create
    definer = dev@`%` procedure updateRoleMenu(IN roleId int,IN id varchar(100))
begin
    -- 定义局部变量
    declare oTempChild VARCHAR(4000);
    # 拆分结果
    declare cnt int default 0;
    declare i int default 0;
    declare oTemp varchar(100);
    declare ids varchar(100);
    set cnt = func_split_TotalLength(id,',');
    -- cast 类型转换
    set oTemp = '';
    -- 每次执行存储过程时,检查是否存在 tmp_print临时表,存在删除
    drop table if exists tmp_split;
    -- 创建临时表
    create temporary table tmp_split (status int not null);
#     set oTempChild = cast(id AS char);
    while i < cnt
        do
            set i = i + 1;
            set oTempChild= func_split(id,',',i);
                while oTempChild is not null
                    do
                    -- 循环获取子类的 id
                    -- 判断 oTemp 是否为空
                    if oTemp != '' then
                        insert into tmp_split(`status`) values (oTemp);
                    else
                        insert into tmp_split(`status`) values (oTempChild);
                    end if;
                    -- 搜索父类 id
                    select GROUP_CONCAT(menu_pid) INTO oTempChild
                    from mps_menu
                         -- find_in_set 是全表扫描的,find_in_set 是精确匹配,字段值以英文”,”分隔
                         -- 搜索 com_id
                    where FIND_IN_SET(menu_id, oTempChild) > 0;
            end while;
        end while;
#         将得到的父级结合和子集节点合并去重,拼接成字符串,并更新角色菜单权限
        update mps_role a ,(select GROUP_CONCAT(DISTINCT(status)) as list from tmp_split) c set a.role_render=c.list where role_id = roleId;
end;

4.在dao层需要编写的接口

  /**
     * 动态查找当前节点列表的所有父节点
     * @param roleId 角色编号
     * @param ids 节点字符串数组
     */
    void updateRoleMenu(@Param("roleId")Integer roleId,@Param("ids")String ids);
    <select id="updateRoleMenu" parameterType="java.lang.Object" statementType="CALLABLE">
        {call updateRoleMenu(#{roleId,jdbcType=INTEGER, mode=IN}, #{ids,jdbcType=VARCHAR, mode=IN})}
    </select>

5.通过节点拼接所需要的数据结构(工具)

 /**
     * 通过数据列表,拼装成树结构数据
     * @param nodes 数据结合列表
     * @param <T>
     * @return
     */
    public static <T> List<TreeItem<T>> buildMenu(List<TreeItem<T>> nodes) {
        if (nodes == null) {
            return null;
        }
        List<TreeItem<T>> tree = new ArrayList<>();
        nodes.forEach(children -> {
            Integer pid = children.getParentId();
            if (pid==1) {
                //是父节点
                tree.add(children);
                return;
            }
            for (TreeItem<T> parent : nodes) {
                Integer id = parent.getId();
                if (id != null && id.equals(pid)) {
                    //说明是该节点是children的父节点
                    children.setHasParent(true);
                    parent.setHasChildren(true);
                    parent.getChildren().add(children);
                    return;
                }
            }
        });
        return tree;
    }
public class TreeItem<T> implements Serializable {

    /**
     * 节点ID
     */
    private Integer id;

    /**
     * 父节点ID
     */
    private Integer parentId;

    /**
     * 是否有子节点
     */
    private Boolean hasChildren;

    /**
     * 是否有父节点
     */
    private Boolean hasParent;

    /**
     * 路径
     */
    private String path;

    /**
     * 跳转路径
     */
    private String redirect;

    /**
     * 组件
     */
    private String component;

    /**
     * 名称
     */
    private String name;


    private Meta meta;

    /**
     * 子节点信息
     */
    private List<TreeItem<T>> children = new ArrayList<>();


}

6.返回的数据结构

MySQL通过自定义函数以及存储过程实现递归查询父级ID第1张

7:此外mysql存储过程如何遍历查询结构集(使用游标的方式)

-- 创建存储过程之前需判断该存储过程是否已存在,若存在则删除
DROP PROCEDURE IF EXISTS init_report;
-- 创建存储过程
CREATE PROCEDURE init_report(IN roleId int)
BEGIN
    -- 定义变量
    DECLARE s int DEFAULT 0;
    DECLARE id varchar(256);
    DECLARE str varchar(256);
#     set str = '';
    -- 定义游标,并将sql结果集赋值到游标中
    DECLARE report CURSOR FOR select distinct status from tmp_split;
    -- 声明当游标遍历完后将标志变量置成某个值
    DECLARE CONTINUE HANDLER FOR NOT FOUND SET s=1;
    -- 打开游标
    open report;
    -- 将游标中的值赋值给变量,注意:变量名不要和返回的列名同名,变量顺序要和sql结果列的顺序一致
    fetch report into id;
    -- 当s不等于1,也就是未遍历完时,会一直循环
    while s<>1 do
            -- 执行业务逻辑
            -- 当s等于1时表明遍历以完成,退出循环
        end while;
        update mps_role set role_render = str where role_id = roleId;
    -- 关闭游标
    close report;
END;

免责声明:文章转载自《MySQL通过自定义函数以及存储过程实现递归查询父级ID》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇iOS cocoapods升级及问题python原生结束线程的方法下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

CentOS7安装zabbix

二进制包安装    centos 7 添加阿里云镜像 wget -O /etc/yum.repos.d/CentOS-Base.repo http://mirrors.aliyun.com/repo/Centos-7.repo yum install epel-release 安装常用的开发组件 yum groups install "Develo...

模块三 GO语言实战与应用-并发安全字典sync.Map(上)

在前面,我几乎已经把 Go 语言自带的同步工具全盘托出了。你是否已经听懂了会用了呢? 无论怎样,我都希望你能够多多练习、多多使用。它们和 Go 语言独有的并发编程方式并不冲突,相反,配合起来使用,绝对能达到“一加一大于二”的效果。 当然了,至于怎样配合就是一门学问了。我在前面已经讲了不少的方法和技巧,不过,更多的东西可能就需要你在实践中逐渐领悟和总结了。...

PostgreSQL 字符串操作函数 迎客

函数:string || string 说明:String concatenation 字符串连接操作例子:'Post' || 'greSQL' = PostgreSQL 函数:string || non-string or non-string || string说明:String concatenation with one non-string i...

JAVA Swing开发单机版项目

一、序   最近公司做的项目里出现了一个新的需求,项目大部分是为金融业定制开发的数据集成平台,包括数据的采集,处理,使用。   数据的采集方式不固定,有机构化数据,有非结构话数据,还有附件等其它文件形式。   对于采集端,大部分要求具备硬件服务器架设能力,这时就出现了一个问题,有些采集端是不具备硬件服务器架设能力的,或者主观上不愿意架设,   要求公司拿...

VB 的字符串处理函数

一、InStr 返回 Variant (Long),指定一字符串在另一字符串中最先出现的位置。 语法 InStr([start, ]string1, string2[, compare]) start 可选参数。为数值表达式,设置每次搜索的起点。如果省略,将从第一个字符的位置开始。如果 start 包含 Null,将发生错误。如果指定了 compar...

EasySharding.EFCore 如何设计使用一套代码完成的EFCore Migration 构建Saas系统多租户不同业务需求且满足租户自定义分库分表、数据迁移能力?

下面用一篇文章来完成这些事情 多租户系统的设计单纯的来说业务,一套Saas多租户的系统,面临很多业务复杂性,不同的租户存在不同的业务需求,大部分相同的表结构,那么如何使用EFCore来完成这样的设计呢?满足不同需求的数据库结构迁移 这里我准备设计一套中间件来完成大部分分库分表的工作,然后可以通过自定义的Migration 数据库文件来迁移构建不同的租户数据...