I've written an 'upsert' query using CTEs that looks something like this:
WITH
new_data (id, value) AS (
VALUES (1, 2), (3, 4), ...
),
updated AS (
UPDATE table t set
value = t.value + new_data.value
FROM new_data
WHERE t.id = new_data.id
RETURNING t.*
)
INSERT INTO table (id, value)
SELECT id, value
FROM new_data
WHERE NOT EXISTS (
SELECT 1 FROM updated WHERE updated.id = new_data.id
)
However I then need to work with the new values in my application, but this query will not return anything. Adding returning *
to the end of the insert will return all the rows that were inserted, but none of the rows that were updated.
So, the question is (how) can I extend this to return the rows that were updated AND the rows that were inserted?
EDIT: of course I could run this followed by a SELECT
in a transaction, however I'm curious to see if there's a single-query way.
PostgreSQL doesn't have any built-in UPSERT (or MERGE ) facility, and doing it efficiently in the face of concurrent use is very difficult.
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).
In PostgreSQL, the CTE(Common Table Expression) is used as a temporary result set that the user can reference within another SQL statement like SELECT, INSERT, UPDATE or DELETE. CTEs are temporary in the sense that they only exist during the execution of the query.
Use the ODBC Enterprise stage in upsert mode to insert, update, or delete records from an external data source table. You can also use the stage to insert and then update records or update and then insert records. You can match records based on field names.
Try something like:
WITH
new_data (id, value) AS (
VALUES (1, 2), (3, 4), ...
),
updated AS (
UPDATE table t set
value = t.value + new_data.value
FROM new_data
WHERE t.id = new_data.id
RETURNING t.*
),
inserted as (
INSERT INTO table (id, value)
SELECT id, value
FROM new_data
WHERE NOT EXISTS (
SELECT 1 FROM updated WHERE updated.id = new_data.id
)
RETURNING id, value)
SELECT id, value
FROM inserted
UNION ALL
SELECT id, value
FROM updated
BTW this query is not a classical Postgres upsert. It will fail, if someone concurrently insert rows while UPDATE table t
is going.
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