You can have a fourth alternative, that is consistent with your definition, where the database takes care of itemNumbers when you INSERT
your data; without the need for triggers. [Although you could use them as well.]
Assuming this is your schema:
CREATE TABLE "Order"
(
"orderId" integer primary key, -- this should probably be serial
"order" text not null
) ;
CREATE TABLE "OrderItem"
(
"orderId" integer not null REFERENCES "Order"("orderId")
ON DELETE CASCADE ON UPDATE CASCADE,
"itemNumber" integer not null,
"orderItem" text,
PRIMARY KEY ("orderId", "itemNumber")
) ;
and that we have three orders:
INSERT INTO "Order" ("orderId", "order") VALUES(1, 'Order number 1') ;
INSERT INTO "Order" ("orderId", "order") VALUES(2, 'Order number 2') ;
INSERT INTO "Order" ("orderId", "order") VALUES(3, 'Order number 3') ;
If you need to add ONE item to order 3, you'd do:
INSERT INTO
"OrderItem"
("orderId", "orderItem", "itemNumber")
VALUES
(3, 'whichever item you need',
(SELECT coalesce(max("itemNumber"),0) + 1
FROM "OrderItem"
WHERE "orderId"=3)
) ;
That is, when you INSERT
, you just look for the max("itemNumber")
for your specific "order_Id" and add 1 to it. If this is the first item you insert an item to an order, the COALESCE(___, 0) + 1 will give you a 0 + 1, instead of NULL + 1 (which wouldn't work).
If you need to add several items to several orders (which isn't that common for most applications), you can do it in a slightly more complicated fashion, but using the same principle:
-- Assume you want to add items to orders 1 and 2
WITH "orderItemsToAdd" ("orderId", "orderItem") AS
(
VALUES
(1, 'first item, 1st order'),
(1, 'second item, 1st order'),
(2, 'first item, 2nd order'),
(2, 'second item, 2nd order'),
(2, 'third item, 2nd order')
)
-- Get the max item numbers (or zero, via coalesce) for each order
, "maxItemNumbers" AS
(
SELECT
"orderId", coalesce(max("itemNumber"), 0) AS "baseItemNumber"
FROM
"orderItemsToAdd"
LEFT JOIN "OrderItem" USING("orderId")
GROUP BY
"orderId"
)
-- We insert new items, using row_number() + baseItemNumber as the new item numbers
-- (NOTE: orderItemsToAdd should, in practice be ORDERed BY something!)
INSERT INTO
"OrderItem" ("orderId", "itemNumber", "orderItem")
SELECT
"orderId", "baseItemNumber" + (row_number() over (partition by "orderId")), "orderItem"
FROM
"orderItemsToAdd"
JOIN "maxItemNumbers" USING("orderId")
RETURNING
* ;
This is what you'll get:
+--------------------------------+
¦ 1 ¦ 1 ¦ first item, 1st order ¦
¦---+---+------------------------¦
¦ 1 ¦ 2 ¦ second item, 1st order ¦
¦---+---+------------------------¦
¦ 2 ¦ 1 ¦ first item, 2nd order ¦
¦---+---+------------------------¦
¦ 2 ¦ 2 ¦ second item, 2nd order ¦
¦---+---+------------------------¦
¦ 2 ¦ 3 ¦ third item, 2nd order ¦
+--------------------------------+
You can check it at rextester.
You need to be aware of:
As a side note, if you use PostgreSQL, I'd advise using underscored_lowercase_identifiers
instead of camelCaseOnes
. You save a lot of time not having to type "
(and the risk of forgetting them) ;-)
Second side-note: this approach can lead to race conditions (and transactions having to be aborted then retried) if many people try to change the same order at the same time. See comments by @Erwin.