Yu Ching Tsoi
Yu Ching Tsoi

Reputation: 25

SQL Query in Snowflake - Calculate Days of Supply

I tried to find the days of supply each day. For example, date 1/23/2025 - It has inventory 700 which already subtract the forecast, 400 on that day. Count how many date of forecast can be covered by 700. 700 <= (200+300+100) equal 3 days on 1/23/2025. 600 <= (300+100) equal 2 days

Data table

Location Material Start_Date Inventory Forecast
W01 123456 1/23/2025 700 400
W01 123456 1/24/2025 600 200
W01 123456 1/25/2025 400 300
W01 123456 1/26/2025 450 100
W01 123456 1/27/2025 50 300

Desired Output

Location Material Start_Date Inventory Forecast DOS
W01 123456 1/23/2025 700 400 3
W01 123456 1/24/2025 600 200 2
W01 123456 1/25/2025 400 300 2
W01 123456 1/26/2025 450 100 1
W01 123456 1/27/2025 50 300 0

My query doesn't work

SELECT 
    "A"."LOCATION",
    "A"."MATERIAL", 
    "A"."START_DATE", 
    "A"."INVENTORY", 
    "A"."FORECAST",

(SELECT SUM("B"."FORECAST")
FROM "B"
WHERE "B"."START_DATE" >= "A"."START_DATE" ) AS "FUTURE_DEMAND",

CASE
    WHEN (SELECT SUM("B"."FORECAST")
        FROM "B"
        WHERE "B"."START_DATE" >= "A"."START_DATE ) = 0 THEN NULL
    ELSE "A"."INVENTORY" /
        (SELECT SUM ("B"."FORECAST")
            FROM "B"
            WHERE "B"."START_DATE" >= "A"."START_DATE 

END AS "DOS"

FROM "A" 
ORDER BY 
    "A"."START_DATE"

Upvotes: 1

Views: 56

Answers (1)

samhita
samhita

Reputation: 3505

In your output, for 1/26/2025 DOS should not be 2, instead it should be 1 as per your explanation that the current row forecast should not be included.So if inventory is 450 on 1/26/2025 then only 300 should be included which is 1 day.

I have approached it with a recursion where the anchor is set at 0 DOS and then each row for the subsequent dates are compared until the sum of forecast becomes >= than the inventory.

Finally in the outer query all columns are fetched alongside DOS by joining DOS_CTE and the table.

WITH RECURSIVE dos_cte AS (
  SELECT 
    location,
    Material,
    start_date,
    inventory,
    forecast,
    0 AS DOS,
    inventory AS remaining_inventory,
    start_date AS initial_date
  FROM 
    test
  
  UNION ALL
  
  SELECT 
    a.location,
    a.material,
    a.start_date,
    a.inventory,
    a.forecast,
    cte.dos + 1 AS DOS,
    cte.remaining_inventory - a.forecast AS remaining_inventory,
    cte.initial_date
  FROM 
    test a
  JOIN 
    DOS_CTE cte
  ON 
    a.location = cte.location
    AND a.material = cte.material
    AND a.start_date > cte.start_date 
  WHERE 
    cte.remaining_inventory - a.forecast >= 0
)

SELECT 
  t.location,
  t.material,
  t.start_date,
  t.inventory,
  t.forecast,
  COALESCE(MAX(cte.DOS) , 0) AS DOS
FROM 
  test t
LEFT JOIN 
  DOS_CTE cte
ON 
  t.location = cte.location
  AND t.material = cte.material
  AND t.start_date = cte.initial_date
GROUP BY 
  t.location,
  t.material,
  t.start_date,
  t.inventory,
  t.forecast
ORDER BY 
  t.start_date;

enter image description here

Upvotes: 0

Related Questions