如何使用postgresql模拟“插入忽略”和“重复密钥更新”(sql merge)?

时间:2009-06-17 21:23:48

标签: database postgresql rules

某些SQL服务器具有一个功能,如果它违反主/唯一键约束,则跳过INSERT。例如,MySQL有INSERT IGNORE

使用PostgreSQL模拟INSERT IGNOREON DUPLICATE KEY UPDATE的最佳方式是什么?

11 个答案:

答案 0 :(得分:130)

使用PostgreSQL 9.5,现在是native functionality(好像MySQL has had多年):

  

INSERT ......关于冲突没有/更新(" UPSERT")

     9.5支持" UPSERT"操作。   INSERT扩展为接受ON CONFLICT DO UPDATE / IGNORE子句。该子句规定了在可能发生重复违规时采取的替代行动。

...

  

新语法的进一步示例:

INSERT INTO user_logins (username, logins)
VALUES ('Naomi',1),('James',1) 
ON CONFLICT (username)
DO UPDATE SET logins = user_logins.logins + EXCLUDED.logins;

答案 1 :(得分:95)

编辑:如果你错过了warren's answer,PG9.5现在就有了这个原因;时间升级!


在Bill Karwin的回答的基础上,阐明基于规则的方法是什么样的(从同一个数据库中的另一个模式转移,并使用多列主键):

CREATE RULE "my_table_on_duplicate_ignore" AS ON INSERT TO "my_table"
  WHERE EXISTS(SELECT 1 FROM my_table 
                WHERE (pk_col_1, pk_col_2)=(NEW.pk_col_1, NEW.pk_col_2))
  DO INSTEAD NOTHING;
INSERT INTO my_table SELECT * FROM another_schema.my_table WHERE some_cond;
DROP RULE "my_table_on_duplicate_ignore" ON "my_table";

注意:该规则适用于所有INSERT操作,直到规则被删除,因此不是特别的。

答案 2 :(得分:30)

尝试进行更新。如果它没有修改任何意味着它不存在的行,那么插入也是如此。显然,你在交易中这样做。

如果您不想在客户端添加额外的代码,您当然可以将其包装在一个函数中。在这种想法中,你还需要一个非常罕见的竞争条件的循环。

文档中有一个例子:http://www.postgresql.org/docs/9.3/static/plpgsql-control-structures.html,示例40-2就在底部。

这通常是最简单的方法。你可以用规则做一些魔术,但它可能会变得更加混乱。我建议在任何一天使用包裹式功能。

这适用于单行或几行值。如果你正在处理大量的行,例如来自子查询,你最好把它分成两个查询,一个用于INSERT,一个用于UPDATE(当然是一个适当的连接/子选择 - 不需要写你的主要过滤两次)

答案 3 :(得分:23)

要获得插入忽略逻辑,您可以执行以下操作。我发现只是从文字值的select语句插入效果最好,然后你可以用NOT EXISTS子句掩盖重复的键。为了获得重复逻辑的更新,我怀疑需要一个pl / pgsql循环。

INSERT INTO manager.vin_manufacturer
(SELECT * FROM( VALUES
  ('935',' Citroën Brazil','Citroën'),
  ('ABC', 'Toyota', 'Toyota'),
  ('ZOM',' OM','OM')
  ) as tmp (vin_manufacturer_id, manufacturer_desc, make_desc)
  WHERE NOT EXISTS (
    --ignore anything that has already been inserted
    SELECT 1 FROM manager.vin_manufacturer m where m.vin_manufacturer_id = tmp.vin_manufacturer_id)
)

答案 4 :(得分:23)

对于那些拥有Postgres 9.5或更高版本的人来说,新的 ON CONFLICT DO NOTHING 语法应该有效:

INSERT INTO target_table (field_one, field_two, field_three ) 
SELECT field_one, field_two, field_three
FROM source_table
ON CONFLICT (field_one) DO NOTHING;

对于我们这些拥有早期版本的人来说,这种正确的联接将起作用:

INSERT INTO target_table (field_one, field_two, field_three )
SELECT source_table.field_one, source_table.field_two, source_table.field_three
FROM source_table 
LEFT JOIN target_table ON source_table.field_one = target_table.field_one
WHERE target_table.field_one IS NULL;

答案 5 :(得分:20)

INSERT INTO mytable(col1,col2) 
    SELECT 'val1','val2' 
    WHERE NOT EXISTS (SELECT 1 FROM mytable WHERE col1='val1')

答案 6 :(得分:12)

看起来PostgreSQL支持名为规则的模式对象。

http://www.postgresql.org/docs/current/static/rules-update.html

您可以为给定的表创建规则ON INSERT,如果存在具有给定主键值的行,则执行NOTHING,或者使其执行UPDATE而不是如果存在具有给定主键值的行,则为INSERT

我自己没有尝试过,所以我不能从经验中说话或提供一个例子。

答案 7 :(得分:3)

正如@hanmari在评论中提到的那样。当插入postgres表时,on conflict(..)什么都不做是用于不插入重复数据的最佳代码。:

query = "INSERT INTO db_table_name(column_name)
         VALUES(%s) ON CONFLICT (column_name) DO NOTHING;"

ON CONFLICT代码行将允许insert语句仍然插入数据行。查询和值代码是从Excel插入postgres db表的日期示例。 我在postgres表中添加了约束,用于确保ID字段是唯一的。我没有对相同的数据行执行删除操作,而是添加了一行sql代码,用于重新编号从1开始的ID列。 例如:

q = 'ALTER id_column serial RESTART WITH 1'

如果我的数据有ID字段,我不会将其用作主ID /序列ID,我创建一个ID列并将其设置为serial。 我希望这些信息对每个人都有帮助。 *我没有软件开发/编码的大学学位。我在编码时所知道的一切,都是我自己研究的。

答案 8 :(得分:2)

此解决方案避免使用规则:

BEGIN
   INSERT INTO tableA (unique_column,c2,c3) VALUES (1,2,3);
EXCEPTION 
   WHEN unique_violation THEN
     UPDATE tableA SET c2 = 2, c3 = 3 WHERE unique_column = 1;
END;

但它有性能缺陷(见PostgreSQL.org):

  

包含EXCEPTION子句的块要贵得多   进入和退出比没有一个的块。因此,请勿使用   不需要例外。

答案 9 :(得分:1)

批量处理时,您始终可以在插入之前删除该行。删除不存在的行不会导致错误,因此可以安全地跳过它。

答案 10 :(得分:-1)

对于数据导入脚本,要取代“IF NOT EXISTS”,在某种程度上,有一个稍微尴尬的表述仍然有效:

DO
$do$
BEGIN
PERFORM id
FROM whatever_table;

IF NOT FOUND THEN
-- INSERT stuff
END IF;
END
$do$;