Skip to content
Advertisement

How to reference the output of a previous calculation within the same query in SQL?

I’m still pretty new to SQL and I’m trying to perform a calculation that references the output from a calculation that was performed on the same table in a previous row/time period.

[Sample of table]

| Full Part | Quarter Yr | Name          | Value     | 
|-----------|------------|---------------|-----------| 
| 470205-17 | 42019      | Q1 FY 20 CCBP | 12524     | 
| 470205-17 | 42019      | Q1 FY 20 SP   | 579.0005  | 
| 470205-17 | 42019      | Q1 FY 21 CCBP | 17436     | 
| 470205-17 | 42019      | Q1 FY 21 SP   | 885.9997  | 
| 470205-17 | 42019      | Q2 FY 20 CCBP | 14807     | 
| 470205-17 | 42019      | Q2 FY 20 SP   | 764.9999  | 
| 470205-17 | 42019      | Q2 FY 21 CCBP | 15220     | 
| 470205-17 | 42019      | Q2 FY 21 SP   | 851.9996  | 
| 470205-17 | 42019      | Q3 FY 20 CCBP | 16196     | 
| 470205-17 | 42019      | Q3 FY 20 SP   | 1066.9998 | 
| 470205-17 | 42019      | Q4 FY 19 CCBP | 4841      | 
| 470205-17 | 42019      | Q4 FY 19 SP   | 730.0005  | 
| 470205-17 | 42019      | Q4 FY 20 CCBP | 17100     | 
| 470205-17 | 42019      | Q4 FY 20 SP   | 1115.9995 | 
| 470205-17 | 42019      | Total QOH     | 2181      | 
CREATE TABLE mytable(
   Full_Part  VARCHAR(9) NOT NULL
  ,Quarter_Yr INTEGER  NOT NULL
  ,Name       VARCHAR(13) NOT NULL
  ,Value      NUMERIC(9,4) NOT NULL
);
INSERT INTO mytable(Full_Part,Quarter_Yr,Name,Value) VALUES
 ('470205-17',42019,'Q1 FY 20 CCBP',12524)
,('470205-17',42019,'Q1 FY 20 SP',579.0005)
,('470205-17',42019,'Q1 FY 21 CCBP',17436)
,('470205-17',42019,'Q1 FY 21 SP',885.9997)
,('470205-17',42019,'Q2 FY 20 CCBP',14807)
,('470205-17',42019,'Q2 FY 20 SP',764.9999)
,('470205-17',42019,'Q2 FY 21 CCBP',15220)
,('470205-17',42019,'Q2 FY 21 SP',851.9996)
,('470205-17',42019,'Q3 FY 20 CCBP',16196)
,('470205-17',42019,'Q3 FY 20 SP',1066.9998)
,('470205-17',42019,'Q4 FY 19 CCBP',4841)
,('470205-17',42019,'Q4 FY 19 SP',730.0005)
,('470205-17',42019,'Q4 FY 20 CCBP',17100)
,('470205-17',42019,'Q4 FY 20 SP',1115.9995)
,('470205-17',42019,'Total QOH',2181);

SP is forecast , QOH is Current inventory and CCBP is Supply

What I need as output for Quarter YR=42019 is (Total QOH+Q4 FY 19 CCBP)-Q4 FY 19 SP

Then for quarter Yr=12020, I need Ending Inventory(Output of previous calculation)+(Q1 FY 20 CCBP)-Q1 FY 20 SP

And so on until QuarterYr=22021

The output needs to be in the same format i.e Full Part, QuartrYr,Name=’Ending Inventory’, Value

| Full Part | Quarter Yr | Name             | Value | 
|-----------|------------|------------------|-------| 
| 470205-17 | 42019      | Ending Inventory | 6292  | 
| 470205-17 | 12020      | Ending Inventory | 18237 | 
| 470205-17 | 22020      | Ending Inventory | 32280 | 
| 470205-17 | 32020      | Ending Inventory | 47409 | 
| 470205-17 | 42020      | Ending Inventory | 63393 | 
| 470205-17 | 12021      | Ending Inventory | 79944 | 
| 470205-17 | 22021      | Ending Inventory | 94313 | 

I’m assuming that this needs recursion/for loops to achieve the desired output. Any help in making this work would be greatly appreciated.

Advertisement

Answer

Tested on SQL Server 2017:

Here’s an Example query to produce the desired results:

declare @mytable table(
   Full_Part  VARCHAR(9) NOT NULL
  ,Quarter_Yr INTEGER  NOT NULL
  ,Name       VARCHAR(13) NOT NULL
  ,Value      NUMERIC(9,4) NOT NULL
);

INSERT INTO @mytable(Full_Part,Quarter_Yr,Name,Value) VALUES
 ('470205-17',42019,'Q1 FY 20 CCBP',12524)
,('470205-17',42019,'Q1 FY 20 SP',579.0005)
,('470205-17',42019,'Q1 FY 21 CCBP',17436)
,('470205-17',42019,'Q1 FY 21 SP',885.9997)
,('470205-17',42019,'Q2 FY 20 CCBP',14807)
,('470205-17',42019,'Q2 FY 20 SP',764.9999)
,('470205-17',42019,'Q2 FY 21 CCBP',15220)
,('470205-17',42019,'Q2 FY 21 SP',851.9996)
,('470205-17',42019,'Q3 FY 20 CCBP',16196)
,('470205-17',42019,'Q3 FY 20 SP',1066.9998)
,('470205-17',42019,'Q4 FY 19 CCBP',4841)
,('470205-17',42019,'Q4 FY 19 SP',730.0005)
,('470205-17',42019,'Q4 FY 20 CCBP',17100)
,('470205-17',42019,'Q4 FY 20 SP',1115.9995)
,('470205-17',42019,'Total QOH',2181);

WITH PARSE1 AS (
    SELECT 
        CASE WHEN Name LIKE 'Q_ FY%' THEN SUBSTRING(Name,2,1)  ELSE NULL END AS N1,
        CASE WHEN Name LIKE 'Q_ FY%' THEN SUBSTRING(Name,7,2)  ELSE NULL END AS N2,
        CASE WHEN Name LIKE 'Q_ FY%' THEN SUBSTRING(Name,10,4) 
             WHEN NAME = 'Total QOH' THEN 'QOH' ELSE NULL END AS N3,
        Full_Part, Quarter_Yr, Value
    FROM @mytable
), PARSE2 AS (
    SELECT Full_Part, Quarter_Yr, Value, N3, CASE WHEN N3 = 'SP' THEN -1 ELSE 1 END AS mValue, CAST(N1 AS INT) AS Q, 2000 + CAST(N2 AS INT) AS YR
    FROM PARSE1
), PIVOT1 AS (
    SELECT Q, YR, Full_Part, Quarter_Yr, SUM(Value * mValue) AS Qtr_Value 
    FROM PARSE2
    GROUP BY Q, YR, Full_Part, Quarter_Yr
), ANALYTIC1 AS (
    SELECT Q, YR, Full_Part, Quarter_Yr, Qtr_Value,
        SUM(Qtr_Value) OVER (PARTITION BY [Full_Part] ORDER BY YR, Q) AS Run_Value
    FROM PIVOT1
) 
SELECT Full_Part AS [Full Part], CONCAT(Q,YR) AS [Quarter Yr], 'Ending Inventory' AS Name, CAST(ROUND(Run_Value,1) AS INT) AS Value
FROM ANALYTIC1 
WHERE Q IS NOT NULL

Output:

Full Part   Quarter Yr  Name                Value
470205-17   42019       Ending Inventory    6292
470205-17   12020       Ending Inventory    18237
470205-17   22020       Ending Inventory    32279
470205-17   32020       Ending Inventory    47408
470205-17   42020       Ending Inventory    63392
470205-17   12021       Ending Inventory    79942
470205-17   22021       Ending Inventory    94310

About the different Common Table Expressions – CTE(s)

PARSE1/PARSE2:

  • Some key data is hidden inside of the string “Name”.
  • We need to extract out the Data Type, Quarter, and Year information (Assuming 2000 + Two Digits available).
  • We’ll also plan to subtract SP by multiplying the Value by negative 1.

PIVOT1:

  • Now we can apply our group by function to sum up the quarterly parts.

ANALYTIC1:

  • Using a special sum analytic function, we can create a running tally when ordering by Quarter_Yr.
  • An partition by clause is included to create a running tally within each logical set of data.

FINAL SELECT:

  • A final clean up query is provided to present the data as desired.

You can replace ROUND() with CEILING() or FLOOR() to fine tune the results.

To inspect each individual CTE, You can replace the final select with:

  • SELECT * FROM PARSE1
  • SELECT * FROM PARSE2
  • SELECT * FROM PIVOT1
  • SELECT * FROM ANALYTIC1
User contributions licensed under: CC BY-SA
1 People found this is helpful
Advertisement