The problem domain I'm working in is returns management for eCommerce.
I'm using Postgres (11.9) and have the following tables (I've removed a number of fields from each table that aren't relevant to the question):
CREATE TABLE "order" (
id BIGSERIAL PRIMARY KEY,
platform text NOT NULL,
platform_order_id text NOT NULL,
CONSTRAINT platform_order_id_unique UNIQUE (platform, platform_order_id)
);
CREATE TABLE order_item (
id BIGSERIAL PRIMARY KEY,
order_id int8 NOT NULL,
platform_item_id text NOT NULL,
quantity integer,
CONSTRAINT FK_order_item_order_id FOREIGN KEY (order_id) REFERENCES "order",
CONSTRAINT platform_item_id_unique UNIQUE (order_id, platform_item_id)
);
CREATE TABLE return (
id BIGSERIAL PRIMARY KEY,
order_id int8 NOT NULL,
CONSTRAINT FK_return_order_id FOREIGN KEY (order_id) REFERENCES "order"
);
CREATE TABLE return_item (
return_id int8 NOT NULL,
order_item_id int8 NOT NULL,
quantity integer NOT NULL,
CONSTRAINT FK_return_item_return_id FOREIGN KEY (return_id) REFERENCES return,
CONSTRAINT FK_return_item_item_id FOREIGN KEY (order_item_id) REFERENCES order_item
);
To explain the domain briefly, I pull orders from eCommerce platforms and store them in my database. An order is comprised of one or more distinct items that has a quantity > 1
. When a user wishes to return an item, they may return up to the quantity per return.
Put more concretely, if I buy two black small t-shirts in a single order, you would find an order
in the database with a single order_item
that has a quantity of 2
. I would have the ability to create two separate returns, each return with one return_item
referencing the same order_item_id
but with a quantity of 1.
order_item
and return_item
are inserted in different transactions and I'm not preventing multiple transactions from updating either of those at the same time.
How can I ensure that the summed value of each quantity
for all return_item
with a particular order_item_id
does not exceed the quantity stored in the corresponding order_item
for with said id
?
Put in more plain English, how do I prevent a third item from being returned when the quantity of that item in the original order was 2 as in the example I described?
It's easy enough to write an application check to catch this in most cases, and also not difficult to add a business rule checking WHERE
clause to my return_item
insertions, but neither of these solutions gives me the consistency guarantees that uniqueness constraint does. How would I go about writing a trigger to error on insert here? Or is there a better approach than a trigger?