插入多行时重复项会发生什么?

时间:2015-06-16 18:17:22

标签: sql postgresql exception duplicates upsert

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

for($i = 0;$i < 4;$i++) {
    echo '<div class="row">';
    for($j = 0;$j < 4;$j++) {
        echo '<div class="col-sm-3"></div>';
    }
    echo '</div>;'
}

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

2 个答案:

答案 0 :(得分:9)

INSERT只会插入所有行, 没有 特殊会发生,除非你有某种{{3禁止重复/重叠值(PRIMARY KEYUNIQUECHECKEXCLUDE约束) - 您在问题中未提及。但这就是你可能担心的事情。

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

通常,如果违反任何约束,则会引发异常(除非陷入像plpgsql这样的过程服务器端语言),不仅会回滚语句,还会回滚 < em>整个交易

没有并发写入

I.e。:没有其他交易会尝试同时写入同一张表。

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

  • 并且不要忘记删除插入的集合中的重复项,这将被半反连接{{1>排除}

同时处理这两种技术的一种方法是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;

Select rows which are not present in other table会在源中折叠重复的行。如果您知道没有欺骗,ALL或其他技术之一会更快。相关:

通常,如果目标表格,则源EXCEPT ALL与源WHERE NOT EXISTS结合可能会更快:

DISTINCT

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

相关:

使用并发写入

Postgres 9.5 或更高版本中使用Postgres 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 1 FROM tbl WHERE col1 = t.c1 AND col2 = t.c2 ); 实施Select rows which are not present in other table

UPSERT

更详细的相关答案:

文档:

Craig对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! 问题的参考答案:

答案 1 :(得分:1)

是否会停止整个查询并抛出异常?是。

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

你基本上应该这样做:

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