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

gpi*_*ino 129 database postgresql rules

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

什么是模仿的最好方式INSERT IGNORE,并ON DUPLICATE KEY UPDATE与PostgreSQL的?

war*_*ren 150

使用PostgreSQL 9.5,这是现在的本机功能(就像MySQL已经有几年了):

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;
Run Code Online (Sandbox Code Playgroud)

  • 不错的更新!我在已接受的答案上“广告”了您的答案。 (2认同)

Eog*_*anM 97

编辑:如果你错过了沃伦的回答,那么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";
Run Code Online (Sandbox Code Playgroud)

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

  • 哇,这就像一个魅力.它几乎和`INSERT IGNORE`一样好! (3认同)
  • @EoghanM我在postgresql 9.3中测试了规则,并且仍然可以插入带有多行插入语句的重复项,例如INSERT INTO"my_table"(a,b),(a,b); (假设"my_table"中不存在行(a,b).) (2认同)

Mag*_*der 31

尝试做一个更新.如果它没有修改任何意味着它不存在的行,那么插入也是如此.显然,您在事务中执行此操作.

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

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

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

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

  • *更新:*使用PostgreSQL 9.5现在就像`INSERT ...在冲突中没有任何东西那样简单;`.另请参阅答案http://stackoverflow.com/a/34639631/2091700. (22认同)
  • "如果你正在处理大量的行",这正是我的情况.我想批量更新/插入行,使用mysql我可以只使用一个查询而不进行任何循环.现在我想知道这是否也可以使用postgresql:只使用一个查询来批量更新或插入.你说:"你最好将它分成两个查询,一个用于INSERT,一个用于UPDATE",但是如何进行不会在重复键上抛出错误的插入?(即"INSERT IGNORE") (4认同)
  • Magnus意味着你使用这样的查询:"启动事务;创建临时表temporary_table作为select*from test where false; copy temporary_table from'data_file.csv'; lock table test; update test set = temporary_table.data from temporary_table where test.id = temporary_table.id;从test_table中插入test select*,其中id不在(从test中选择id)为" (4认同)
  • 所以最后的答案是"INSERT ...... WHERE":]谢谢. (2认同)

han*_*ari 24

对于那些拥有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;
Run Code Online (Sandbox Code Playgroud)

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

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;
Run Code Online (Sandbox Code Playgroud)

  • “ON CONFLICT (field_one) DO NOT HING”是答案中最好的部分。 (2认同)

Key*_*eyo 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)
)
Run Code Online (Sandbox Code Playgroud)

  • 就像一个FYI一样,"WHERE NOT EXISTS"技巧不适用于多个事务,因为不同的事务无法从其他事务中看到新添加的数据. (5认同)

小智 20

INSERT INTO mytable(col1,col2) 
    SELECT 'val1','val2' 
    WHERE NOT EXISTS (SELECT 1 FROM mytable WHERE col1='val1')
Run Code Online (Sandbox Code Playgroud)


小智 13

正如@hanmari 在他的评论中提到的。插入 postgres 表时,冲突 (..) do nothing 是用于不插入重复数据的最佳代码。:

query = "INSERT INTO db_table_name(column_name)
         VALUES(%s) ON CONFLICT (column_name) DO NOTHING;"
Run Code Online (Sandbox Code Playgroud)

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

q = 'ALTER id_column serial RESTART WITH 1'
Run Code Online (Sandbox Code Playgroud)

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


Bil*_*win 12

看起来PostgreSQL支持称为规则的模式对象.

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

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

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

  • PostgreSQL支持事务性DDL,这意味着如果您创建规则并将其放在单个事务中,该规则将永远不会在该事务之外可见(因此永远不会产生任何影响). (4认同)
  • 是的,我也有同样的问题.规则机制是我在PostgreSQL中找到的最接近MySQL的INSERT IGNORE或ON DUPLICATE KEY UPDATE的东西.如果我们谷歌搜索"重复密钥更新的postgresql",你会发现其他人推荐规则机制,即使规则适用于任何INSERT,而不仅仅是临时的. (3认同)

Num*_*our 5

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

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;
Run Code Online (Sandbox Code Playgroud)

但它有一个性能缺陷(请参阅PostgreSQL.org):

包含 EXCEPTION 子句的块比没有 EXCEPTION 子句的块进入和退出的成本要高得多。因此,不要在不需要的情况下使用 EXCEPTION。