bea*_*ind 9 sql database postgresql insert upsert
我有一个包含单个主键的表.当我尝试插入时,尝试插入具有现有密钥的行可能会导致冲突.我想允许插入更新所有列?这有什么简单的语法吗?我试图让它"upsert"所有列.
我正在使用PostgreSQL 9.5.5.
Erw*_*ter 13
该UPDATE
语法 需要明确命名目标列.避免这种情况的可能原因:
"All columns"
必须表示匹配顺序和匹配数据类型的"目标表的所有列"(或至少"表的前导列").否则,您必须提供目标列名称列表.
测试表:
CREATE TABLE tbl (
id int PRIMARY KEY
, text text
, extra text
);
INSERT INTO tbl AS t
VALUES (1, 'foo')
, (2, 'bar');
Run Code Online (Sandbox Code Playgroud)
DELETE
&INSERT
在单个查询,而不是不知道除了之外的任何列名id
.
仅适用于"目标表的所有列".虽然语法甚至适用于前导子集,但目标表中的多余列将使用DELETE
和重置为NULL INSERT
.
INSERT ... ON CONFLICT ...
需要UPSERT()来避免并发写入加载下的并发/锁定问题,并且只是因为没有通用方法来锁定Postgres中尚未存在的行(值锁定).
您的特殊要求仅影响UPDATE
零件.在现有行受到影响的情况下,可能会出现并发症.那些被正确锁定.简化一些,您可以将您的情况下减少DELETE
和INSERT
:
WITH data(id) AS ( -- Only 1st column gets explicit name!
VALUES
(1, 'foo_upd', 'a') -- changed
, (2, 'bar', 'b') -- unchanged
, (3, 'baz', 'c') -- new
)
, del AS (
DELETE FROM tbl AS t
USING data d
WHERE t.id = d.id
-- AND t <> d -- optional, to avoid empty updates
) -- only works for complete rows
INSERT INTO tbl AS t
TABLE data -- short for: SELECT * FROM data
ON CONFLICT (id) DO NOTHING
RETURNING t.id;
Run Code Online (Sandbox Code Playgroud)
在Postgres的MVCC模型,一个UPDATE
是大致相同DELETE
,并INSERT
反正(除了某些极端情况并发,HOT更新和存储脱节的大列值).由于您仍想要替换所有行,只需删除之前的冲突行INSERT
.在提交事务之前,已删除的行将保持锁定状态.将INSERT
可能只对以前不存在的键值找到冲突行,如果并发事务发生并发插入(在后DELETE
,但在此之前的INSERT
).
在这种特殊情况下,您将丢失受影响行的其他列值.没有例外.但是如果竞争查询具有相同的优先级,那么这几乎不是问题:另一个查询赢得了某些行.此外,如果另一个查询是类似的UPSERT,它的替代方法是等待此事务提交然后立即更新."获胜"可能是一场惨淡的胜利.
关于"空更新":
好的,你要求它:
WITH data(id) AS ( -- Only 1st column gets explicit name!
VALUES -- rest gets default names "column2", etc.
(1, 'foo_upd', NULL) -- changed
, (2, 'bar', NULL) -- unchanged
, (3, 'baz', NULL) -- new
, (4, 'baz', NULL) -- new
)
, ups AS (
INSERT INTO tbl AS t
TABLE data -- short for: SELECT * FROM data
ON CONFLICT (id) DO UPDATE
SET id = t.id
WHERE false -- never executed, but locks the row!
RETURNING t.id
)
, del AS (
DELETE FROM tbl AS t
USING data d
LEFT JOIN ups u USING (id)
WHERE u.id IS NULL -- not inserted !
AND t.id = d.id
-- AND t <> d -- avoid empty updates - only for full rows
RETURNING t.id
)
, ins AS (
INSERT INTO tbl AS t
SELECT *
FROM data
JOIN del USING (id) -- conflict impossible!
RETURNING id
)
SELECT ARRAY(TABLE ups) AS inserted -- with UPSERT
, ARRAY(TABLE ins) AS updated -- with DELETE & INSERT;
Run Code Online (Sandbox Code Playgroud)
怎么样?
data
只提供数据.可能是一张桌子.ups
:UPSERT.冲突的行id
不会更改,但也会被锁定.del
删除冲突的行.他们仍然被锁定.ins
插入整行.仅允许进行相同的交易要检查空更新测试(之前和之后):
SELECT ctid, * FROM tbl; -- did the ctid change?
Run Code Online (Sandbox Code Playgroud)
这也适用于前导列的子集,保留现有值.
诀窍是让Postgres动态地用系统目录中的列名构建查询字符串,然后执行它.
查看代码的相关答案:
归档时间: |
|
查看次数: |
6210 次 |
最近记录: |