我有以下计数器表:
CREATE TABLE cache (
key text PRIMARY KEY,
generation int
);
我想增加其中一个计数器,或者如果相应的行还不存在则将其设置为零。有没有办法在标准SQL中没有并发问题的情况下执行此操作?该操作有时是交易的一部分,有时是分开的。
如果可能,SQL必须在SQLite,PostgreSQL和MySQL上不加修改地运行。
搜索产生了一些想法,这些想法要么遇到并发问题,要么特定于数据库:
尝试INSERT
新行,UPDATE
如果出现错误。不幸的是,INSERT
上的错误会中止当前的交易。
UPDATE
行,如果没有修改行,INSERT
新行。
MySQL有一个ON DUPLICATE KEY UPDATE
子句。
答案 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
。