插入多行时重复会发生啥?

Posted

技术标签:

【中文标题】插入多行时重复会发生啥?【英文标题】:What happens with duplicates when inserting multiple rows?插入多行时重复会发生什么? 【发布时间】:2015-09-01 17:17:31 【问题描述】:

我正在运行一个将大量数据插入 Postgres 数据库的 python 脚本,我使用单个查询来执行多行插入:

INSERT INTO table (col1,col2) VALUES ('v1','v2'),('v3','v4') ... etc

我想知道如果它碰到重复的插入键会发生什么。它会停止整个查询并抛出异常吗?或者它会仅仅忽略该特定行的插入并继续前进吗?

【问题讨论】:

缺少基本信息:Postgres 版本,表定义显示所有约束。 【参考方案1】:

INSERT 只会插入所有行,什么都不会发生除非你有某种constraint 不允许重复/ 重叠值(PRIMARY KEYUNIQUECHECKEXCLUDE 约束)-您在问题中没有提到。但这就是您可能担心的问题。

假设UNIQUE(col1,col2) 上的PK 约束,您正在处理教科书UPSERT 的情况。在这里可以找到许多相关的问题和答案。

一般来说,如果 any 约束被违反,则会引发异常(除非陷入子事务中,就像在 plpgsql 这样的过程服务器端语言中可能发生的那样)不仅会回滚语句,还会回滚整个交易

没有并发写入

即:没有其他事务会尝试同时写入同一个表。

使用WHERE NOT EXISTS ... 或任何其他适用技术排除表中已存在的行:

Select rows which are not present in other table

并且不要忘记删除插入集内的重复,这不会被半反连接WHERE NOT EXISTS ...

同时处理这两种情况的一种技术是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(或其他技术之一)。见:

Using EXCEPT clause in PostgreSQL

一般来说,如果目标表是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
   );

如果可以有很多骗子,首先将它们折叠到源中是值得的。否则少用一个子查询。

相关:

Select rows which are not present in other table

并发写入

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!

进一步阅读:

How to use RETURNING with ON CONFLICT in PostgreSQL? How do I insert a row which contains a foreign key?

文档:

The manual The commit page The Postgres Wiki page

Craig 对UPSERT 问题的参考答案:

How to UPSERT (MERGE, INSERT ... ON DUPLICATE UPDATE) in PostgreSQL?

【讨论】:

【参考方案2】:

它会停止整个查询并抛出异常吗?是的。

为避免这种情况,您可以查看以下 SO 问题 here,该问题描述了当数据库中已存在一些插入的键时,如何避免 Postgres 为多次插入引发错误。

你基本上应该这样做:

INSERT INTO DBtable
        (id, field1)
    SELECT 1, 'value'
    WHERE
        NOT EXISTS (
            SELECT id FROM DBtable WHERE id = 1
);

【讨论】:

以上是关于插入多行时重复会发生啥?的主要内容,如果未能解决你的问题,请参考以下文章

使用select将多行插入表中,但是表在oracle SQL中具有主键[重复]

Oracle中的多行插入查询(从一张表中选择多行并插入到另一张表中[重复]

如何在WinForms中制作自动滚动多行TextBox? [重复]

插入多行查询失败[重复]

用一个查询插入多行[重复]

使用内爆在sqlite中插入多行[重复]