I'm working on an ecommerce app in a distributed system where multiple servers are interacting with a PostgreSQL database. I need to implement an order number counter for each vendor to ensure each vendor has its own unique and incremental order numbers.
Each vendor has a distinct sequence of order numbers, such as Vendor 1 having orders numbered 1, 2, 3... and Vendor 2 also having orders numbered 1, 2, 3... These order numbers are independent of the database IDs and are generated sequentially for each vendor.
I've considered using sequences and triggers, but I really dont know if it is the right approach. Also I have heard about optimistic locks and could help but I am not familiare with the implementation and I fear that locking the DB would cause some performance issues or would require creating optimistic locks failure handlers. this is how to SQL data looks like:
CREATE TABLE vendors (
id SERIAL PRIMARY KEY,
name VARCHAR(255) NOT NULL);
CREATE TABLE orders (
id SERIAL PRIMARY KEY,
order_number INT NOT NULL,
vendor_id INT REFERENCES vendors(id));
So is there a way to do this ?
Effective ordinal number per vendor
You'll need a
viewfor that: if you try to only guard the consistency of that numbering through default values that are established on insert (either automatically db-side or by hand client-side), then a single delete, update, or non-default insert can break that consistency:deleteintroduces a gap in the sequence of a vendor's orders - you said it's not a problem for you, but still, it's avoidable.updatean order, reassigning it to another vendor or explicitly changing its number within the same vendor. In both cases thatorder_numberwill collide with another, future or existing, automatically-numbered order.insertan invalidly numbered, out-of-sequence order. Depending on how you set up the default,OVERRIDING SYSTEM VALUEclause might not even be necessary.A view can compute the current effective order of things, as of query time, using
row_number()window function, partitioned byvendor_id: demo1Per-vendor sequential identifier
If those are just identifiers that you want to be sequential and unique for each vendor, then that's a fairly similar problem to keeping up-to-date, cached group counts (example). None of the standard methods help here:
id serialid int generated by default as identity storedid int default nextval()All of them use a single, common sequence tied to the
idcolumn. A workaround is to use a function that acceptsvendor_idas a "seed" and maintains separate sequences for each distinct seed, returning adequatenextval()based on that. Or, maintain a separate table holding the most recent id per vendor: demo2It makes no attempt at removing gaps or correcting manually broken sequences. Also, all concurrent
insert into ordersattempts for the same vendor, lock each other and have to wait for predecessors to finish. You can trade that for only locking if they all insert the first orders of a vendor: demo3If a vendor already has a sequence, they'll all just call their adequate, dynamically constructed
nextval(). If not, they'll all get locked until the first one finishes setting it up, then callnextval()on it.It's tempting to try and use a function like that in a
defaultorgenerated always asexpression, but the former can't reference any other fields to establish the shared seed, and the latter can't use non-immutablefunctions - which this one is not. It's possible but not necessarily a good idea to try to falsely declare it asimmutable: that tells the planner it can potentially re-use a single evaluation of thatnextval()throughout all rows of a statement. Even if it doesn't, it's technically allowed to: