MySQL通過自定義函數以及存儲過程實現遞歸查詢父級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.返回的數據結構

 

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;

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM