我正在尝试制作一个递归地为特定类别构建路径的函数
CREATE FUNCTION getPath(inId INT)
RETURNS TEXT
DETERMINISTIC
BEGIN
DECLARE return_path TEXT;
DECLARE return_parent_id INT;
SELECT CONCAT('/', name) INTO return_path FROM article_categories WHERE id = inId;
SELECT parent_id INTO return_parent_id FROM article_categories WHERE id = inId;
IF return_parent_id > 0 THEN
SELECT CONCAT(getPath(return_parent_id), return_path) INTO return_path;
END IF;
RETURN return_path;
END
当我尝试使用没有父级(parent_id = 0)的类别运行此函数时,它工作正常但是当我尝试具有 parent_id > 0 的类别时,我得到 1424 Recursive stored functions and triggers are not allowed.
我该如何解决这个问题?我将在常规网络托管服务上托管此代码,该服务至少应具有 MySQL 服务器版本 5.1.
在 Ike Walker 的帮助下,我做了一个程序,但效果很好
DROP PROCEDURE IF EXISTS getPath;
DELIMITER //
CREATE PROCEDURE getPath(IN category_id INT UNSIGNED, OUT return_path TEXT)
BEGIN
DECLARE parent_id INT UNSIGNED;
DECLARE path_result TEXT;
SET max_sp_recursion_depth=50;
SELECT CONCAT('/', ac.name), ac.parent_id INTO return_path, parent_id FROM article_categories AS ac WHERE ac.id = category_id;
IF parent_id > 0 THEN
CALL getPath(parent_id, path_result);
SELECT CONCAT(path_result, return_path) INTO return_path;
END IF;
END //
DELIMITER ;
然后我用这样的东西来称呼它
CALL getPath(72, @temp); SELECT @temp;
MySQL 不允许递归函数,即使您设置了 max_sp_recursion_depth。
如果设置 max_sp_recursion_depth,它确实允许在一个过程中最多进行 255 次递归。
所以我建议你用一个过程替换你的函数,使用一个 INOUT 变量作为 return_path。
在您问题中的存储过程中,*在@Ike Walker 的帮助下,
DROP PROCEDURE IF EXISTS getPath;
DELIMITER $$
CREATE PROCEDURE getPath(IN category_id INT UNSIGNED, OUT return_path TEXT)
BEGIN
DECLARE parent_id INT UNSIGNED;
DECLARE path_result TEXT;
SET max_sp_recursion_depth=50;
SELECT CONCAT('/', ac.name), ac.parent_id INTO return_path, parent_id FROM article_categories AS ac WHERE ac.id = category_id;
IF parent_id > 0 THEN
CALL getPath(parent_id, path_result);
SELECT CONCAT(path_result, return_path) INTO return_path;
END IF;
END $$
DELIMITER ;
创建函数:
DROP FUNCTION IF EXISTS getPath;
CREATE FUNCTION getPath(category_id INT) RETURNS TEXT DETERMINISTIC
BEGIN
DECLARE res TEXT;
CALL getPath(category_id, res);
RETURN res;
END$$
接下来,您可以选择:
SELECT category_id, name, getPath(category_id) AS path FROM article_categories ;
使用 MySQL 8.0,您还有另一种可能的方法。 您可以将递归 CTE 包装到您的函数中:
DELIMITER $$
DROP FUNCTION IF EXISTS getPath $$
CREATE FUNCTION getPath(inId INT)
RETURNS TEXT
DETERMINISTIC
BEGIN
DECLARE result TEXT;
WITH RECURSIVE cte_cat(id, parent_id, output_name) AS (
SELECT ac0.id, ac0.parent_id, CONCAT('/',ac0.`name`)
FROM article_categories ac0
WHERE ac0.id = inId
UNION ALL
SELECT ac.id, ac.parent_id, CONCAT('/',ac.`name`,cte.output_name)
FROM cte_cat cte
INNER JOIN article_categories ac ON ac.id = cte.parent_id
)
SELECT output_name INTO result FROM cte_cat WHERE parent_id IS NULL;
RETURN result;
END$$
DELIMITER ;
这是我的测试表:
CREATE TABLE `article_categories` (
`id` INT NOT NULL,
`parent_id` INT NULL,
`name` VARCHAR(45) NULL,
PRIMARY KEY (`id`));
INSERT INTO `article_categories`(`id`,`parent_id`,`name`) VALUES(1,NULL,'A');
INSERT INTO `article_categories`(`id`,`parent_id`,`name`) VALUES(2,1,'A1');
INSERT INTO `article_categories`(`id`,`parent_id`,`name`) VALUES(3,2,'A1a');
INSERT INTO `article_categories`(`id`,`parent_id`,`name`) VALUES(4,1,'A2');
INSERT INTO `article_categories`(`id`,`parent_id`,`name`) VALUES(5,NULL,'B');