Search code examples
sqlsql-servercommon-table-expression

Calculate value based on sum of previous calculated values


What needs to happen: create a new column called AdjustedAllocation by taking the IncrementedSourceRatio and multiplying it by the original AllocationAmount LESS the SUM of all previous AdjustedAllocation for that participant.

Issue is that I'm referencing the value I'm trying to calculate in the same column. And I need all the prior calculated values to make the correct adjustment. The last column is the expected value.

(A) PlanID (B) EEID (C) TotalBalance (D) Ticker (E) MarketValue (F) CurrentDenominator (G) AllocationAmount (H) IncrementedSourceRatio (I) Adjusted Allocation Formula (J) Adjusted Allocation
10241299 11973 241781.86 VEMAX 5654.12 241781.86 1 0.0233 =H2*G2 0.0233
10241299 11973 241781.86 VMGMX 5972.14 236127.74 1 0.0252 =H3*(G3-J2) 0.02461284
10241299 11973 241781.86 VTMGX 35099.02 230155.6 1 0.1525 =H4*(G4-(J2+J3)) 0.1451932919
10241299 11973 241781.86 VTSAX 45529.07 195056.58 1 0.2334 =H5*(G5-(J2+J3+J4)) 0.18832902881454
10241299 11973 241781.86 VFIAX 149527.51 149527.51 1 1 =H6*(G6-(J2+J3+J4+J5)) 0.61856483928546

I've tried variations of LAG(), SUM(), COALESCE(), adding ROW_NUMBER(), and all kinds of temp tables to no avail.

I've narrowed it down to some kind of recursive query/CTE, but most of the examples I have found are very simple and don't exactly fit what I'm trying to do. I'm still trying to learn how to apply CTEs and I'm hoping seeing a solution for this item will help solidify my understanding.

These solutions came close, but not exactly what I need to accomplish:

Thank you in advance for helping my learning.

IF OBJECT_ID('tempdb..#TEMP', 'u') IS NOT NULL DROP TABLE #TEMP
IF OBJECT_ID('tempdb..#TEMP2', 'u') IS NOT NULL DROP TABLE #TEMP2
GO

CREATE TABLE #TEMP
(
    PlanID NVARCHAR(10)
    ,EEID NVARCHAR(15)
    ,Ticker NVARCHAR(10)
    ,MarketValue MONEY
    ,TotalBalance MONEY
    ,CurrentDenominator MONEY
    ,AllocationAmount DECIMAL(18,6)
)

INSERT INTO #TEMP VALUES
('10241299',    '70973',    'VEMAX',    5654.12,    241781.86,  241781.86,  1),
('10241299',    '70973',    'VMGMX',    5972.14,    241781.86,  236127.74,  1),
('10241299',    '70973',    'VTMGX',    35099.02,   241781.86,  230155.6,   1),
('10241299',    '70973',    'VTSAX',    45529.07,   241781.86,  195056.58,  1),
('10241299',    '70973',    'VFIAX',    149527.51,  241781.86,  149527.51,  1)

SELECT
    PlanID 
    ,EEID 
    ,TotalBalance 
    ,Ticker
    ,MarketValue 
    ,CurrentDenominator 
    ,AllocationAmount
    ,IncrementedSourceRatio = (MarketValue/CurrentDenominator)
INTO #TEMP2
FROM #TEMP

SELECT
    PlanID 
    ,EEID 
    ,TotalBalance 
    ,Ticker
    ,MarketValue 
    ,CurrentDenominator 
    ,AllocationAmount
    ,IncrementedSourceRatio
FROM #TEMP2

Solution

  • You can use a recursion. First you should define a column to order your rows, MarketValue I guess. So add ,rn = row_number() over(order by MarketValue) to your #temp2 query and than

    with rcte as (
        select *
          ,cast(IncrementedSourceRatio * AllocationAmount as DECIMAL(10,9)) AdjustedAllocation
          ,cast(0 as DECIMAL(10,9)) cumulAA
        from #temp2 
        where rn=1
    
        union all
    
        select t2.*
          ,cast(t2.IncrementedSourceRatio * (t2.AllocationAmount - rcte.cumulAA - rcte.AdjustedAllocation) as DECIMAL(10,9))
          ,cast(rcte.cumulAA + rcte.AdjustedAllocation as DECIMAL(10,9))
        from rcte
        join #temp2 t2 on t2.rn= rcte.rn+1
      )
    select *
    from rcte
    order by MarketValue;
    

    Returns

    PlanID  EEID    TotalBalance    Ticker  MarketValue AllocationAmount    IncrementedSourceRatio  rn  AdjustedAllocation  cumulAA
    10241299    70973   241781.8600 VEMAX   5654.1200   1.000000    0.0233  1   0.023300000 0.000000000
    10241299    70973   241781.8600 VMGMX   5972.1400   1.000000    0.0252  2   0.024612840 0.023300000
    10241299    70973   241781.8600 VTMGX   35099.0200  1.000000    0.1525  3   0.145193290 0.047912840
    10241299    70973   241781.8600 VTSAX   45529.0700  1.000000    0.2334  4   0.188329030 0.193106130
    10241299    70973   241781.8600 VFIAX   149527.5100 1.000000    1.0000  5   0.618564840 0.381435160
    

    db-fiddle