Checkout the following query:
Schema (MySQL v5.7)
create table tbl_budget (
`id` INT ,
`budget_code` VARCHAR(255),
`level` INT
);
INSERT INTO `tbl_budget` VALUES
(1, '001', 1),
(2, '001-1', 2),
(3, '001-2', 2),
(4, '001-2-1', 3),
(5, '002', 1),
(6, '002-1', 2),
(7, '002-2', 2);
Query
SELECT
@count_dash := LENGTH(tb1.budget_code) - LENGTH(REPLACE(tb1.budget_code, '-', '')) as count_dash,
tb1.id,
tb1.budget_code,
SUBSTRING_INDEX(tb1.budget_code, '-', -1) as child_part,
SUBSTRING_INDEX(tb1.budget_code, '-', @count_dash) as parent_part,
(
SELECT id
FROM tbl_budget
WHERE
`level` = tb1.level-1 AND parent_part = `budget_code`
LIMIT 1
) as parent_id
FROM tbl_budget tb1;
| count_dash | child_part | parent_part | parent_id | id | budget_code |
| ---------- | ---------- | ----------- | --------- | --- | ----------- |
| 0 | 001 | | | 1 | 001 |
| 1 | 1 | 001 | 1 | 2 | 001-1 |
| 1 | 2 | 001 | 1 | 3 | 001-2 |
| 2 | 1 | 001-2 | 3 | 4 | 001-2-1 |
| 0 | 002 | | | 5 | 002 |
| 1 | 1 | 002 | 5 | 6 | 002-1 |
| 1 | 2 | 002 | 5 | 7 | 002-2 |
Explanation:
I am assuming that there is naming consistency in your data. The issue is that to get the parent of an entry you dont only need the level
but also part of the code to be matching
Step 1:
The following counts the number of '-' in the budget_code
:
@count_dash := LENGTH(tb1.budget_code) - LENGTH(REPLACE(tb1.budget_code, '-', ''))
Step 2:
Now, we split the budget_code
by '-' and we return:
- the last part (
-1
) which is the child code
- all the parts but not the last, which is the parent code
This is done using SUBSTRING_INDEX which splits the given string by that character and returns the number of parts requested
Step 3:
Since each code has a parent we join and return the id of the parent. To find the correct one we need the following to be true:
- The parents' level is equal to our level -1
- The parents' budget code equals our parent-code part
View on DB Fiddle
Method 2
The above shows you how to get the parent code (even without join). However, your query can be simplified to the following:
SELECT
tb1.id,
tb1.budget_code,
(
SELECT id
FROM tbl_budget
WHERE
`level` = tb1.level-1
AND tb1.budget_code LIKE CONCAT(budget_code, '%')
LIMIT 1
) as parent_id
FROM tbl_budget tb1;
In this case we are saying: Find an entry, one level above the current one and whose budget_code is included the current one. Note that you can change parent_id to parent_code, however if code is what is required I would avoid the join