Reputation: 11
I noticed a postgresql table with a primary key id field of type bigserial had an id with a value in the millions but only had a COUNT showing a few hundred records. The table was being called regularly with an upsert statment similar to the following:
INSERT INTO test_table (ticker,column1) VALUES ('a','1') ON CONFLICT (ticker) DO UPDATE SET column1=EXCLUDED.column1;
And I found that if the ticker already existed, the sequence was being updated and when a record didn't already exist, the upsert would give the id the value of the sequence last_value + 1.
This did not seem like normal behavior to me that the sequence last_value would be incremented when an upsert statement updated a record instead of inserting it.
The only workaround I can think of is to reset the sequence after every upsert statement with a statement something like this:
SELECT setval('test_table_id_seq', (SELECT MAX(id) FROM test_table));
Any input would be welcome.
To look into this behavior I created a simple db with 1 table as follows:
CREATE TABLE public.test_table (
id bigserial NOT NULL,
ticker varchar(10) NOT NULL,
column1 varchar(10) NULL,
CONSTRAINT test_table_pk PRIMARY KEY (id),
CONSTRAINT test_table_unique UNIQUE (ticker)
);
This automatically created a sequence with the name test_table_id_seq. As expected the last_value field in test_table_id_seq was 1.
Then I used an upsert statement to insert a record because it didn't already exist:
INSERT INTO test_table (ticker,column1) VALUES ('a','1') ON CONFLICT (ticker) DO UPDATE SET column1=EXCLUDED.column1;
As expected the last_value field in test_table_id_seq was still 1 and the primary key id field for the new record in the test_table was 1.
Then I ran some statements updating the existing record:
INSERT INTO test_table (ticker,column1) VALUES ('a','2') ON CONFLICT (ticker) DO UPDATE SET column1=EXCLUDED.column1;
INSERT INTO test_table (ticker,column1) VALUES ('a','3') ON CONFLICT (ticker) DO UPDATE SET column1=EXCLUDED.column1;
INSERT INTO test_table (ticker,column1) VALUES ('a','4') ON CONFLICT (ticker) DO UPDATE SET column1=EXCLUDED.column1;
And then saw the unexpected behavior where the last_value in test_table_id_seq had been incremented to 4 but should have been 1 because no new records were inserted and the highest value in the primary key id field of the test_table was 1, not 4.
And of course when I added a new record as follows it had the id value of 5 because the sequence last_value was 4:
INSERT INTO test_table (ticker,column1) VALUES ('b','1') ON CONFLICT (ticker) DO UPDATE SET column1=EXCLUDED.column1;
I was expecting that updating an existing record would not increment the sequence so that when a new record is inserted it would have a primary key id equal to the highest previous id + 1. So this behavior was a surprise.
Or course when I redid the entire experiment but put the following statement after every single upsert statement, the id field values were all incremented by 1 as I originally had expected:
SELECT setval('test_table_id_seq', (SELECT MAX(id) FROM test_table));
Is this behavior normal? Am I missing something?
Upvotes: 1
Views: 270