I'm using Postgres 9.5 and seeing some wired things here.
I've a cron job running ever 5 mins firing a sql statement that is adding a list of records if not existing.
INSERT INTO sometable (customer, balance) VALUES (:customer, :balance) ON CONFLICT (customer) DO NOTHING
sometable.customer is a primary key (text)
sometable structure is:
id: serial
customer: text
balance: bigint
Now it seems like everytime this job runs, the id field is silently incremented +1. So next time, I really add a field, it is thousands of numbers above my last value. I thought this query checks for conflicts and if so, do nothing but currently it seems like it tries to insert the record, increased the id and then stops.
Any suggestions?
SERIAL or BIGSERIAL SERIAL is an auto-incremented integer column that takes 4 bytes while BIGSERIAL is an auto-incremented bigint column taking 8 bytes. Behind the scenes, PostgreSQL will use a sequence generator to generate the SERIAL column values upon inserting a new ROW.
PostgreSQL serial data type is used to define the auto increment number of column in a table; it will generate a serial sequence of integer numbers. Using serial data type, it will not automatically create the index on column; we need to create it explicitly.
There is essentially no difference.
The reason this feels weird to you is that you are thinking of the increment on the counter as part of the insert operation, and therefore the "DO NOTHING" ought to mean "don't increment anything". You're picturing this:
But in fact, the increment has to happen before the insert is attempted. A SERIAL
column in Postgres is implemented as a DEFAULT
which executes the nextval()
function on a bound SEQUENCE
. Before the DBMS can do anything with the data, it's got to have a complete set of columns, so the order of operations is like this:
This can be seen intuitively if the duplicate key is in the autoincrement field itself:
CREATE TABLE foo ( id SERIAL NOT NULL PRIMARY KEY, bar text ); -- Insert row 1 INSERT INTO foo ( bar ) VALUES ( 'test' ); -- Reset the sequence SELECT setval(pg_get_serial_sequence('foo', 'id'), 0, true); -- Attempt to insert row 1 again INSERT INTO foo ( bar ) VALUES ( 'test 2' ) ON CONFLICT (id) DO NOTHING;
Clearly, this can't know if there's a conflict without incrementing the sequence, so the "do nothing" has to come after that increment.
As already said by @a_horse_with_no_name and @Serge Ballesta serials are always incremented even if INSERT
fails.
You can try to "rollback" serial value to maximum id
used by changing the corresponding sequence:
SELECT setval('sometable_id_seq', MAX(id), true) FROM sometable;
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With