如何在不使用 LOOP 的情况下计算取决于先前值的当前值?
Posted
技术标签:
【中文标题】如何在不使用 LOOP 的情况下计算取决于先前值的当前值?【英文标题】:How to calculate current value that depends on previous value without using LOOP? 【发布时间】:2021-09-09 13:57:49 【问题描述】:我正在尝试在 SQL 中实现以下计算。目前,我正在使用带有 FOR LOOP 的 PL/SQL(或 PLPGSQL)和几个变量来计算每一行的值——当然,这是非常低效的。有没有办法使用窗口函数来完成这个?
对于第一行,当前值应该是某个固定值。对于以下行,
编辑:
相关公式:
Group-Return = IF(Period = 1, 0, SUMPRODUCT([Type-AA-Return]:[Type-BB-Return], [PREV Group-AA-Actual]:[PREV Group-BB-Actual] ))
Group-AA-Actual = IF(Period = 1, AA-Target, PREV Value/(1 + Group Return))
BB 和 CC 也是如此
样本数据:
前四列是静态数据。其他一切都是公式。
GROUP | Period | Type-AA-Return | Type-BB-Return | Group-Return | Group-Growth | AA-Target | BB-Target | CC-Target | Group-AA-Actual | Group-BB-Actual | Group-CC-Actual |
---|---|---|---|---|---|---|---|---|---|---|---|
GROUP-ONE | 1 | 0 | 0 | 0 | 1000 | 0.8 | 0.19 | 0.01 | 0.8 | 0.19 | 0.01 |
GROUP-ONE | 2 | -0.0040289 | -0.0040209 | -0.003987091 | 996.012909 | 0.8 | 0.19 | 0.01 | 0.799966419 | 0.189993551 | 0.010040031 |
GROUP-ONE | 3 | -0.003768282 | -0.008660322 | -0.004659904 | 991.371584 | 0.8 | 0.19 | 0.01 | 0.800683026 | 0.189229939 | 0.010087035 |
GROUP-ONE | 4 | -0.041907121 | 0.006951106 | -0.032238963 | 959.410792 | 0.8 | 0.19 | 0.01 | 0.792684016 | 0.19689292 | 0.010423064 |
GROUP-ONE | - | - | - | - | - | - | - | - | - | - | - |
GROUP-ONE | - | - | - | - | - | - | - | - | - | - | - |
GROUP-ONE | - | - | - | - | - | - | - | - | - | - | - |
GROUP-ONE | 12 | -0.079204478 | -0.000449001 | -0.063450162 | 950.886857 | 0.8 | 0.19 | 0.01 | 0.75 | 0.24 | 0.01 |
GROUP-TWO | 1 | 0 | 0 | 0 | 1000 | 0.8 | 0.19 | 0.01 | 0.8 | 0.19 | 0.01 |
GROUP-TWO | 2 | -0.0040289 | -0.0040209 | -0.003987091 | 996.012909 | 0.8 | 0.19 | 0.01 | 0.799966419 | 0.189993551 | 0.010040031 |
GROUP-TWO | 3 | -0.003768282 | -0.008660322 | -0.004659904 | 991.371584 | 0.8 | 0.19 | 0.01 | 0.800683026 | 0.189229939 | 0.010087035 |
GROUP-TWO | 4 | -0.041907121 | 0.006951106 | -0.032238963 | 959.410792 | 0.8 | 0.19 | 0.01 | 0.792684016 | 0.19689292 | 0.010423064 |
GROUP-TWO | - | - | - | - | - | - | - | - | - | - | - |
GROUP-TWO | - | - | - | - | - | - | - | - | - | - | - |
GROUP-TWO | - | - | - | - | - | - | - | - | - | - | - |
GROUP-TWO | 12 | -0.079204478 | -0.000449001 | -0.063450162 | 950.886857 | 0.8 | 0.19 | 0.01 | 0.75 | 0.24 | 0.01 |
【问题讨论】:
您将需要使用实际数据样本和预期结果对此进行扩展。 “使用先前值计算的另一列中的值”对我来说不清楚。 我已添加该信息。显得笨重了许多。希望这会有所帮助。 除了样本数据之外,您还需要发布该数据的预期结果。还要解释你的Relevant Formulas
,因为从我的角度来看,它们完全是胡言乱语。
这包含预期的输出 - 即 Group-Return 和 Group-AA-Actual 列。前四列是静态数据。其他一切都是公式。
【参考方案1】:
您可以使用 lag() 函数从前一行获取值吗?例如lag(GROUP-AA-ACTUAL)
?
您可能还需要使用 partition by
来让每个组都能正常工作。
看看这里是否有帮助:oracle-lag-function-with-group-by
【讨论】:
【参考方案2】:您可以使用recursive common table expression (CTE)
遍历一组行,这样:
输入示例数据:
CREATE TABLE t (value integer);
INSERT INTO t VALUES (10), (5), (15), (1), (4);
查询:
首先我生成行号,所以我可以一次迭代一行。然后我从第一行开始,得到下一行(第二行)。在UNION ALL
之后的子查询中,我一次得到一行(对于每一行,我可以访问以前的值(sq)和实际值(t_rn)。
在我的例子中calc_value(n) = calc_value(n-1) / (1 + value(n))
。
WITH RECURSIVE t_rn AS (SELECT ROW_NUMBER() OVER () AS rn, * FROM t),
sq AS (SELECT *, 10000.0 AS calc_value
FROM t_rn
WHERE rn = 1
UNION ALL
SELECT t_rn.rn, t_rn.value, (sq.calc_value / (1 + t_rn.value)) AS calc_value
FROM sq
INNER JOIN t_rn ON sq.rn + 1 = t_rn.rn)
SELECT value, ROUND(calc_value, 2)
FROM sq
ORDER BY rn;
输出:
value | calc_value |
---|---|
10 | 10000.00 |
5 | 1666.67 |
15 | 104.17 |
1 | 52.08 |
4 | 10.42 |
Fiddle!
【讨论】:
以上是关于如何在不使用 LOOP 的情况下计算取决于先前值的当前值?的主要内容,如果未能解决你的问题,请参考以下文章
如何在不删除先前相同值的情况下选择具有重复项的列表中的特定数字?