How to merge (upsert & delete orphan rows) to tableA
?
tableA
:
+---------+--------+----------+-------+
| company | option | category | rates |
+---------+--------+----------+-------+
| a | f | null | 2.5 |
+---------+--------+----------+-------+
| a | f | d | 2 | *
+---------+--------+----------+-------+
| a | g | e | 3 | **
+---------+--------+----------+-------+
| c | g | e | 4 |
+---------+--------+----------+-------+
| d | f | d | 1 |
+---------+--------+----------+-------+
*
denotes orphan row*.
**
denotes value to change (3 -> 4).
Only touch companies existing in tableB
(a
& c
in the example, leave d
alone).
tableB
:
+---------+--------+----------+-------+
| company | option | category | rates |
+---------+--------+----------+-------+
| a | f | null | 2.5 |
+---------+--------+----------+-------+
| a | g | e | 4 |
+---------+--------+----------+-------+
| c | g | e | 4 |
+---------+--------+----------+-------+
There is a unique index on (company, option, category)
in both tables.
Desired resulting tableA
:
+---------+--------+----------+-------+
| company | option | category | rates |
+---------+--------+----------+-------+
| a | f | null | 2.5 |
+---------+--------+----------+-------+
| a | g | e | 4 | <-
+---------+--------+----------+-------+
| c | g | e | 4 |
+---------+--------+----------+-------+
| d | f | d | 1 |
+---------+--------+----------+-------+
Only the second row (a,f,d,2)
was deleted and rates
was changed from 3 to 4 for (a,g,e)
.
Here is a fiddle: https://rextester.com/QUVC30763
I'm thinking to first delete the orphan row with this:
DELETE from tableA
USING tableB
WHERE
-- ignore rows with IDs that don't exist in tableB
tableA.company = tableB.company
-- ignore rows that have an exact all-column match in tableB
AND NOT EXISTS
(select * from tableB
where tableB.company is not distinct from tableA.company
AND tableB.option is not distinct from tableA.option
AND tableB.category is not distinct from tableA.category );
Then upsert with this:
INSERT INTO tableA (company, option, category, rates)
SELECT company, option, category, rates
FROM tableB
ON CONFLICT (company, option, category)
DO update
set rates= EXCLUDED.rates
WHERE
tableA.rates IS DISTINCT FROM
EXCLUDED.rates;
But the problem with the upsert function is that it can't handle nullable fields. I have to set -1
in place of null
or else the function won't be able to know if there are duplicates or not. I feel like setting -1
in place of null
will create many workarounds in the future, so I'd like to avoid that if I can.
Note: I found that INSERT ... ON CONFLICT ... DO UPDATE
is probably the way to go:
But I haven't seen a query suitable for my case. And I'm not sure if it's possible with nullable fields. Hence the question:
Is there a clean way to merge with nullable fields?