1

Follow the below script to create two tables source_t and dest_t.

drop table if exists source_t;

create table source_t as
(select 1 id, 'dummy1' varcol union
select 3,'dummy3');

drop table if exists dest_t;

create table dest_t as
(select 1 id, 'mmy' varcol
union select 2,'dummy2');

ALTER TABLE dest_t 
    ALTER id SET NOT NULL,  
    ALTER id ADD GENERATED ALWAYS AS identity;

   ALTER TABLE dest_t ADD CONSTRAINT UNQ_CON_dest_t unique  (id);

Now the content of two tables:

source_t:

enter image description here

dest_t:

enter image description here

1st case is when I want to update a record using upsert: Now my upsert statement:

insert into    dest_t 
select * from source_t
on conflict on constraint unq_con_dest_t 
do update set (varcol)=( select excluded.varcol);

The expectation is to just update varcol column for id = 1 in the dest_t table.

The error I am getting :

ERROR: cannot insert into column "id"
Detail: Column "id" is an identity column defined as GENERATED ALWAYS.

First question: Why am I getting insert error while the select query is returning only data which is the "updateable" candidate.

OK. Now I change my upsert as below to see if it gets fixed:

insert into    dest_t OVERRIDING SYSTEM VALUE
select * from source_t
on conflict on constraint unq_con_dest_t 
do update set (varcol)=( select excluded.varcol);

And even though this is not a fix, let's say I was able to proceed with the expected update.

2nd case:

let's insert a record in source_t as below, since this does not exist in dest_t this can be a "insert" candidate for dest_t :

insert into source_t values(null,'dummy0');

Running same upsert statement:

insert into    dest_t OVERRIDING SYSTEM VALUE
select * from source_t
on conflict on constraint unq_con_dest_t 
do update set (varcol)=( select excluded.varcol);

Now I get the below error:

ERROR: null value in column "id" violates not-null constraint

Yes, I understand it cannot insert null to not null column. I get the same error with or without using the "overriding" clause.

Second question: How do I make sure the upsert query automatically increments identity column when it is inserting and while updating it uses the existing value returned in select query of upsert. And identity column is the unique key.

Or is it not possible to use "UPSERT" when the unique key of the destination table is the "identity" column.?

9
  • Unrelated, but: set (varcol)=( select excluded.varcol); can be simplified to set varcol = excluded.varcol
    – user1822
    Commented Feb 7, 2022 at 8:45
  • It seems strange to me that you need to compare a generated value for the conflict detection. I would have assumed the varcol columns decides if it's a duplicate or not.
    – user1822
    Commented Feb 7, 2022 at 8:47
  • Hi @a_horse_with_no_name, There is a reason I added "select" there. When a table has only two columns with one as a unique key, then it does not allow you to just update one column using upsert. Strange but you can test as well. Use same script as above. ERROR : ERROR: source for a multiple-column UPDATE item must be a sub-SELECT or ROW() expression Commented Feb 7, 2022 at 8:55
  • @a_horse_with_no_name, in my case varcol column can have duplicate values. By the way, thank you for editing and formatting the question. Commented Feb 7, 2022 at 8:56
  • No, you don't need the SELECT: dbfiddle.uk/… - but using a generated value as the conflict resolution still seems like a really strange thing to do. As you have no real control over the generated values, how do you know that ID=1 in one table should really match ID=1 in the other? What if rows were deleted or an INSERT was rolled back? Then you'd have gaps in the numbers and the whole "matching on ID" doesn't work any longer.
    – user1822
    Commented Feb 7, 2022 at 9:01

0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Browse other questions tagged or ask your own question.