mysqlrecursion调用自己的存储过程

我有下面的表格:

id | parent_id | quantity ------------------------- 1 | null | 5 2 | null | 3 3 | 2 | 10 4 | 2 | 15 5 | 3 | 2 6 | 5 | 4 7 | 1 | 9 

现在我需要一个存储过程在mysql中recursion调用,并返回计算的数量。 例如,id 6具有5作为父母,其中3作为具有2作为父母的父母。 所以我需要计算4 * 2 * 10 * 3 (= 240)。

我对存储过程相当陌生,将来不会经常使用它们,因为我更喜欢在程序代码中而不是在数据库中使用我的业务逻辑。 但在这种情况下,我无法避免它。

也许一个MySQL大师(就是你)可以在几秒钟内把一份工作声明拼凑在一起。

其工作只在mysql版本> = 5

存储过程声明是这样的,

你可以给它一点改善,但是这个工作:

 DELIMITER $$ CREATE PROCEDURE calctotal( IN number INT, OUT total INT ) BEGIN DECLARE parent_ID INT DEFAULT NULL ; DECLARE tmptotal INT DEFAULT 0; DECLARE tmptotal2 INT DEFAULT 0; SELECT parentid FROM test WHERE id = number INTO parent_ID; SELECT quantity FROM test WHERE id = number INTO tmptotal; IF parent_ID IS NULL THEN SET total = tmptotal; ELSE CALL calctotal(parent_ID, tmptotal2); SET total = tmptotal2 * tmptotal; END IF; END$$ DELIMITER ; 

调用就像(设置这个variables很重要):

 SET @@GLOBAL.max_sp_recursion_depth = 255; SET @@session.max_sp_recursion_depth = 255; CALL calctotal(6, @total); SELECT @total; 

看看Mike Hillyer 在MySQL中pipe理分层数据 。

它包含处理层次数据的完整工作示例。

如何避免程序:

 SELECT quantity from ( SELECT @rq:=parent_id as id, @val:=@val*quantity as quantity from ( select * from testTable order by -id limit 1000000 # 'limit' is required for MariaDB if we want to sort rows in subquery ) t # we have to inverse ids first in order to get this working... join ( select @rq:= 6 /* example query */, @val:= 1 /* we are going to multiply values */) tmp where id=@rq ) c where id is null; 

检查小提琴!

注意! 如果行的parent_id>id这将不起作用。

干杯!

 DELIMITER $$ CREATE DEFINER=`arun`@`%` PROCEDURE `recursivesubtree`( in iroot int(100) , in ilevel int(110) , in locid int(101) ) BEGIN DECLARE irows,ichildid,iparentid,ichildcount,done INT DEFAULT 0; DECLARE cname VARCHAR(64); SET irows = ( SELECT COUNT(*) FROM account WHERE parent_id=iroot and location_id=locid ); IF ilevel = 0 THEN DROP TEMPORARY TABLE IF EXISTS _descendants; CREATE TEMPORARY TABLE _descendants ( childID INT, parentID INT, name VARCHAR(64), childcount INT, level INT ); END IF; IF irows > 0 THEN BEGIN DECLARE cur CURSOR FOR SELECT f.account_id,f.parent_id,f.account_name, (SELECT COUNT(*) FROM account WHERE parent_id=t.account_id and location_id=locid ) AS childcount FROM account t JOIN account f ON t.account_id=f.account_id WHERE t.parent_id=iroot and t.location_id=locid ORDER BY childcount<>0,t.account_id; DECLARE CONTINUE HANDLER FOR SQLSTATE '02000' SET done = 1; OPEN cur; WHILE NOT done DO FETCH cur INTO ichildid,iparentid,cname,ichildcount; IF NOT done THEN INSERT INTO _descendants VALUES(ichildid,iparentid,cname,ichildcount,ilevel ); IF ichildcount > 0 THEN CALL recursivesubtree( ichildid, ilevel + 1 ); END IF; END IF; END WHILE; CLOSE cur; END; END IF; IF ilevel = 0 THEN -- Show result table headed by name that corresponds to iroot: SET cname = (SELECT account_name FROM account WHERE account_id=iroot and location_id=locid ); SET @sql = CONCAT('SELECT CONCAT(REPEAT(CHAR(36),2*level),IF(childcount,UPPER(name),name))', ' AS ', CHAR(39),cname,CHAR(39),' FROM _descendants'); PREPARE stmt FROM @sql; EXECUTE stmt; DROP PREPARE stmt; END IF; END$$ DELIMITER ;