I want to update rows in my postgres database if the updated version wouldn't violate the primary key constraint. If it would, I want to leave the row as it is.
Assuming the table has primary keys on col1, col2, col3
, if I run a query like this:
UPDATE table SET (col1, col2) = ('A', 'B')
WHERE col1='D' AND col2='E';
The query will fail and I will get a duplicate key error if there exists two entries:
'A', 'B', 'C'
'D', 'E', 'C'
i.e col3
is the same between an existing row and a row to be updated.
If I was INSERT
ing rows I would use ON CONFLICT DO NOTHING
but I can't find an implementation of this for UPDATE
. Does an equivalent exist?
ON CONFLICT DO NOTHING simply avoids inserting a row as its alternative action. ON CONFLICT DO UPDATE updates the existing row that conflicts with the row proposed for insertion as its alternative action.
In relational databases, the term upsert is referred to as merge. The idea is that when you insert a new row into the table, PostgreSQL will update the row if it already exists, otherwise, it will insert the new row. That is why we call the action is upsert (the combination of update or insert).
Postgres now supports UPSERT - git.postgresql.org/gitweb/…
NOT EXISTS will still attempt to insert duplicates if these exists in the source (tables), and fail. ON CONFLICT IGNORE will insist (and succeed) on inserting/updating them, in an undefined order.
Notice that the ON CONFLICT clause is only available from PostgreSQL 9.5. If you are using an earlier version, you will need a workaround to have the upsert feature. If you are also working with MySQL, you will find that the upsert feature is similar to the insert on duplicate key update statement in MySQL.
We use the virtual EXCLUDED table, which contains the items we intended to insert, to update the name column to a new value on conflict. You can show that the records were all updated or added by typing: PostgreSQL's INSERT...ON CONFLICT construct allows you to choose between two options when a proposed record conflicts with an existing record.
DO UPDATE: This tells PostgreSQL that you want to update the row that is already in the table. The syntax for the update mirrors that of the normal UPDATE command. When DO UPDATE is specified, a special virtual table called EXCLUDED is available for use within the UPDATE clause.
DO NOTHING: Tells PostgreSQL to leave the conflicting record as-is. In essence, this action makes no changes, but suppresses the error that would normally occur if you tried to insert a row that violates a condition.
You can use a correlated subquery with a WHERE NOT EXISTS
clause to ensure that your update will not generate duplicates, like :
UPDATE mytable t
SET (col1, col2) = ('AAA', 'BBB')
WHERE t.col1 = 'AAB' and t.col2 = 'BBA'
AND NOT EXISTS (
SELECT 1 FROM mytable WHERE col1 = 'AAA' AND col2 = 'BBB' AND col3 = t.col3
);
Tested in this db fiddle.
As commented by Roman Konoval, please note that this would still generate duplicate key error if a concurrent transaction inserts the same key while the UPDATE
is running. This pinpoints that updating the primary key of a table is not a good practice (see the below answer from @Lau for a detailed discussion on this matter).
AFAIK, there is no such equivalent.
Let us say you are developing an application that connects to a postgresql database, there are a few things you need to keep in mind, in the context of your question:
on conflict
(update or nothing) so it makes sense to have a syntax to let you decide.ON CONFLICT ...
for inserts is not intended to update the primary key fields. The very opposite in fact: it is intended to update all the fields except the ones from the primary key in a single record.While I am on that point, please note that there was no need for a conflict on primary key for the query to fail
1 record with the "convenient" ON UPDATE NO ACTION
foreign key would have made it fail too (which is still better than updating 10M+ records in 50 tables with a ON UPDATE CASCADE
...). BTW, did you know Oracle does not even have the ON UPDATE CASCADE
clause? What do you think is the reason for that?
What can you/should not do in that situation?
UNIQUE
constraints but please please please, NEVER update primary keys.CHECK
or EXCLUSION
), will you really type the additional code it takes with no error in order to, once again, only avoid an error code?current transaction is aborted, commands ignored until end of transaction block
for everything.Here you go:
BEGIN;
SAVEPOINT MySavepoint;
UPDATE mytable set myuniquefield = 3; /*2+ records are going to be updated */
rollback to savepoint MySavepoint;
/*Insert Some more queries here*/
COMMIT;
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