I have a requirement of order no which need to start with country code + some running number which is incermenting
I did a trigger in following way. The order_no has a unique key constraint.
Now the issue I am having is that when there is concurrent order coming in. First order will be inserted and second order will be failed due to unique key constraint as 2 same order no generated.
What is the best way I can do this in database level?
CREATE OR REPLACE FUNCTION func_gen_order_no()
RETURNS TRIGGER
LANGUAGE PLPGSQL
AS
$$
DECLARE
current_no INT;
BEGIN
SELECT COUNT(*) INTO current_no FROM order_main dt WHERE dt.order_country_code = NEW."order_country_code";
NEW."order_no" = CONCAT(NEW."order_country_code", TO_CHAR(current_no + 1, '000000FM') );
RETURN NEW;
END;
$$;
CREATE TRIGGER tgr_gen_order_no
BEFORE INSERT
ON order_main
FOR EACH ROW
EXECUTE PROCEDURE func_gen_order_no();
2
Answers
Just don’t.
Persisted gapless, ordinal numbers for orders is a wet dream causing nothing but pain in reality. Very awkward for concurrent write access, and will cause problems with updates and deletes sooner or later.
Also keep country code and serial number in separate columns – each with appropriate data type, and the country code with a
FOREIGN KEY
constraint to enforce valid states. For display, you can always (very cheaply) concatenate. If you must, add a generated column. See:Use an
IDENTITY
column for the serial number. And be prepared to see gaps in the numbering after failed inserts / deletes / upodates. See:If you must have gapless numbers per country, create them after the fact in a view. See:
Adjusting the answer in a similar thread to your structure and the fact you want the
country_code
in theorder_no
: you can employ generated seed-based sequences. Demo:It doesn’t mind if multiple concurrent transactions target the same country code and only locks briefly the first time a given country is entered into the table. The demo also shows how to initially set up sequences for pre-existing country codes and set
order_no
for pre-existing entries. Sequence ownership is handy.If you don’t mind gaps in your sequence,
because
delete
,update
and rolled-backinsert
will cause them at some point anyways, use generated columns: oneidentity
, one regular: demoAs opposed to
concat()
andto_char()
,||
andlpad()
are immutable and can be used in that context. There are slight differences in their behaviour.