Question

I've searched and tried for long hours, and can't seem to get what I need. I have a table of accounts as a tree -parents and children-, only leaf accounts can have transactions, any parent will NOT have debits and credits itself.

My fiddle of data is here

What I need is to show sum of "Debits of children" and sum of "Credits of children" in their parent's row, like this picture:

enter image description here

How can I do it in MySql?

Thank you

Was it helpful?

Solution

With your example, you can run

CREATE PROCEDURE procedure_name()
BEGIN
        DECLARE finished INTEGER DEFAULT 0;
        DECLARE _accoujntno INTEGER DEFAULT 0;
        DECLARE _uacn varchar(191) DEFAULT "";
    DEClARE curaccount 
        CURSOR FOR 
            Select account_no ,user_account_number FROM TBL1 WHERE length(user_account_number)-length(replace(user_account_number,'-',''))+1  = 2 order by user_account_number;

    -- declare NOT FOUND handler
    DECLARE CONTINUE HANDLER 
        FOR NOT FOUND SET finished = 1;
        
        CREATE TEMPORARY TABLE credits(
        user_account_number VARCHAR(191),
        `Debit_Before_Period` DECIMAL(20,2),
         `Credit_Before_Period` DECIMAL(20,2),
         `Debit_In_Period` DECIMAL(20,2),
         `Credit_In_Period` DECIMAL(20,2)
        );
        
        OPEN curaccount;

    getaccount: LOOP
        FETCH curaccount INTO _accoujntno,_uacn;
        IF finished = 1 THEN 
            LEAVE getaccount;
        END IF;
                INSERT INTO credits SELECT _uacn ,SUM(`Debit Before Period`),SUM(`Credit Before Period`) ,SUM(`Debit In Period`) ,SUM(`Credit In Period`)  
                FROM TBL1 
                WHERE LEFT(user_account_number,7) = _uacn AND account_no <> _accoujntno;
    END LOOP getaccount;
    CLOSE curaccount;

   SELECT * FROM credits;
END 

The resulting temporary table credits you can use to run further analysis

simply add the procedure and CALL procedure_name()

In a query tab in phmyadmin wor mysql Workbench you need to add DELIMITER to the procedure, but that doesn't work in dbfiddle

example

It is like in the comment

CREATE PROCEDURE procedure_name()
BEGIN
        DECLARE finished INTEGER DEFAULT 0;
        DECLARE _accoujntno INTEGER DEFAULT 0;
        DECLARE _uacn varchar(191) DEFAULT "";
    DEClARE curaccount 
        CURSOR FOR 
            Select account_no ,user_account_number FROM TBL1 WHERE length(user_account_number)-length(replace(user_account_number,'-',''))+1  = 1 order by user_account_number;

    -- declare NOT FOUND handler
    DECLARE CONTINUE HANDLER 
        FOR NOT FOUND SET finished = 1;
        
        CREATE TEMPORARY TABLE credits(
        user_account_number VARCHAR(191),
        `Debit_Before_Period` DECIMAL(20,2),
         `Credit_Before_Period` DECIMAL(20,2),
         `Debit_In_Period` DECIMAL(20,2),
         `Credit_In_Period` DECIMAL(20,2)
        );
        
        OPEN curaccount;

    getaccount: LOOP
        FETCH curaccount INTO _accoujntno,_uacn;
        IF finished = 1 THEN 
            LEAVE getaccount;
        END IF;
                INSERT INTO credits SELECT _uacn ,SUM(`Debit Before Period`),SUM(`Credit Before Period`) ,SUM(`Debit In Period`) ,SUM(`Credit In Period`)  
                FROM TBL1 
                WHERE LEFT(user_account_number,3) = _uacn;
    END LOOP getaccount;
    CLOSE curaccount;

   SELECT * FROM credits;
END 

new example

OTHER TIPS

Based on the accepted answer from @nbk, I made a small change in the sql code to fit my case. I Hope this is useful for others:

-- HELPER SP 

DELIMITER //
DROP PROCEDURE IF EXISTS sumChildrenAccounts//
CREATE PROCEDURE sumChildrenAccounts(
  _Level SMALLINT
)
BEGIN
  DECLARE finished INTEGER DEFAULT 0;
  DECLARE _accoujntno INTEGER DEFAULT 0;
  DECLARE _uacn varchar(191) DEFAULT "";
    DEClARE curaccount CURSOR FOR 
            SELECT account_no ,user_account_number FROM VIEW2 WHERE length(user_account_number)-length(replace(user_account_number,'-',''))+1  = _Level ORDER BY user_account_number;

    -- declare NOT FOUND handler
    DECLARE CONTINUE HANDLER 
  FOR NOT FOUND SET finished = 1;
  
  CREATE TEMPORARY TABLE TOTALS_BY_LEVEL(
          user_account_number VARCHAR(191),
          `Debit Before Period`   DECIMAL(20,2),
          `Credit Before Period` DECIMAL(20,2),
          `Debit In Period`      DECIMAL(20,2),
          `Credit In Period`      DECIMAL(20,2)
  );
  OPEN curaccount;

  getaccount: LOOP
      FETCH curaccount INTO _accoujntno,_uacn;
      IF finished = 1 THEN 
          LEAVE getaccount;
      END IF;
      INSERT INTO TOTALS_BY_LEVEL SELECT _uacn ,SUM(`Debit Before Period`),SUM(`Credit Before Period`) ,SUM(`Debit In Period`) ,SUM(`Credit In Period`)  
      FROM VIEW2 
      WHERE LEFT(user_account_number,  (_level -1 + (_level * 3))) = _uacn;
    
  END LOOP getaccount;
    CLOSE curaccount;
  
  -- update parents' totals in the same source table (where parents had nulls actually)
  UPDATE TOTALS_BY_LEVEL b
    INNER JOIN VIEW2 a ON  b.user_account_number = a.user_account_number
    SET 
      a. `Debit Before Period`       = b. `Debit Before Period` ,
      a. `Credit Before Period`      = b. `Credit Before Period`,
      a. `Debit In Period`           = b. `Debit In Period`     ,
      a. `Credit In Period`          = b. `Credit In Period`    ;
  DROP TABLE IF EXISTS TOTALS_BY_LEVEL;

END//
DELIMITER ;

Then I called it in a loop inside the original stored procedure:

-- loop from the 1 until the deepest level of child accounts
SET @ml := 1;
SELECT max(level) FROM acc_accounts LIMIT 1 INTO @ml; 

SET @x = 1;
WHILE @x <= @ml DO
    call sumChildrenAccounts(@x);
    SET @x := @x + 1;
END WHILE;
Licensed under: CC-BY-SA with attribution
Not affiliated with dba.stackexchange
scroll top