对于那些没有 MySQL 8.0 或 Maria DB 并希望在 MySQL 5.7 中使用递归方法的人。 我只是希望你不必超过最大 rec.depth 255 manual:)
MySQL 不允许递归函数,但它允许递归过程。将它们结合起来,您可以拥有可以在任何选择命令中使用的不错的小功能。
递归 sp 将采用两个输入参数和一个输出。第一个输入是您在节点树中搜索的 ID,第二个输入由 sp 用于在执行期间保存结果。第三个参数是携带最终结果的输出参数。
CREATE DEFINER=`root`@`localhost` PROCEDURE `sp_list_relation_recursive`(
in itemId text,
in iPreserve text,
out oResult text
)
BEGIN
DECLARE ChildId text default null;
IF (coalesce(itemId,'') = '') then
-- when no id received retun whatever we have in the preserve container
set oResult = iPreserve;
ELSE
-- add the received id to the preserving container
SET iPreserve = concat_ws(',',iPreserve,itemId);
SET oResult = iPreserve;
SET ChildId =
(
coalesce(
(
Select
group_concat(TNode.child_id separator ',') -- get all children
from
list_relation as TNode
WHERE
not find_in_set(TNode.child_id, iPreserve) -- if we don't already have'em
AND find_in_set(TNode.parent_id, itemId) -- from these parents
)
,'')
);
IF length(ChildId) >0 THEN
-- one or more child found, recursively search again for further child elements
CALL sp_list_relation_recursive(ChildId,iPreserve,oResult);
END IF;
END IF;
-- uncomment this to see the progress looping steps
-- select ChildId,iPreserve,oResult;
END
测试一下:
SET MAX_SP_RECURSION_DEPTH = 250;
set @list = '';
call test.sp_list_relation_recursive(1,'',@list);
select @list;
+----------------+
| @list |
+----------------+
| ,1,2,3,6,4,4,5 |
+----------------+
不介意重复的父节点或额外的逗号,我们只想知道节点中是否存在元素,而不需要太多 if 和 whens。
目前看起来还不错,但是 SP 不能在 select 命令中使用,所以我们只为这个 sp 创建包装函数。
CREATE DEFINER=`root`@`localhost` FUNCTION `fn_list_relation_recursive`(
NodeId int
) RETURNS text CHARSET utf8
READS SQL DATA
DETERMINISTIC
BEGIN
/*
Returns a tree of nodes
branches out all possible branches
*/
DECLARE mTree mediumtext;
SET MAX_SP_RECURSION_DEPTH = 250;
call sp_list_relation_recursive(NodeId,'',mTree);
RETURN mTree;
END
现在检查一下:
SELECT
*,
FN_LIST_RELATION_RECURSIVE(parent_id) AS parents_children
FROM
list_relation;
+----------+-----------+------------------+
| child_id | parent_id | parents_children |
+----------+-----------+------------------+
| 1 | 7 | ,7,1,2,3,6,4,4,5 |
| 2 | 1 | ,1,2,3,6,4,4,5 |
| 3 | 1 | ,1,2,3,6,4,4,5 |
| 4 | 2 | ,2,4 |
| 4 | 3 | ,3,4,5 |
| 5 | 3 | ,3,4,5 |
| 6 | 1 | ,1,2,3,6,4,4,5 |
| 51 | 50 | ,50,51 |
+----------+-----------+------------------+
您的插入将如下所示:
insert into list_relation (child_id,parent_id)
select
-- child, parent
1,6
where
-- parent not to be foud in child's children node
not find_in_set(6,fn_list_relation_recursive(1));
1,6 应该添加 0 条记录。但是 1,7 应该可以工作。
一如既往,我只是在证明这个概念,非常欢迎你们
调整 sp 以返回父节点的子节点或子节点的父节点。或者为每个节点树设置两个单独的 SP,或者甚至将所有 SP 组合在一起,因此从单个 id 返回所有父节点和子节点。
试试吧..没那么难:)