sha*_*unc 41 postgresql plpgsql upsert
我已经阅读了UPSERTPostgreSQL 中的不同实现,但所有这些解决方案都相对较旧或相对奇特(例如,使用可写 CTE)。
而且我根本不是 psql 专家,无法立即找出这些解决方案是否过时,因为它们被很好地推荐,或者它们(好吧,几乎所有这些都是)只是不适合生产使用的玩具示例。
在 PostgreSQL 中实现 UPSERT 的最线程安全的方法是什么?
Jos*_*rns 28
更新(2015-08-20):
现在有一个通过使用ON CONFLICT DO UPDATE(官方文档)处理 upsert 的官方实现。在撰写本文时,此功能目前位于 PostgreSQL 9.5 Alpha 2 中,可在此处下载:Postgres 源目录。
这是一个示例,假设item_id是您的主键:
INSERT INTO my_table
(item_id, price)
VALUES
(123456, 10.99)
ON
CONFLICT (item_id)
DO UPDATE SET
price = EXCLUDED.price
Run Code Online (Sandbox Code Playgroud)
原帖...
这是我在希望了解是否发生插入或更新时遇到的一个实现。
的定义upsert_data是将值合并到单个资源中,而不必指定 price 和 item_id 两次:一次用于更新,一次用于插入。
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
Run Code Online (Sandbox Code Playgroud)
如果您不喜欢使用upsert_data,这里有一个替代实现:
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
Run Code Online (Sandbox Code Playgroud)
Lei*_*fel 24
PostgreSQL 现在有UPSERT。
根据类似 StackOverflow 问题的首选方法目前如下:
CREATE TABLE db (a INT PRIMARY KEY, b TEXT);
CREATE FUNCTION merge_db(key INT, data TEXT) RETURNS VOID AS
$$
BEGIN
LOOP
-- first try to update the key
UPDATE db SET b = data WHERE a = key;
IF found THEN
RETURN;
END IF;
-- not there, so try to insert the key
-- if someone else inserts the same key concurrently,
-- we could get a unique-key failure
BEGIN
INSERT INTO db(a,b) VALUES (key, data);
RETURN;
EXCEPTION WHEN unique_violation THEN
-- do nothing, and loop to try the UPDATE again
END;
END LOOP;
END;
$$
LANGUAGE plpgsql;
SELECT merge_db(1, 'david');
SELECT merge_db(1, 'dennis');
Run Code Online (Sandbox Code Playgroud)