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_targetis 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_nameclause. Theconstraint_namemust be the name of a unique constraint.
- A WHEREclause.
 
- 
The conflict_actionis 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, use- DO UPDATE SET column_1 = value_1, .. WHERE conditionto 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 NOTHINGto do nothing:INSERT INTO users (nickname, login_name, notes) VALUES ('Tim2', 'tim', 'This is Tim2') ON CONFLICT (login_name) DO NOTHING;INSERT 0 0Here used the DO NOTHINGoption. Then, PostgreSQL returned normally and inserted 0 rows.
- 
Use DO UPDATEto 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 UPDATEclause, you can also use theEXCLUDEDobject 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_keyinstead 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.