MySQL 5.x/5.5/5.6/5.7 均不支援標準 WITH RECURSIVE(8.0+ 才支援),需自寫存儲函數模擬遞歸遍歷。本文提供子節點、父節點兩個通用函數。
CREATE TABLE user_role (
id INT PRIMARY KEY,
parent_id INT,
name VARCHAR(50)
);
CREATE FUNCTION `getChildList`(rootId INT)
RETURNS VARCHAR(1000)
DETERMINISTIC
READS SQL DATA
BEGIN
DECLARE sChildList VARCHAR(1000) DEFAULT '';
DECLARE sChildTemp VARCHAR(1000);
SET sChildTemp = CAST(rootId AS CHAR);
WHILE sChildTemp IS NOT NULL DO
IF sChildList <> '' THEN
SET sChildList = CONCAT(sChildList, ',', sChildTemp);
ELSE
SET sChildList = sChildTemp;
END IF;
SELECT GROUP_CONCAT(id) INTO sChildTemp
FROM user_role
WHERE FIND_IN_SET(parent_id, sChildTemp) > 0;
END WHILE;
RETURN sChildList;
END;
-- 1. 直接獲取 ID 串
SELECT getChildList(0) AS ids;
-- 2. 關聯查詢完整信息
SELECT * FROM user_role
WHERE FIND_IN_SET(id, getChildList(2));
CREATE FUNCTION `getParentList`(rootId INT)
RETURNS VARCHAR(1000)
DETERMINISTIC
READS SQL DATA
BEGIN
DECLARE sParentList VARCHAR(1000) DEFAULT '';
DECLARE sParentTemp VARCHAR(1000);
SET sParentTemp = CAST(rootId AS CHAR);
WHILE sParentTemp IS NOT NULL DO
IF sParentList <> '' THEN
SET sParentList = CONCAT(sParentTemp, ',', sParentList);
ELSE
SET sParentList = sParentTemp;
END IF;
SELECT GROUP_CONCAT(parent_id) INTO sParentTemp
FROM user_role
WHERE FIND_IN_SET(id, sParentTemp) > 0;
END WHILE;
RETURN sParentList;
END;
SELECT getParentList(6) AS ids;
SELECT * FROM user_role
WHERE FIND_IN_SET(id, getParentList(2));
VARCHAR 存「當前層級 ID 列表」,循環迭代。FIND_IN_SET(parent_id, current_list) 找下一層子節點;FIND_IN_SET(id, current_list) 找上一層父節點。GROUP_CONCAT 將多行 ID 合併為逗號串供下輪 FIND_IN_SET 使用。FIND_IN_SET 關聯。path VARCHAR(1000) 存 /1/5/12/,LIKE '/1/5/%' 查子樹。WITH RECURSIVE。函數返回長度受限於
group_concat_max_len(默認 1024),大樹需調大或改用閉包表。