如何更新表中的行或如果它不存在则插入它?

Remy Blank 79 mysql sql sqlite postgresql upsert

我有下表的柜台:

CREATE TABLE cache (
    key text PRIMARY KEY,
    generation int
);

我想增加其中一个计数器,或者如果相应的行还不存在则将其设置为零.有没有办法在标准SQL中没有并发问题的情况下执行此操作?该操作有时是交易的一部分,有时是分开的.

如果可能的话,SQL必须在SQLite,PostgreSQL和MySQL上不加修改地运行.

搜索产生了一些想法,这些想法要么遇到并发问题,要么特定于数据库:

  • 尝试换INSERT行,UPDATE如果出现错误.不幸的是,INSERT中止当前事务的错误.

  • UPDATE行,如果没有修改行,则为INSERT新行.

  • MySQL有一个ON DUPLICATE KEY UPDATE条款.

编辑:感谢所有的好评.看起来保罗是对的,并没有单一,便携的方式来做到这一点.这对我来说非常令人惊讶,因为它听起来像是一个非常基本的操作.

andygeers.. 131

MySQL(以及随后的SQLite)也支持REPLACE INTO语法:

REPLACE INTO my_table (pk_id, col1) VALUES (5, '123');

这会自动识别主键并找到要更新的匹配行,如果没有找到则插入新行.

  • 重要的是要理解它是插入+删除而永远不会更新.这样做的结果是,您总是希望确保在进行替换时,应始终包含所有字段的数据. (85认同)
  • 实际上,具体来说,MySQL的REPLACE总是进行插入,但是如果它已经存在,它将首先删除该行.http://dev.mysql.com/doc/refman/4.1/en/replace.html (12认同)
  • >(以及后来的SQLite)......你完全从我的回答中偷走了:-) (4认同)
  • 对不起 - 希望不被认为粗鲁? (3认同)
  • @Zoredache从技术上讲,它是一个'删除,然后插入',因为(n)'insert + delete'实际上与删除相同但是分裂头发. (2认同)

Kyle Cronin.. 30

SQLite支持替换已存在的行:

INSERT OR REPLACE INTO [...blah...]

你可以缩短这个

REPLACE INTO [...blah...]

添加此快捷方式以与MySQL REPLACE INTO表达式兼容.


jmoz.. 19

我会做类似以下的事情:

INSERT INTO cache VALUES (key, generation)
ON DUPLICATE KEY UPDATE (key = key, generation = generation + 1);

在代码或sql中将生成值设置为0,但使用ON DUP ...来增加值.无论如何,我认为这就是语法.


Fire Crow.. 9

ON DUPLICATE KEY UPDATE子句是最好的解决方案,因为:REPLACE执行一个DELETE后跟一个INSERT,所以在一个非常小的时间段内删除记录会产生一个很小的可能性,如果页面是,那么查询可以返回跳过在REPLACE查询期间查看.

我更喜欢INSERT ...在DUPLICATE UPDATE ...出于这个原因.

jmoz的解决方案是最好的:虽然我更喜欢SET语法到括号

INSERT INTO cache 
SET key = 'key', generation = 'generation'
ON DUPLICATE KEY 
UPDATE key = 'key', generation = (generation + 1)
;

  • REPLACE是原子的,因此没有行不存在的时间段. (4认同)

BradC.. 8

我不知道你会找到一个平台中立的解决方案.

这通常称为"UPSERT".

看一些相关的讨论:


用户甲.. 5

在PostgreSQL中没有合并命令,实际上编写它并不是一件容易的事 - 实际上有一些奇怪的边缘情况使得任务"有趣".

最好的(如:在最可能的条件下工作)方法是使用函数 - 例如手动(merge_db)中显示的函数.

如果您不想使用功能,通常可以逃脱:

updated = db.execute(UPDATE ... RETURNING 1)
if (!updated)
  db.execute(INSERT...)

请记住,它不是故障证明,最终失败.