'Rolling On-Hand Remainder column?
CONumber, LineNumber, PartNumber, OrderQty, ScheduleDate, OnHandQty columns are a pure SELECT query with no transformations. I am trying to recreate the RollingOnHand column in SQL.
The rules are
- If a part only has one row, report the real [
OnHandQty] - If a part has multiple rows, the oldest order consumes its [
OrderQty] from [OnHandQty] - The next oldest order pulls its [
OrderQty] from the remaining [OnHandQty], repeat until final row of the matching part - The last row of a given part will display the remaining [
OnHandQty]
Is this possible to accomplish in an SQL query?
| CONumber | LineNumber | PartNumber | OrderQty | ScheduleDate | OnHandQty | RollingOnHand |
|---|---|---|---|---|---|---|
| C02959 | 00002 | Part 01 | 102 | 2022-04-01 | 0 | 0 |
| C04017 | 00001 | Part 02 | 2007 | 2022-04-01 | 5099 | 5099 |
| C04107 | 00001 | Part 03 | 1 | 2022-03-09 | 0 | 0 |
| C04106 | 00001 | Part 04 | 1 | 2022-03-09 | 0 | 0 |
| C04108 | 00001 | Part 05 | 1 | 2022-03-09 | 0 | 0 |
| C03514 | 00002 | Part 06 | 250 | 2022-03-11 | 310 | 250 |
| C03514 | 00003 | Part 06 | 250 | 2022-03-18 | 310 | 60 |
| C03757 | 00001 | Part 06 | 250 | 2022-04-06 | 310 | 0 |
| C04225 | 00002 | Part 07 | 40 | 2022-03-31 | 53 | 53 |
| C03965 | 00002 | Part 08 | 24 | 2022-04-04 | 0 | 0 |
| C04034 | 00001 | Part 09 | 88 | 2022-03-18 | 128 | 128 |
| C04144 | 00002 | Part 10 | 22 | 2022-04-04 | 0 | 0 |
| C04141 | 00001 | Part 10 | 100 | 2022-04-04 | 0 | 0 |
| C03734 | 00003 | Part 11 | 116 | 2022-03-29 | 103 | 103 |
| C03379 | 00001 | Part 12 | 128 | 2022-03-07 | 19 | 19 |
| C03344 | 00003 | Part 13 | 40 | 2022-03-11 | 5 | 5 |
| C04058 | 00001 | Part 14 | 407 | 2022-03-25 | 0 | 0 |
| C03697 | 00002 | Part 15 | 436 | 2022-04-04 | 235 | 235 |
| C03689 | 00002 | Part 16 | 111 | 2022-03-16 | 87 | 87 |
| C03690 | 00001 | Part 16 | 250 | 2022-03-23 | 87 | 0 |
| C03690 | 00002 | Part 16 | 250 | 2022-04-06 | 87 | 0 |
| C03240 | 00004 | Part 17 | 3 | 2022-03-16 | 30 | 3 |
| C03725 | 00001 | Part 17 | 250 | 2022-03-16 | 30 | 27 |
| C03725 | 00002 | Part 17 | 250 | 2022-03-23 | 30 | 0 |
| C03726 | 00001 | Part 17 | 250 | 2022-04-01 | 30 | 0 |
| C03726 | 00002 | Part 17 | 250 | 2022-04-06 | 30 | 0 |
| C03596 | 00017 | Part 18 | 56 | 2022-04-06 | 344 | 344 |
| C03927 | 00001 | Part 19 | 600 | 2022-04-04 | 1800 | 600 |
| C03927 | 00002 | Part 19 | 1000 | 2022-04-06 | 1800 | 1200 |
Solution 1:[1]
I think this basically does what you need (Fiddle)
WITH T AS
(
SELECT *,
AlreadyConsumed = SUM(OrderQty) OVER (PARTITION BY [PartNumber] ORDER BY ScheduleDate ASC ROWS BETWEEN UNBOUNDED PRECEDING AND 1 PRECEDING),
PrevLineNumber = LAG([LineNumber]) OVER (PARTITION BY [PartNumber] ORDER BY ScheduleDate ASC),
NextLineNumber = LEAD([LineNumber]) OVER (PARTITION BY [PartNumber] ORDER BY ScheduleDate ASC)
FROM Demo
)
SELECT CONumber,
LineNumber,
PartNumber,
OrderQty,
ScheduleDate,
OnHandQty,
RollingOnHand = CASE
--If a part only has one row, report the real [OnHandQty]
WHEN PrevLineNumber IS NULL
AND NextLineNumber IS NULL THEN OnHandQty
--Not the last row and won't use all the remainder up
WHEN NextLineNumber IS NOT NULL AND Remainder > OrderQty THEN OrderQty
--otherwise use what's left
ELSE Remainder
END
FROM T
CROSS APPLY (SELECT CASE WHEN AlreadyConsumed > OnHandQty THEN 0 ELSE OnHandQty - ISNULL(AlreadyConsumed,0) END) C(Remainder)
The
SUM ... PARTITION BY [PartNumber] ... ROWS BETWEEN UNBOUNDED PRECEDING AND 1 PRECEDING computes the cumulative OrderQty for all rows before the current row (not including it)
The LAG/ LEAD results are used as indicators to determine whether we are in the first/last rows of a partition and special logic is needed.
I didn't quite follow the rationale behind the business logic so I may have made some invalid simplifications but it returns the desired results with the sample data and anyway the query should be easy to tweak if needed.
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 |
