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

时间:2009-03-27 17:07:58

标签: 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子句。

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

10 个答案:

答案 0 :(得分:131)

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

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

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

答案 1 :(得分:30)

SQLite支持replacing a row(如果它已存在):

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

您可以将此缩短为

REPLACE INTO [...blah...]

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

答案 2 :(得分:19)

我会做以下事情:

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

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

答案 3 :(得分: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)
;

答案 4 :(得分:8)

我不知道你会找到一个与平台无关的解决方案。

这通常被称为“UPSERT”。

参见一些相关的讨论:

答案 5 :(得分:5)

在PostgreSQL中没有合并命令,实际上编写它并非易事 - 实际上有一些奇怪的边缘情况使得任务“有趣”。

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

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

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

请记住,这不是故障证明, 最终会失败。

答案 6 :(得分:4)

标准SQL为此任务提供MERGE语句。并非所有DBMS都支持MERGE语句。

答案 7 :(得分:0)

如果您没有通过原子方式更新或插入(例如,通过事务)的常用方法,那么您可以回退到另一个锁定方案。一个0字节的文件,系统互斥,命名管道等......

答案 8 :(得分:0)

你能使用插入触发器吗?如果失败,请进行更新。

答案 9 :(得分:0)

如果您可以使用为您编写SQL的库,那么您可以使用Upsert(目前仅限Ruby和Python):

Pet.upsert({:name => 'Jerry'}, :breed => 'beagle')
Pet.upsert({:name => 'Jerry'}, :color => 'brown')

适用于MySQL,Postgres和SQLite3。

它在MySQL和Postgres中编写存储过程或用户定义函数(UDF)。它在SQLite3中使用INSERT OR REPLACE