PostgreSQL INSERT ON CONFLICT
This article describes how the PostgreSQL INSERT ON CONFLICT
statement handles conflict situations when inserting rows.
PostgreSQL INSERT ON CONFLICT
statements allow you to handle some data conflicts when inserting rows. If there is no conflict, insert rows normally, and if there is a conflict, the existing rows will be updated. That is to say, the INSERT ON CONFLICT
statement implements the upsert function.
This INSERT ON CONFLICT
statement was introduced in PostgreSQL 9.5.
PostgreSQL INSERT ON CONFLICT
syntax
To implement the upsert function in PostgreSQL, use the INSERT ON CONFLICT
statement as follows:
INSERT INTO table_name(column_list)
VALUES(value_list)
ON CONFLICT conflict_target conflict_action
[RETURNING {* | column_names}];;
Compared with the INSERT
statement, there is only one more ON CONFLICT
clause in INSERT ON CONFLICT
.
In this syntax:
-
The
conflict_target
is the object where happends conflicts, which can be one of the following:- A column name. The column must be a primary key or unique index.
- The
ON CONSTRAINT constraint_name
clause. Theconstraint_name
must be the name of a unique constraint. - A
WHERE
clause.
-
The
conflict_action
is the action to take if there is a conflict, it can be one of the following:DO NOTHING
: If there is a conflict, take no action.DO UPDATE
: If there is a conflict, useDO UPDATE SET column_1 = value_1, .. WHERE condition
to update the columns in the table.
PostgreSQL INSERT ON CONFLICT
Examples
We are going to demonstrate the following example in the testdb
database. Please use the following statement to create a database named testdb
:
CREATE DATABASE testdb;
Connect to the testdb
database as the current database:
\c testdb;
To demonstrate, use the following statement to create a new table, named users
:
DROP TABLE IF EXISTS users;
CREATE TABLE users (
id SERIAL PRIMARY KEY,
nickname VARCHAR(50) NOT NULL,
login_name VARCHAR(50) UNIQUE,
notes VARCHAR(255)
);
Here, the users
table has id
, nickname
, login_name
, and notes
four columns, where login_name
is a unique index column.
Insert some rows into the users
table using the INSERT
statement:
INSERT INTO
users (nickname, login_name, notes)
VALUES
('Tim', 'tim', 'This is Tim'),
('Tom', 'tom', 'This is Tom');
Insert a new row with a duplicate login_name
of the existing row:
INSERT INTO
users (nickname, login_name, notes)
VALUES
('Tim2', 'tim', 'This is Tim2');
ERROR: duplicate key value violates unique constraint "users_login_name_key"
DETAIL: Key (login_name)=(tim) already exists.
Here, PostgreSQL gaven a error for the duplicate value.
You can try again using the INSERT ON CONFLICT
statement to take some action if there are duplicate login_name
. You can take two actions:
-
Use
DO NOTHING
to do nothing:INSERT INTO users (nickname, login_name, notes) VALUES ('Tim2', 'tim', 'This is Tim2') ON CONFLICT (login_name) DO NOTHING;
INSERT 0 0
Here used the
DO NOTHING
option. Then, PostgreSQL returned normally and inserted 0 rows. -
Use
DO UPDATE
to update the existing rows:INSERT INTO users (nickname, login_name, notes) VALUES ('Tim2', 'tim', 'This is Tim2') ON CONFLICT (login_name) DO UPDATE SET nickname = 'Tim2', notes = 'This is Tim2' RETURNING *;
id | nickname | login_name | notes ----+----------+------------+-------------- 1 | Tim2 | tim | This is Tim2 (1 row)
In the
DO UPDATE
clause, you can also use theEXCLUDED
object to refer the data that caused the conflict. The above statement can be modified to the following statement usingEXCLUDED
:INSERT INTO users (nickname, login_name, notes) VALUES ('Tim2', 'tim', 'This is Tim2') ON CONFLICT (login_name) DO UPDATE SET nickname = EXCLUDED.nickname, notes = EXCLUDED.notes RETURNING *;
For the conflict objects, You can also use constraint names instead of column names. The above statement can use constraint names
users_login_name_key
instead of column nameslogin_name
:INSERT INTO users (nickname, login_name, notes) VALUES ('Tim3', 'tim', 'This is Tim3') ON CONFLICT ON CONSTRAINT users_login_name_key DO UPDATE SET nickname = EXCLUDED.nickname, notes = EXCLUDED.notes RETURNING *;
id | nickname | login_name | notes ----+----------+------------+-------------- 1 | Tim3 | tim | This is Tim3 (1 row)
Conclusion
PostgreSQL INSERT ON CONFLICT
implements the upsert feature so that you can INSERT
and UPDATE
in one query.