I have a database that tracks the location of widgets (i.e. an inventory system that tracks individual items rather than simply Counts of SKUs).
There are two tables concerned:
InventoryUpdates (
UpdateId bigint PRIMARY KEY,
LocationId bigint,
Type tinyint, -- an enum column
DateTime datetimoffset
)
WidgetState {
SerialNumber varchar(20) PRIMARY KEY,
UpdateId bigint
}
Explanation:
Inventory Updates are either "Complete", "Additive", or "Subtractive" (this is determined by the Type
column). When an Update is Complete
, it means the associated set of WidgetState
represents the entire inventory (i.e. if a widget is listed, then it's there; and absence of a widget is positive verification that it was not present in the inventory at that time). When an Update is Additive
, it means the Inventory needs to be determined by summing up all of the subsequent Additive
updates since the last Complete
update (just like incremental backups), finally a Subtractive
update means those items included in the update should be excluded from the inventory. Of course, multiple Additive
and Subtractive
updates can appear after a Complete
update.
So my problem is:
"Given the last Complete
update and the set of all subsequent Additive
and Subtractive
updates, get the current inventory of a location"
The data would look like this:
InventoryUpdates
----------------------------------------------
UpdateId LocationId Type DateTime
1 1 Complete 2014-01-01
2 1 Additive 2014-01-02
3 1 Subtractive 2014-01-03
4 1 Additive 2014-01-04
WidgetState
----------------------------------------------
SerialNumber UpdateId
0001 1
0004 1
008B 2
0001 3
0004 3
0004 4
In this simple example, we see that the unique widget 0004
is known to be in the inventory in Update 1, then it's removed in Update 3, then added back in Update 4.
Supposing I want to find out the current inventory, my resultset should be:
WidgetsInInventory
----------------------------------------------
SerialNumber
0004
008B
There's probably a really simple solution, I just can't think of it right now. I'm thinking it might look something like this:
SELECT
DISTINCT SerialNumber,
Type
FROM
WidgetState
INNER JOIN InventoryUpdates ON WidgetState.UpdateId = InventoryUpdates.UpdateId
WHERE
InventoryUpdates.Type = Complete OR
InventoryUpdates.Type = Additive
ORDER BY
InventoryUpdates.DateTime DESC
EXCEPT
SELECT
DISTINCT SerialNumber,
Type
FROM
WidgetState
INNER JOIN InventoryUpdates ON WidgetState.UpdateId = InventoryUpdates.UpdateId
WHERE
InventoryUpdates.Type = Subtractive
ORDER BY
InventoryUpdates.DateTime DESC
...but then it falls apart when a Subtraction happens regardless of the fact it might have happened with an earlier DateTime value than a subsequent Addition.
Update
I just had a thought while on my way home that this might be a solution that works, I'll test it out tomorrow.
The code below uses DISTINCT
combined with ORDER BY
to get the last appearance of each WidgetState
, so if the last appearance was an Addition
then it would cancel-out any previous Subtraction
, and vice-versa (well, if the last appearance was a Subtraction
then it can then be easily excluded from future super-queries with a WHERE Type <> Subtractive
predicate). It's just gone 03:00h and I haven't had a chance to try it, but does the theory sound right? Thanks!
SELECT
WidgetState.SerialNumber,
InventoryUpdates.Type
FROM
WidgetState
INNER JOIN InventoryUpdates ON WidgetState.UpdateId = InventoryUpdates.UpdateId
WHERE
InventoryUpdates.DateTime >= @dateTimeOfLastCompleteUpdate
AND
InventoryUpdates.LocationId >= @locationId
GROUP BY
SerialNumber
ORDER BY
InventoryUpdates.DateTime DESC