简体   繁体   中英

Sum of previous rows and multiply with value from another column

I have a table with around 80000 records. Here is an example:

startyear division account budget forecast
2021 40 4100 5122952,22 0,012306656
2022 40 4100 0 0,011424198
2023 40 4100 0 0,010431491
2024 40 4100 0 0,009311863
2025 40 4100 0 0,008324122
2026 40 4100 0 0,007763793
2027 40 4100 0 0,007557735
2028 40 4100 0 0,007357883
2029 40 4100 0 0,007160051
2030 40 4100 0 0,006953345
2031 40 4100 0 0,006737952
2032 40 4100 0 0,006535297
2033 40 4100 0 0,006364179
2034 40 4100 0 0,006213237
2035 40 4100 0 0,006085724
2036 40 4100 0 0,005944279
2037 40 4100 0 0,005758285
2038 40 4100 0 0,005559474
2039 40 4100 0 0,005360105
2040 40 4100 0 0,005163794
2041 40 4100 0 0,004972228

I'm trying to calculate a new column starting with the budget from line 1. So year 2021 will be the same. the first calculation is correct. But the rest of the calculation ignores the changes before. This is the code:

WITH cte AS (
SELECT startyear, division, account, forecast, SUM(budget) AS asum
FROM @table
GROUP BY startyear,
         division,
         account,
         forecast
), cteRanked AS (
SELECT cte.startyear,
             cte.division,
             cte.account,
             cte.forecast,
             cte.asum AS asum
FROM cte
)
SELECT c1.startyear,
          c1.division,
          c1.account,
          c1.forecast,
          c1.asum , ISNULL((SELECT SUM(c2.asum)*c1.forecast 
FROM cteRanked c2 
WHERE c1.startyear > 2021 
    AND c2.startyear < c1.startyear 
    AND c2.division = c1.division 
    AND c2.account = c1.account), c1.asum) AS Bud
FROM cteRanked c1
ORDER BY c1.division, c1.account, c1.startyear

Gives this result:

startyear division account forecast budget result correct
2021 40 4100 0,012306656 5122952,22 5122952,22 5122952,22
2022 40 4100 0,011424198 0 58525,62051 58525,62
2023 40 4100 0,010431491 0 53440,02998 54050,54
2024 40 4100 0,009311863 0 47704,22923 48752,52
2025 40 4100 0,008324122 0 42644,07928 43987,00
2026 40 4100 0,007763793 0 39773,54059 41367,57

As you can see this query does not cumulate the results when the query runs. Anyone have any ideas?

While your post is lacking some vital information...I don't necessarily blame you because it's a difficult problem to both explain and to solve.

It looks like you're basically trying to come up with a compounding interest calculator. Except in this case, the rate changes every year.

To calculate the PRODUCT aggregate of your forecasts, I found this blog post: https://blog.jooq.org/2018/09/21/how-to-write-a-multiplication-aggregate-function-in-sql/

It just required a very tiny bit of tweaking.

This is my answer:

DECLARE @StartingYear int = 2021,
        @StartingBudget decimal(12, 2);

SELECT @StartingBudget = yt.budget
FROM #YourTable yt
WHERE yt.startyear = @StartingYear

SELECT yt.startyear, yt.division, yt.account, yt.budget, yt.forecast, YearBudget = yt.budget, PrevYearDiff = yt.budget
FROM #YourTable yt
WHERE yt.startyear = @StartingYear
UNION ALL
SELECT x.startyear, x.division, x.account, x.budget, x.forecast
     , YearBudget = CONVERT(decimal(10,2), x.SumProdBudget)
     , PrevYearDiff = CONVERT(decimal(10,2), x.SumProdBudget - LAG(x.SumProdBudget,1,@StartingBudget) OVER (ORDER BY x.startyear))
FROM (
    SELECT yt.startyear, yt.division, yt.account, yt.budget, yt.forecast
        , SumProdBudget = EXP(SUM(LOG(1+yt.forecast)) OVER (ORDER BY yt.startyear)) * @StartingBudget
    FROM #YourTable yt
    WHERE yt.startyear > @StartingYear
) x

Returns:

| startyear | division | account | budget     | forecast    | YearBudget | PrevYearDiff | 
|-----------|----------|---------|------------|-------------|------------|--------------| 
| 2021      | 40       | 4100    | 5122952.22 | 0.012306656 | 5122952.22 | 5122952.22   | 
| 2022      | 40       | 4100    | 0.00       | 0.011424198 | 5181477.84 | 58525.62     | 
| 2023      | 40       | 4100    | 0.00       | 0.010431491 | 5235528.38 | 54050.54     | 
| 2024      | 40       | 4100    | 0.00       | 0.009311863 | 5284280.90 | 48752.52     | 
| 2025      | 40       | 4100    | 0.00       | 0.008324122 | 5328267.90 | 43987.00     | 
| 2026      | 40       | 4100    | 0.00       | 0.007763793 | 5369635.47 | 41367.57     | 

The key to this whole thing is this line:

SELECT SumProdBudget = EXP(SUM(LOG(1+yt.forecast)) OVER (ORDER BY yt.startyear)) * @StartingBudget

This is saying to return a running product of all previous forecasts, then multiply that by the original budget. This will produce the budget for each year, technically based on the budget of the previous year.

Then once you have that, I stuck it in a sub-query to find the difference between the year and its previous year.


Sample Data:

IF OBJECT_ID('tempdb..#YourTable','U') IS NOT NULL DROP TABLE #YourTable; --SELECT * FROM #YourTable
CREATE TABLE #YourTable (
    startyear   int             NOT NULL,
    division    int             NOT NULL,
    account     int             NOT NULL,
    budget      decimal(12, 2)  NOT NULL,
    forecast    decimal(10, 9)  NOT NULL,
);

INSERT INTO #YourTable (startyear, division, account, budget, forecast)
VALUES (2021, 40, 4100, 5122952.22, 0.012306656)
     , (2022, 40, 4100, 0         , 0.011424198)
     , (2023, 40, 4100, 0         , 0.010431491)
     , (2024, 40, 4100, 0         , 0.009311863)
     , (2025, 40, 4100, 0         , 0.008324122)
     , (2026, 40, 4100, 0         , 0.007763793)
     , (2027, 40, 4100, 0         , 0.007557735)
     , (2028, 40, 4100, 0         , 0.007357883)
     , (2029, 40, 4100, 0         , 0.007160051)
     , (2030, 40, 4100, 0         , 0.006953345)
     , (2031, 40, 4100, 0         , 0.006737952)
     , (2032, 40, 4100, 0         , 0.006535297)
     , (2033, 40, 4100, 0         , 0.006364179)
     , (2034, 40, 4100, 0         , 0.006213237)
     , (2035, 40, 4100, 0         , 0.006085724)
     , (2036, 40, 4100, 0         , 0.005944279)
     , (2037, 40, 4100, 0         , 0.005758285)
     , (2038, 40, 4100, 0         , 0.005559474)
     , (2039, 40, 4100, 0         , 0.005360105)
     , (2040, 40, 4100, 0         , 0.005163794)
     , (2041, 40, 4100, 0         , 0.004972228);

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM