Heim > Fragen und Antworten > Hauptteil
Ich habe einige Berechnungen und möchte diese in einer Abfrage durchführen.
Es gibt Eltern-- und Kind-Tabellen mit einer Eins-zu-viele-Beziehung:
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`) );Um den endgültigen Wert des übergeordneten Elements zu ermitteln, sollte ich über die untergeordneten Elemente iterieren und die folgende Formel berechnen:
newParentValue = childMultiple(parentValue + childSum)
function calculateFinalParentValue($parentValue, $children) { foreach ($children as $child) { $parentValue = $child['multiple'] * ($parentValue + $child['sum']); } return $parentValue; }
Wie implementiert man eine Berechnung in einer Abfrage?
Ich versuche es so (mit temporären Variablen):
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;Ich habe die Variable in der Join-Bedingung festgelegt
, um die Variable für jedes neue übergeordnete Element zurückzusetzen. (@value := p.value)
Aber dieser Weg ist nicht nachhaltig.
Gibt es einen besseren Weg?
Beispiel:
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 | +----+-----------+----------+------+Basierend auf den oben genannten Daten würde ich die folgende Antwort erwarten:
+----+--------+ | 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 +----+--------+Für parent.id=1 gibt es drei Kinder und parent.value ist 10, sodass nach der Berechnung der Formel für das erste Kind der neue Wert
ist (was ein Vielfaches aller drei Kinder ist und die Summe 1 ergibt). 1 * (10 + 1) = 11
,第二个孩子的值是 1 * (11 + 1) = 12
正如预期的那样,第三个子值是 1 * (12 + 1) = 13
ist (beide Kinder sind Vielfache und die Summe entspricht 2). 2 * (20 + 2) = 44
,第二个孩子的值是 2 * (44 + 2) = 92
+----+--------+ | id | value | +----+--------+ | 1 | 13.00 | | 2 | 92.00 | +----+--------+Um das Beispiel zu vereinfachen: Alle
Spalten der untergeordneten Tabelle jedes Elternteils sind gleich (unter der Annahme unterschiedlicher Werte) und der Endwert ist das Maximum. Der Endwert ist möglicherweise nicht jedes Mal das Maximum. multiply
和 sum
P粉4933130672024-02-18 16:22:54
使用ROW_NUMBER()
窗口函数对children
的行进行排名,按parent_id
分区并按id
和SUM()
窗口函数进行排序,以获得所需的总和。
最后使用FIRST_VALUE()
窗口函数获取每个id的最后一个总和:
WITH cte_children AS (SELECT *, ROW_NUMBER() OVER (PARTITION BY parent_id ORDER BY id) rn FROM children), cte_sums AS ( SELECT p.id, c.rn, POW(c.multiple, c.rn) * p.value + SUM(POW(c.multiple, c.rn)) OVER (PARTITION BY p.id ORDER BY c.rn) * c.sum value FROM parent p INNER JOIN cte_children c ON c.parent_id = p.id ) SELECT DISTINCT id, FIRST_VALUE(value) OVER (PARTITION BY id ORDER BY rn DESC) value FROM cte_sums;
查看演示。
P粉9596764102024-02-18 00:50:20
有一点棘手,因为在父级发生变化时,你必须在中间重置你的值。
尝试以下查询:
SELECT parentId, ROUND(iteratingValue, 2) reqValue FROM (SELECT parentId, `childMultiple`, childSum, @running_parent, (CASE WHEN @current_parent_value=0 THEN @current_parent_value:=parentValue ELSE @current_parent_value=@current_parent_value END) , (CASE WHEN @running_parent!=parentId THEN @current_parent_value:=parentValue ELSE @current_parent_value:=@current_parent_value END), @current_parent_value:=(`childMultiple`*(@current_parent_value+childSum)) AS iteratingValue, @running_parent:=parentId FROM (SELECT p.`id` parentId, c.`multiple`childMultiple, p.`value` parentValue, c.`sum` AS childSum, @current_parent_value:=0, @running_parent:=0 FROM parent p JOIN `children` c ON c.`parent_id`=p.`id` ) subTable ORDER BY parentId) finalTable;
你也可以用IF
语句替换上述提到的CASE
语句(更易读一些)
IF(@current_parent_value=0, @current_parent_value:=parentValue, @current_parent_value=@current_parent_value), IF(@running_parent!=parentId, @current_parent_value:=parentValue, @current_parent_value:=@current_parent_value),
这应该给你想要的输出。
我使用了两个变量@current_parent_value
和@running_parent
@running_parent
将帮助你确定前一行和当前行是否属于同一个parent
,而@current_parent_value
将帮助你存储当前的运行值。