What you are looking is simply not facilitated by MySQL as of writing this answer. Common Table Expressions (CTEs) are supported by several databases. @a_horse_with_no_name posted an answer naming those RDBMSs.
I have posted recursive Stored Procedures: Find highest level of a hierarchical field: with vs without CTEs. However, you emphasized not using any Stored Procedures. To do what you are asking does require Stored Procedures in MySQL.
Compromise Solution...
You will still need a Stored Procedure, but not using recursion. Using a simple loop, you can construct a self-join query and executed it dynamically. Given the depth of the tree you are looking for, here is such a Stored Procedure:
DELIMITER $$
DROP PROCEDURE IF EXISTS `dianuj`.`GetTopParentGivenDepth` $$
CREATE PROCEDURE `dianuj`.`GetTopParentGivenDepth` (GivenDepth INT)
BEGIN
DECLARE x1,x2 INT;
SET x = 0;
SET y = 1;
SET @SQ = 'SELECT DISTINCT A0.id FROM prarent A0';
WHILE y < GivenDepth DO
SET @SQ = CONCAT(@SQ,' INNER JOIN prarent A',y,' ON A',x,'.id = A',y,'.parent_id');
SET x = y;
SET y = x + 1;
END WHILE;
SET @SQ = CONCAT(@SQ,' WHERE A0.parent_id = 0');
SELECT @SQ;
PREPARE stmt FROM @SQ;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
END $$
DELIMITER ;
After loading your data into MySQL on my desktop, I ran the Stored Procedure with...
Depth 1
mysql> call GetTopParentGivenDepth(1);
+--------------------------------------------------------------+
| @SQLSTMT |
+--------------------------------------------------------------+
| SELECT DISTINCT A0.id FROM prarent A0 WHERE A0.parent_id = 0 |
+--------------------------------------------------------------+
1 row in set (0.00 sec)
+----+
| id |
+----+
| 1 |
| 2 |
| 3 |
+----+
3 rows in set (0.00 sec)
Query OK, 0 rows affected (0.00 sec)
mysql>
Depth 2
mysql> call GetTopParentGivenDepth(2);
+------------------------------------------------------------------------------------------------------------+
| @SQLSTMT |
+------------------------------------------------------------------------------------------------------------+
| SELECT DISTINCT A0.id FROM prarent A0 INNER JOIN prarent A1 ON A0.id = A1.parent_id WHERE A0.parent_id = 0 |
+------------------------------------------------------------------------------------------------------------+
1 row in set (0.00 sec)
+----+
| id |
+----+
| 3 |
| 2 |
+----+
2 rows in set (0.01 sec)
Query OK, 0 rows affected (0.01 sec)
mysql>
Depth 3 .. 6
mysql> call GetTopParentGivenDepth(3);
+----------------------------------------------------------------------------------------------------------------------------------------------------------+
| @SQLSTMT |
+----------------------------------------------------------------------------------------------------------------------------------------------------------+
| SELECT DISTINCT A0.id FROM prarent A0 INNER JOIN prarent A1 ON A0.id = A1.parent_id INNER JOIN prarent A2 ON A1.id = A2.parent_id WHERE A0.parent_id = 0 |
+----------------------------------------------------------------------------------------------------------------------------------------------------------+
1 row in set (0.00 sec)
+----+
| id |
+----+
| 3 |
+----+
1 row in set (0.00 sec)
Query OK, 0 rows affected (0.00 sec)
mysql> call GetTopParentGivenDepth(4);
+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| @SQLSTMT |
+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| SELECT DISTINCT A0.id FROM prarent A0 INNER JOIN prarent A1 ON A0.id = A1.parent_id INNER JOIN prarent A2 ON A1.id = A2.parent_id INNER JOIN prarent A3 ON A2.id = A3.parent_id WHERE A0.parent_id = 0 |
+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
1 row in set (0.00 sec)
+----+
| id |
+----+
| 3 |
+----+
1 row in set (0.00 sec)
Query OK, 0 rows affected (0.00 sec)
mysql> call GetTopParentGivenDepth(5);
+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| @SQLSTMT |
+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| SELECT DISTINCT A0.id FROM prarent A0 INNER JOIN prarent A1 ON A0.id = A1.parent_id INNER JOIN prarent A2 ON A1.id = A2.parent_id INNER JOIN prarent A3 ON A2.id = A3.parent_id INNER JOIN prarent A4 ON A3.id = A4.parent_id WHERE A0.parent_id = 0 |
+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
1 row in set (0.01 sec)
+----+
| id |
+----+
| 3 |
+----+
1 row in set (0.01 sec)
Query OK, 0 rows affected (0.01 sec)
mysql> call GetTopParentGivenDepth(6);
+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| @SQLSTMT |
+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| SELECT DISTINCT A0.id FROM prarent A0 INNER JOIN prarent A1 ON A0.id = A1.parent_id INNER JOIN prarent A2 ON A1.id = A2.parent_id INNER JOIN prarent A3 ON A2.id = A3.parent_id INNER JOIN prarent A4 ON A3.id = A4.parent_id INNER JOIN prarent A5 ON A4.id = A5.parent_id WHERE A0.parent_id = 0 |
+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
1 row in set (0.00 sec)
Empty set (0.00 sec)
Query OK, 0 rows affected (0.01 sec)
mysql>
Notice that Depth 6
has no return value. To find the maximum depth, you would have to iterate on it until no rows comes back.
CAVEAT
- The worst case scenario would be to use the number of rows in the table as the depth. Such a tree would be nothing more than a degenerate linked list.
- I cannot give you any guarantees on query performance, such on SQL construction and subsequent execution. Any kind of recursion would be the sole responsibility of the Query Parser.
Give it a Try !!!
The following query:
SELECT m1.id, m1.coa, IFNULL(sum(t.debit), 0) as debit,
IFNULL(sum(t.credit), 0) as credit
FROM master m1
JOIN master m2
ON m2.coa like CONCAT(m1.coa, '%')
LEFT JOIN transaction t
ON m2.id = t.id
GROUP BY m1.id;
Returns the following for me:
+----+------+-------+--------+
| id | coa | debit | credit |
+----+------+-------+--------+
| 1 | 1 | 30 | 30 |
| 2 | 11 | 0 | 25 |
| 3 | 111 | 0 | 25 |
| 4 | 12 | 30 | 5 |
| 5 | 121 | 30 | 0 |
| 6 | 122 | 0 | 5 |
+----+------+-------+--------+
6 rows in set (0.00 sec)
Please note that all transactions are or have 1 as parent, so I am not sure why you didn't add up your third transaction.
My query returns 1 row for each id, even if it has not transactions (with credit and debit 0). Change the LEFT JOIN to a JOIN and get rid of the IFNULLs if you only want ids with movements.
Also please be aware that this query will be very badly improved in performance with indexes, and that I would recommend rethinking an alternative structure if the query is frequent and any of the two tables is very tall.
Best Answer
A long, long time ago (Oct 24, 2011), in a galaxy far away, someone boldly asked
Find highest level of a hierarchical field: with vs without CTEs
In my answer to that post, I wrote three stored procedures to find specific relationships
GetParentIDByID
GetAncestry
GetFamilyTree
If you use the code in
GetFamilyTree
, just pick the nth member of the output.I have referred others to that post
Jan 31, 2014
: Recursive Query in MySQL using stored proceedure and CURSORJul 11, 2013
: Recursive self joinsDec 10, 2012
: MySQL: Tree-Hierarchical queryGive it a Try !!!