Pau*_*yot 22 postgresql upsert postgresql-9.5
可写CTE被认为是9.5之前的UPSERT的解决方案,如插入,PostgreSQL中的重复更新中所述?
可以执行带有以下信息的UPSERT,无论它是以UPDATE还是INSERT与以下可写CTE惯用语:
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)
Run Code Online (Sandbox Code Playgroud)
此查询将返回"已更新"或"已插入",或者可能(很少)因违反约束而失败,如https://dba.stackexchange.com/questions/78510/why-is-cte-open-to中所述-lost-更新
使用PostgreSQL 9.5+新的"UPSERT"语法可以实现类似的功能,从优化中获益并避免可能的约束违规吗?
Vao*_*sun 15
我认为xmax::text::int > 0这是最简单的伎俩:
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
Run Code Online (Sandbox Code Playgroud)
从图形@ lad2025的答案,结果可能滥用实现设置和自定义选项与相关功能在WHERE子句来获得所需的副作用.
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";
Run Code Online (Sandbox Code Playgroud)
第三个参数set_config是is_local:true表示设置将在交易结束时消失.更准确地说,current_setting('upsert.action')将返回NULL(并且不会抛出错误)直到会话结束.
在SQL Server MERGE 语句$action中返回字符串'INSERT', 'UPDATE', or 'DELETE'.
因为Postgresql我找不到类似的功能/变量RETURNING.
解决方法的一种方法是向is_updated表中添加列:
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;
Run Code Online (Sandbox Code Playgroud)
输出:
????????????????????????
? id ? col ? action ?
????????????????????????
? 3 ? c ? INSERTED ?
? 4 ? d ? INSERTED ?
? 1 ? aaaa ? UPDATED ?
????????????????????????
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
3776 次 |
| 最近记录: |