Suppose I have two tables in my Postgres database:
create table transactions
(
id bigint primary key,
doc_id bigint not null,
-- lots of other columns...
amount numeric not null
);
-- same columns
create temporary table updated_transactions
(
id bigint primary key,
doc_id bigint not null,
-- lots of other columns...
amount numeric not null
);
Both tables have just a primary key, and no unique indexes.
I need to upsert rows from updated_transactions
into transactions
using the following rules:
- id column values in
transactions
andupdated_transactions
don't match - other columns like
doc_id
, etc (except of theamount
) should match - when a matching row is found, update both
amount
andid
columns - when a matching row is not found, insert it
id
values in updated_transactions
are taken from a sequence.
A business object just populates updated_transactions
and then merges the
new or updated rows from it into transactions
using an upsert query.
So my old unchanged transactions keep their id
s intact, and the updated ones
are assigned new id
s.
In MSSQL and Oracle, it would be a merge
statement similar to this:
merge into transactions t
using updated_transactions ut on t.doc_id = ut.doc_id, ...
when matched then
update set t.id = ut.id, t.amount = ut.amount
when not matched then
insert (t.id, t.doc_id, ..., t.amount)
values (ut.id, ut.doc_id, ..., ut.amount);
In PostgreSQL, I suppose it should be something like this:
insert into transactions(id, doc_id, ..., amount)
select coalesce(t.id, ut.id), ut.doc_id, ... ut.amount
from updated_transactions ut
left join transactions t on t.doc_id = ut.doc_id, ....
on conflict
on constraint transactions_pkey
do update
set amount = excluded.amount, id = excluded.id
The problem is with the do update
clause: excluded.id
is an old value
from transactions
table, while I need a new value from updated_transactions
.
ut.id
value is inaccessible for the do update
clause, and the only thing I can
use is the excluded
row. But the excluded
row has only coalesce(t.id, ut.id)
expression which returns old id
values for the existing rows.
Is it possible to update both id
and amount
columns using the upsert query?