How to find out if an upsert was an update with PostgreSQL 9.5+ UPSERT?
可写CTE被认为是9.5之前的UPSERT的解决方案,如插入,PostgreSQL中的重复更新中所述?
可以执行带有以下信息的UPSERT,无论它是以UPDATE还是INSERT与以下可写CTE惯用语:
1 2 3 4 5 6 7 8 9 | WITH update_cte AS ( UPDATE t SET v = $1 WHERE id = $2 RETURNING 'updated'::text STATUS ), insert_cte AS ( INSERT INTO t(id, v) SELECT $2, $1 WHERE NOT EXISTS (SELECT 1 FROM update_cte) RETURNING 'inserted'::text STATUS ) (SELECT STATUS FROM update_cte) UNION (SELECT STATUS FROM insert_cte) |
此查询将返回"已更新"或"已插入",或者可能(很少)因违反约束而失败,如https://dba.stackexchange.com/questions/78510/why-is-cte-open-to中所述-lost-更新
使用PostgreSQL 9.5+新的"UPSERT"语法可以实现类似的功能,从优化中获益并避免可能的约束违规吗?
我相信
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 | so=# DROP TABLE IF EXISTS tab; NOTICE: TABLE"tab" does NOT exist, skipping DROP TABLE so=# CREATE TABLE tab(id INT PRIMARY KEY, col text); CREATE TABLE so=# INSERT INTO tab(id, col) VALUES (1,'a'), (2, 'b'); INSERT 0 2 so=# INSERT INTO tab(id, col) VALUES (3, 'c'), (4, 'd'), (1,'aaaa') ON CONFLICT (id) DO UPDATE SET col = EXCLUDED.col returning *,CASE WHEN xmax::text::INT > 0 THEN 'updated' ELSE 'inserted' END,ctid; id | col | CASE | ctid ----+------+----------+------- 3 | c | inserted | (0,3) 4 | d | inserted | (0,4) 1 | aaaa | updated | (0,5) (3 ROWS) INSERT 0 3 so=# INSERT INTO tab(id, col) VALUES (3, 'c'), (4, 'd'), (1,'aaaa') ON CONFLICT (id) DO UPDATE SET col = EXCLUDED.col returning *,CASE WHEN xmax::text::INT > 0 THEN 'updated' ELSE 'inserted' END,ctid; id | col | CASE | ctid ----+------+---------+------- 3 | c | updated | (0,6) 4 | d | updated | (0,7) 1 | aaaa | updated | (0,8) (3 ROWS) INSERT 0 3 |
根据@ lad2025的答案,可以通过滥用WHERE子句中的相关函数的设置和自定义选项来实现结果,以获得所需的副作用。
1 2 3 4 5 6 7 8 9 | CREATE TABLE t(id INT PRIMARY KEY, v TEXT); INSERT INTO t (id, v) SELECT $1, $2 WHERE 'inserted' = set_config('upsert.action', 'inserted', TRUE) ON CONFLICT (id) DO UPDATE SET v = EXCLUDED.v WHERE 'updated' = set_config('upsert.action', 'updated', TRUE) RETURNING current_setting('upsert.action') AS"upsert.action"; |
在
对于
解决方法的一种方法是将列
1 2 3 4 5 6 7 8 9 10 11 12 13 | DROP TABLE IF EXISTS tab; CREATE TABLE tab(id INT PRIMARY KEY, col VARCHAR(100), is_updated BOOLEAN DEFAULT FALSE); INSERT INTO tab(id, col) VALUES (1,'a'), (2, 'b'); -- main query INSERT INTO tab(id, col) VALUES (3, 'c'), (4, 'd'), (1,'aaaa') ON CONFLICT (id) DO UPDATE SET col = EXCLUDED.col, is_updated = TRUE RETURNING id,col, CASE WHEN is_updated THEN 'UPDATED' ELSE 'INSERTED' END AS action; |
Rextester演示
输出:
1 2 3 4 5 6 7 | ╔════╦══════╦══════════╗ ║ id ║ col ║ action ║ ╠════╬══════╬══════════╣ ║ 3 ║ c ║ INSERTED ║ ║ 4 ║ d ║ INSERTED ║ ║ 1 ║ aaaa ║ UPDATED ║ ╚════╩══════╩══════════╝ |