I have the inventory label for each product and its skuid. A product can have 1 more Skuid.
product_skus_inventories table:
ProductID SkuID Inventory_Label
123 a1 InStock
123 a2 OutOfStock
123 a3 NULL
I want to update the below table that is a summary:
product_summary
- product_id
- inventory_label (values can be InSock, OutOfStock or Partial)
The logic is as follows:
- if all SkuID’s for a given productID "InStock" or NULL, then "InStock".
- if any of the SkuIds have "InStock", and other’s have different value then "Partial".
- otherwise, "OutOfStock".
Is it possible to have this type of logic in a update query?
5
Answers
You can create a view of your table that generates the status which can then be easily queried when you need the values. If instead you need to run an UPDATE to materialize the inventory label, then you can use the SELECT inside
JOIN LATERAL
to get the status to insert for a given product_id.The logic for the status is as follows:
out_of_stock
.out_of_stock
exists for this product_id as the previous clause didn’t match, we just need to check if there exists at least 1in_stock
orNULL
.out_of_stock
.Collect label combinations per product and specify summary of each combination
case
: demoThis can be used as an update source, as shown at the end of the demo.
Making this a
view
or amaterialized view
, using it as a source for aninsert into...on conflict do update
or amerge
(also in the demo), could potentially make more sense.Basic query
With original table definition
Don’t count or aggregate all rows. You are looking for a boolean result, basically. And these two aggregate functions happen to fit your task description exactly:
bool_and()
covers case "1."bool_or()
covers case "2."Makes for a fast and simple query:
fiddle
Better with
boolean
product_skus_inventories.inventory_label
can be "in", "out", ornull
. That’s aboolean
quantity. Use the fitting, superior data type. Only occupies 1 byte and makes everything shorter, simpler and faster.To convert the table (triggers a complete table rewrite with exclusive lock):
Then run
VACUUM ANALYZE product_skus_inventories
once.Now, the query is even simpler and faster:
fiddle
"Update" summary table
The appropriate action is an UPSERT, really.
INSERT ... ON CONFLICT ... DO UPDATE ...
in Postgres.Assuming this table definition:
fiddle
Note the added
WHERE
clause. This skips all updates that wouldn’t change the state, typically making the query a lot cheaper. See:Alternatives to my initial answer, at @Mike Organek‘s suggestion to try other aggregate functions:
count(distinct inventory_label)
combinations: demoWith
bit
s, using encodings: demo10
) forInStock
,01
) forOutOfStock
,11
(decimal 3) frombit_or()
when both are found, or10
(2) for justInStock
I prefer the
array_agg(distinct inventory_label)
for its readability. These are all simple, but that one also self-comments. Still,count()
is afilter
shorter, andbit_or()
is faster.How about using this kind of approach.
This query first checks if all SkuIDs for a given productID are either "InStock" or NULL, and updates the inventory_label to "InStock" if that’s the case.
If not, it checks if any SkuIDs have "InStock" and updates the inventory_label to "Partial" if that’s the case.
If neither condition is met, it updates the inventory_label to "OutOfStock".