PostgreSQL - CTE upsert возвращает измененные строки

Я написал запрос 'upsert', используя CTE, который выглядит примерно так:

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
  )

Однако мне тогда нужно работать с новыми значениями в моем приложении, но этот запрос ничего не вернет. Добавление returning * в конец вставки вернет все строки, которые были вставлены, но ни одна из строк, которые были обновлены.

Итак, вопрос в том, как я могу расширить это, чтобы вернуть обновленные строки и строки, которые были вставлены?

EDIT: конечно, я мог бы запустить это, за которым следует SELECT в транзакции, однако мне любопытно посмотреть, есть ли способ с одним запросом.

Ответ 1

Попробуйте что-то вроде:

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 

Кстати, этот запрос не является классическим Postgres upsert. Он будет терпеть неудачу, если кто-то одновременно вставит строки, пока идет UPDATE table t.