I have some calculation and want to do this in the query.
There is parent and children tables with one-to-many relationship:
CREATE TABLE `parent` (
`id` int NOT NULL AUTO_INCREMENT,
`value` decimal(10,2) DEFAULT NULL,
PRIMARY KEY (`id`)
);
CREATE TABLE `children` (
`id` int NOT NULL AUTO_INCREMENT,
`parent_id` int NOT NULL,
`multiple` decimal(10,2) DEFAULT NULL,
`sum` decimal(10,2) DEFAULT NULL,
PRIMARY KEY (`id`),
CONSTRAINT `fk_parent` FOREIGN KEY (`parent_id`) REFERENCES `parent` (`id`)
);
For find the final value of parent I should iterate in children and calculate following formula:
newParentValue = childMultiple(parentValue + childSum)
The implementation in the code is as follows:
function calculateFinalParentValue($parentValue, $children)
{
foreach ($children as $child) {
$parentValue = $child['multiple'] * ($parentValue + $child['sum']);
}
return $parentValue;
}
How can I implement the calculation in the query?
I try this way (Using temporary variable):
set @value = 0;
SELECT
p.id,
@value := (c.multiple * (@value + c.sum)) AS value
FROM
parent p
JOIN
children c ON p.id = c.parent_id AND @value := p.value;
I set the variable in the join condition (@value := p.value)
to reset the variable for each new parent.
This query returns rows for each parent with the number of children, and I need the last row in join for each parent as the answer.
But this way is not sustainable, Is there a better way?
Example:
mysql> select * from parent;
+----+-------+
| id | value |
+----+-------+
| 1 | 10.00 |
| 2 | 20.00 |
+----+-------+
mysql> select * from children;
+----+-----------+----------+------+
| id | parent_id | multiple | sum |
+----+-----------+----------+------+
| 1 | 1 | 1.00 | 1.00 |
| 2 | 1 | 1.00 | 1.00 |
| 3 | 1 | 1.00 | 1.00 |
| 4 | 2 | 2.00 | 2.00 |
| 5 | 2 | 2.00 | 2.00 |
+----+-----------+----------+------+
With the above data, I expect following answer:
+----+--------+
| id | value |
+----+--------+
| 1 | 11.00 |
| 1 | 12.00 |
| 1 | 13.00 | <- final value for parant.id = 1
| 2 | 44.00 |
| 2 | 92.00 | <- final value for parant.id = 2
+----+--------+
For parent.id=1 there is three children and parent.value is 10, so after calculate formula for first child new value is 1 * (10 + 1) = 11
and after second child value is 1 * (11 + 1) = 12
as expected after third child value is 1 * (12 + 1) = 13
(In all three children multiple and sum is equal to 1).
For parent.id=2 there is two children and parent.value is 20, so after calculate formula for first child new value is 2 * (20 + 2) = 44
and after second child value is 2 * (44 + 2) = 92
(In both two children multiple and sum is equal to 2).
And finally I want only final value for each parent so my final expected result is:
+----+--------+
| id | value |
+----+--------+
| 1 | 13.00 |
| 2 | 92.00 |
+----+--------+
4
Answers
In SQL this would look like:
To get the expected output, we need first identify the last child per parent and then join it with both the parent and children tables:
A little tricky as you have to reset your value in between when the parents get changed.
Try the following query:
You can also replace above mentioned
CASE
statements withIF
(a little bit more readable)It should give you your desired output.
I have used two variables
@current_parent_value
and@running_parent
The
@running_parent
will help you determine whether the previous row and current row belong to sameparent
and the@current_parent_value
will help you store your current running value.Use
ROW_NUMBER()
window function to rank the rows ofchildren
partitioned byparent_id
and ordered byid
andSUM()
window function to get the sum that you want:See the demo.