I have a table with a single primary key. When I attempt to do an insert there may be a conflict caused by trying to insert a row with an existing key. I want to allow the insert to update all columns? Is there any easy syntax for this? I am trying to let it "upsert" all columns.
I am using PostgreSQL 9.5.5.
The
UPDATE
syntax requires to explicitly name target columns. Possible reasons to avoid that:"All columns"
has to mean "all columns of the target table" (or at least "leading columns of the table") in matching order and matching data type. Else you'd have to provide a list of target column names anyway.Test table:
1.
DELETE
&INSERT
in single query insteadWithout knowing any column names except
id
.Only works for "all columns of the target table". While the syntax even works for a leading subset, excess columns in the target table would be reset to NULL with
DELETE
andINSERT
.UPSERT (
INSERT ... ON CONFLICT ...
) is needed to avoid concurrency / locking issues under concurrent write load, and only because there is no general way to lock not-yet-existing rows in Postgres (value locking).Your special requirement only affects the
UPDATE
part. Possible complications do not apply where existing rows are affected. Those are locked properly. Simplifying some more, you can reduce your case toDELETE
andINSERT
:In the Postgres MVCC model, an
UPDATE
is largely the same asDELETE
andINSERT
anyway (except for some corner cases with concurrency, HOT updates, and big column values stored out of line). Since you want to replace all rows anyway, just remove conflicting rows before theINSERT
. Deleted rows remain locked until the transaction is committed. TheINSERT
might only find conflicting rows for previously non-existing key values if a concurrent transaction happens to insert them concurrently (after theDELETE
, but before theINSERT
).You would lose additional column values for affected rows in this special case. No exception raised. But if competing queries have equal priority, that's hardly a problem: the other query won for some rows. Also, if the other query is a similar UPSERT, its alternative is to wait for this transaction to commit and then updates right away. "Winning" could be a Pyrrhic victory.
About "empty updates":
No, my query must win!
OK, you asked for it:
How?
data
just provides data. Could be a table instead.ups
: UPSERT. Rows with conflictingid
are not changed, but also locked.del
deletes conflicting rows. They remain locked.ins
inserts whole rows. Only allowed for the same transactionTo check for empty updates test (before and after) with:
2. Dynamic SQL
This works for a subset of leading columns too, preserving existing values.
The trick is to let Postgres build the query string with column names from the system catalogs dynamically, and then execute it.
See related answers for code:
Update multiple columns in a trigger function in plpgsql
Bulk update of all columns
SQL update fields of one table from fields of another one