ATUALIZAÇÃO (20-08-2015):
Agora existe uma implementação oficial para lidar com upserts através do uso de ON CONFLICT DO UPDATE
(documentação oficial). No momento da redação deste artigo, esse recurso atualmente reside no PostgreSQL 9.5 Alpha 2, que está disponível para download aqui: Diretórios de origem do Postgres .
Aqui está um exemplo, assumindo que item_id
é sua Chave Primária:
INSERT INTO my_table
(item_id, price)
VALUES
(123456, 10.99)
ON
CONFLICT (item_id)
DO UPDATE SET
price = EXCLUDED.price
Mensagem original ...
Aqui está uma implementação que cheguei ao desejar obter visibilidade sobre a ocorrência de uma inserção ou atualização.
A definição de upsert_data
é consolidar os valores em um único recurso, em vez de precisar especificar o preço e o item_id duas vezes: uma vez para a atualização e outra para a inserção.
WITH upsert_data AS (
SELECT
'19.99'::numeric(10,2) AS price,
'abcdefg'::character varying AS item_id
),
update_outcome AS (
UPDATE pricing_tbl
SET price = upsert_data.price
FROM upsert_data
WHERE pricing_tbl.item_id = upsert_data.item_id
RETURNING 'update'::text AS action, item_id
),
insert_outcome AS (
INSERT INTO
pricing_tbl
(price, item_id)
SELECT
upsert_data.price AS price,
upsert_data.item_id AS item_id
FROM upsert_data
WHERE NOT EXISTS (SELECT item_id FROM update_outcome LIMIT 1)
RETURNING 'insert'::text AS action, item_id
)
SELECT * FROM update_outcome UNION ALL SELECT * FROM insert_outcome
Se você não gosta do uso de upsert_data
, aqui está uma implementação alternativa:
WITH update_outcome AS (
UPDATE pricing_tbl
SET price = '19.99'
WHERE pricing_tbl.item_id = 'abcdefg'
RETURNING 'update'::text AS action, item_id
),
insert_outcome AS (
INSERT INTO
pricing_tbl
(price, item_id)
SELECT
'19.99' AS price,
'abcdefg' AS item_id
WHERE NOT EXISTS (SELECT item_id FROM update_outcome LIMIT 1)
RETURNING 'insert'::text AS action, item_id
)
SELECT * FROM update_outcome UNION ALL SELECT * FROM insert_outcome