I have some calculations and want to perform this in a query.
There are parent and child tables with a 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`) );
To find the final value of the parent, I should iterate over the children and calculate the 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 to implement calculations in queries?
I tried this way (using temporary variables):
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 the rows for each parent along with the number of children, I need the last row in each parent join as the answer.
But this method 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 | +----+-----------+----------+------+
Based on the above data, I expect the following answers:
+----+--------+ | 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 are three children and parent.value is 10, so after calculating the formula for the first child, the new value is 1 * (10 1) = 11
, No. The value of the second child is 1 * (11 1) = 12
As expected, the value of the third child is 1 * (12 1) = 13
(in all three children are all multiples, and the sum is equal to 1).
For parent.id=2, there are two children and parent.value is 20, so after calculating the formula of the first child, the new value is 2 * (20 2) = 44
, The value of the second child is 2 * (44 2) = 92
(both children are multiples and the sum equals 2).
In the end I just want the final value of each parent, so my final expected result is:
+----+--------+ | id | value | +----+--------+ | 1 | 13.00 | | 2 | 92.00 | +----+--------+
Just to simplify the example, all multiply
and sum
columns of each parent's child table are equal (assuming different values) and the final value is the maximum value, The final value may not be the maximum value every time. < /p>
Use
ROW_NUMBER()
window function to rank rows forchildren
, partitioned byparent_id
and divided byid
andSUM()
Window function sorts to obtain the desired sum.Finally use
FIRST_VALUE()
window function to get the last sum of each id:ViewDemo.
It's a little tricky because you have to reset your value in the middle when the parent changes.
Try the following query:
You can also replace the
CASE
statement mentioned above with theIF
statement (more readable)This should give you the output you want.
I used two variables
@current_parent_value
and@running_parent
@running_parent
will help you determine if the previous row and the current row belong to the sameparent
, and@current_parent_value
will help you store the current running value.