一、根據用戶ID獲取所有子節點,通過存儲過程來實現
CREATE DEFINER=`crmeb`@`%` PROCEDURE `proc_get_user_all_children`(
? ? IN rootUid INTEGER, ? ? ? ? ? ? ?-- 要查詢的根用戶ID
? ? IN includeSelf BOOLEAN ? ? ?-- 是否包含自身(1=包含,0=不包含)
)
BEGIN
? ??
? ? -- 聲明變量?
? ? DECLARE current_level INT DEFAULT 0;
? ??
? ? -- 創建臨時表存儲最終結果
? ? DROP TEMPORARY TABLE IF EXISTS final_result;
? ? CREATE TEMPORARY TABLE final_result (
? ? ? ? id INT PRIMARY KEY,
? ? ? ? level INT COMMENT '層級深度',
? ? ? ? spread_uid INT COMMENT '直接上級ID'
? ? ) ENGINE=InnoDB;
? ??
? ? -- 創建臨時表存儲當前層級的用戶?
? ? DROP TEMPORARY TABLE IF EXISTS current_level_users;
? ? CREATE TEMPORARY TABLE current_level_users (
? ? ? ? id INT PRIMARY KEY
? ? ) ENGINE=Memory;
? ??
? ? -- 創建臨時表存儲下一層級的用戶?
? ? DROP TEMPORARY TABLE IF EXISTS next_level_users;
? ? CREATE TEMPORARY TABLE next_level_users (
? ? ? ? id INT PRIMARY KEY,
? ? ? ? spread_uid INT
? ? ) ENGINE=Memory;
? ??
? ? -- 初始化:添加根節點(如果選擇包含自身)
? ? IF includeSelf THEN
? ? ? ? INSERT INTO final_result?
? ? ? ? SELECT id, 0, spread_uid?
? ? ? ? FROM eb_user?
? ? ? ? WHERE id = rootUid;
? ? END IF;
? ??
? ? -- 初始化當前層級(根節點的直接下級)
? ? INSERT INTO current_level_users
? ? SELECT id?
? ? FROM eb_user?
? ? WHERE spread_uid = rootUid AND id != IF(includeSelf, -1, rootUid);
? ??
? ? -- 將直接下級加入結果集?
? ? INSERT INTO final_result?
? ? SELECT u.id, ?1, u.spread_uid?
? ? FROM eb_user u
? ? WHERE u.spread_uid ?= rootUid AND u.id ?!= IF(includeSelf, -1, rootUid);
? ??
? ? -- 循環處理每一層級(廣度優先遍歷)
? ? WHILE EXISTS (SELECT 1 FROM current_level_users) DO
? ? ? ? SET current_level = current_level + 1;
? ? ? ??
? ? ? ? -- 清空下一層級臨時表
? ? ? ? TRUNCATE TABLE next_level_users;
? ? ? ??
? ? ? ? -- 查找當前層級用戶的直接下級
? ? ? ? INSERT INTO next_level_users?
? ? ? ? SELECT u.id, ?u.spread_uid ?
? ? ? ? FROM eb_user u?
? ? ? ? JOIN current_level_users c ON u.spread_uid ?= c.id?
? ? ? ? WHERE u.id ?NOT IN (SELECT id FROM final_result);
? ? ? ??
? ? ? ? -- 將新找到的子節點添加到結果表?
? ? ? ? INSERT INTO final_result?
? ? ? ? SELECT id, current_level + 1, spread_uid?
? ? ? ? FROM next_level_users;
? ? ? ??
? ? ? ? -- 準備處理下一層級
? ? ? ? TRUNCATE TABLE current_level_users;
? ? ? ? INSERT INTO current_level_users
? ? ? ? SELECT id FROM next_level_users;
? ? END WHILE;
? ??
? ? -- 返回最終結果
? ? SELECT?
? ? ? ? r.id,?
? ? ? ? u.real_name, ? ? ? ? ? ? ? -- 假設表中有real_name字段
? ? ? ? r.level,?
? ? ? ? r.spread_uid,?
? ? ? ? p.real_name ?AS parent_name -- 上級用戶名
? ? FROM final_result r
? ? JOIN eb_user u ON r.id ?= u.id?
? ? LEFT JOIN eb_user p ON r.spread_uid ?= p.id ?
? ? ORDER BY r.level, ?r.id;?
? ??
? ? -- 清理臨時表?
? ? DROP TEMPORARY TABLE IF EXISTS final_result;
? ? DROP TEMPORARY TABLE IF EXISTS current_level_users;
? ? DROP TEMPORARY TABLE IF EXISTS next_level_users;
END
測試結果,如下:
二、根據用戶ID獲取用戶所有上級直屬節點,用存儲過程實現,如下:
?
CREATE PROCEDURE sp_get_user_parent_nodes(
? ? IN base_user_id INT, ? ? ? -- 要查詢的基礎用戶ID?
? ? IN include_self BOOLEAN ? ?-- 是否包含自己(1=包含,0=不包含)
)
BEGIN?
? ??
? ? -- 創建臨時結果表(優化結構)
? ? DROP TEMPORARY TABLE IF EXISTS user_parent_nodes;
? ? CREATE TEMPORARY TABLE user_parent_nodes (
? ? ? ? node_level INT NOT NULL COMMENT '節點層級(0=自己,1=直接上級...)',
? ? ? ? user_id INT NOT NULL,
? ? ? ? real_name VARCHAR(100),
? ? ? ? parent_id INT,
? ? ? ? is_root_node BOOLEAN DEFAULT FALSE,
? ? ? ? PRIMARY KEY (user_id),
? ? ? ? KEY idx_level (node_level)
? ? ) ENGINE=InnoDB;
? ??
? ? -- 變量初始化?
? ? SET @current_user := base_user_id;
? ? SET @current_level := 0;
? ? SET @continue := 1;
? ??
? ? -- 如果包含自己,先添加自己?
? ? IF include_self THEN?
? ? ? ? INSERT INTO user_parent_nodes?
? ? ? ? SELECT?
? ? ? ? ? ? 0 AS node_level,
? ? ? ? ? ? id AS user_id,
? ? ? ? ? ? real_name,
? ? ? ? ? ? spread_uid AS parent_id,
? ? ? ? ? ? CASE WHEN spread_uid IS NULL THEN TRUE ELSE FALSE END AS is_root_node?
? ? ? ? FROM eb_user?
? ? ? ? WHERE id = base_user_id;
? ? END IF;
? ??
? ? -- 使用WHILE循環向上追溯?
? ? WHILE @continue = 1 DO?
? ? ? ? -- 獲取當前用戶的直接上級信息?
? ? ? ? INSERT INTO user_parent_nodes?
? ? ? ? SELECT?
? ? ? ? ? ? @current_level + 1 AS node_level,
? ? ? ? ? ? parent.id ?AS user_id,
? ? ? ? ? ? parent.real_name,?
? ? ? ? ? ? parent.spread_uid ?AS parent_id,
? ? ? ? ? ? CASE WHEN parent.spread_uid ?IS NULL THEN TRUE ELSE FALSE END AS is_root_node?
? ? ? ? FROM eb_user current?
? ? ? ? JOIN eb_user parent ON current.spread_uid ?= parent.id ?
? ? ? ? WHERE current.id ?= @current_user?
? ? ? ? ON DUPLICATE KEY UPDATE node_level = LEAST(node_level, @current_level + 1);
? ? ? ??
? ? ? ? -- 檢查是否插入成功?
? ? ? ? IF ROW_COUNT() = 0 THEN?
? ? ? ? ? ? -- 沒有上級,標記當前用戶為根節點(如果是第一次處理)
? ? ? ? ? ? UPDATE user_parent_nodes?
? ? ? ? ? ? SET is_root_node = TRUE?
? ? ? ? ? ? WHERE user_id = @current_user AND is_root_node = FALSE;
? ? ? ? ? ??
? ? ? ? ? ? SET @continue := 0;
? ? ? ? ELSE?
? ? ? ? ? ? -- 獲取新插入的上級ID?
? ? ? ? ? ? SELECT user_id INTO @current_user?
? ? ? ? ? ? FROM user_parent_nodes?
? ? ? ? ? ? WHERE node_level = @current_level + 1?
? ? ? ? ? ? LIMIT 1;
? ? ? ? ? ??
? ? ? ? ? ? -- 檢查是否已存在(防止循環引用)
? ? ? ? ? ? IF @current_user IN (
? ? ? ? ? ? ? ? SELECT user_id FROM user_parent_nodes?
? ? ? ? ? ? ? ? WHERE node_level < @current_level + 1?
? ? ? ? ? ? ) THEN?
? ? ? ? ? ? ? ? SET @continue := 0;
? ? ? ? ? ? END IF;
? ? ? ? ? ??
? ? ? ? ? ? SET @current_level := @current_level + 1;
? ? ? ? END IF;
? ? END WHILE;
? ??
? ? -- 返回最終結果(按層級排序)
? ? SELECT?
? ? ? ? node_level AS '層級',
? ? ? ? user_id AS '用戶ID',
? ? ? ? real_name AS '用戶名',
? ? ? ? CASE?
? ? ? ? ? ? WHEN node_level = 1 THEN '直接上級'
? ? ? ? ? ? WHEN is_root_node THEN '頂級節點'
? ? ? ? ? ? ELSE CONCAT('第', node_level, '級上級')
? ? ? ? END AS '關系類型',
? ? ? ? parent_id AS '下級用戶ID'
? ? FROM user_parent_nodes?
? ? WHERE node_level > 0 OR include_self = 1?
? ? ORDER BY node_level;
? ??
? ? -- 清理臨時表?
? ? DROP TEMPORARY TABLE IF EXISTS user_parent_nodes;
END?
測試結果,如下: