Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

PostgreSQL Upsert with a WHERE clause

I am trying to migrate an Oracle merge query to PostgreSql. As described in this article, Postgres UPSERT syntax supports a "where clause" to identify conditions of conflict.

Unfortunately, that webpage does not provide an example with the "where clause". I tried searching for it elsewhere but could not find it. Hence this question.

Following the same example in the above given webpage, here is an example setup:

CREATE TABLE customers (
    customer_id serial PRIMARY KEY,
    name VARCHAR UNIQUE,
    email VARCHAR NOT NULL,
    active bool NOT NULL DEFAULT TRUE
);

INSERT INTO customers (NAME, email) VALUES
 ('IBM', '[email protected]'),
 ('Microsoft', '[email protected]'),
 ('Intel','[email protected]');


SELECT * FROM customers;
 customer_id |   name    |         email         | active
-------------+-----------+-----------------------+--------
           1 | IBM       | [email protected]       | t
           2 | Microsoft | [email protected] | t
           3 | Intel     | [email protected]     | t
(3 rows)

I want my UPSERT statement to look something like this:

INSERT INTO customers (NAME, email)
VALUES
('Microsoft', '[email protected]') 
ON CONFLICT where (name = 'Microsoft' and active = TRUE)
DO UPDATE SET email = '[email protected]';

The example is a bit contrived but I hope I have been able to communicate the gist here.

like image 722
DaSarfyCode Avatar asked Dec 13 '17 06:12

DaSarfyCode


People also ask

Does Postgres support upsert?

PostgreSQL lets you either add or modify a record within a table depending on whether the record already exists. This is commonly known as an "upsert" operation (a portmanteau of "insert" and "update").

How does upsert work in PostgreSQL?

In PostgreSQL, the UPSERT operation means either UPDATE or INSERT operation. The UPSERT operation allows us to either insert a row or skip the insert operation if a row already exists and update that row instead. Suppose you want to insert bulk data from one table to another table that already has some data.

Is upsert same as Merge?

A relational database management system uses SQL MERGE (also called upsert) statements to INSERT new records or UPDATE existing records depending on whether condition matches. It was officially introduced in the SQL:2003 standard, and expanded in the SQL:2008 standard.

What is the purpose of where clause in PostgreSQL?

The PostgreSQL WHERE clause is used to specify a condition while fetching the data from single table or joining with multiple tables. If the given condition is satisfied, only then it returns specific value from the table.


1 Answers

You need a partial index. Drop uniqe constraint on the column name and create a partial index on the column:

CREATE TABLE customers (
    customer_id serial PRIMARY KEY,
    name VARCHAR,
    email VARCHAR NOT NULL,
    active bool NOT NULL DEFAULT TRUE
);

CREATE UNIQUE INDEX ON customers (name) WHERE active;

INSERT INTO customers (NAME, email) VALUES
 ('IBM', '[email protected]'),
 ('Microsoft', '[email protected]'),
 ('Intel','[email protected]');

The query should look like this:

INSERT INTO customers (name, email)
VALUES
    ('Microsoft', '[email protected]') 
ON CONFLICT (name) WHERE active
DO UPDATE SET email = excluded.email;

SELECT *
FROM customers;

 customer_id |   name    |         email         | active 
-------------+-----------+-----------------------+--------
           1 | IBM       | [email protected]       | t
           3 | Intel     | [email protected]     | t
           2 | Microsoft | [email protected] | t
(3 rows)    

Note the proper use of the special record excluded. Per the documentation:

The SET and WHERE clauses in ON CONFLICT DO UPDATE have access to the existing row using the table's name (or an alias), and to rows proposed for insertion using the special excluded table.

like image 139
klin Avatar answered Oct 13 '22 17:10

klin