INSERT 只会插入所有行,什么都不会发生,除非你有某种constraint 不允许重复/ 重叠值(PRIMARY KEY、UNIQUE、CHECK 或 EXCLUDE 约束)-您在问题中没有提到。但这就是您可能担心的问题。
假设UNIQUE 或(col1,col2) 上的PK 约束,您正在处理教科书UPSERT 的情况。在这里可以找到许多相关的问题和答案。
一般来说,如果 any 约束被违反,则会引发异常(除非陷入子事务中,就像在 plpgsql 这样的过程服务器端语言中可能发生的那样)不仅会回滚语句,还会回滚整个交易。
没有并发写入
即:没有其他事务会尝试同时写入同一个表。
同时处理这两种情况的一种技术是EXCEPT:
INSERT INTO tbl (col1, col2)
VALUES
(text 'v1', text 'v2') -- explicit type cast may be needed in 1st row
, ('v3', 'v4')
, ('v3', 'v4') -- beware of dupes in source
EXCEPT SELECT col1, col2 FROM tbl;
EXCEPT 没有关键字 ALL 会折叠源中的重复行。如果您知道没有重复项,或者您不想默默地折叠重复项,请使用EXCEPT ALL(或其他技术之一)。见:
一般来说,如果目标表是大,WHERE NOT EXISTS 结合源上的DISTINCT 可能会更快:
INSERT INTO tbl (col1, col2)
SELECT *
FROM (
SELECT DISTINCT *
FROM (
VALUES
(text 'v1', text'v2')
, ('v3', 'v4')
, ('v3', 'v4') -- dupes in source
) t(c1, c2)
) t
WHERE NOT EXISTS (
SELECT FROM tbl
WHERE col1 = t.c1 AND col2 = t.c2
);
如果可以有很多骗子,首先将它们折叠到源中是值得的。否则少用一个子查询。
相关:
并发写入
在 Postgres 9.5 或更高版本中使用 Postgres UPSERT 实现 INSERT ... ON CONFLICT ...:
INSERT INTO tbl (col1,col2)
SELECT DISTINCT * -- still can't insert the same row more than once
FROM (
VALUES
(text 'v1', text 'v2')
, ('v3','v4')
, ('v3','v4') -- you still need to fold dupes in source!
) t(c1, c2)
ON CONFLICT DO NOTHING; -- ignores rows with *any* conflict!
进一步阅读:
文档:
Craig 对UPSERT 问题的参考答案: