我有下表的柜台:
CREATE TABLE cache ( key text PRIMARY KEY, generation int );
我想增加其中一个计数器,或者如果相应的行还不存在则将其设置为零.有没有办法在标准SQL中没有并发问题的情况下执行此操作?该操作有时是交易的一部分,有时是分开的.
如果可能的话,SQL必须在SQLite,PostgreSQL和MySQL上不加修改地运行.
搜索产生了一些想法,这些想法要么遇到并发问题,要么特定于数据库:
尝试换INSERT
行,UPDATE
如果出现错误.不幸的是,INSERT
中止当前事务的错误.
UPDATE
行,如果没有修改行,则为INSERT
新行.
MySQL有一个ON DUPLICATE KEY UPDATE
条款.
编辑:感谢所有的好评.看起来保罗是对的,并没有单一,便携的方式来做到这一点.这对我来说非常令人惊讶,因为它听起来像是一个非常基本的操作.
MySQL(以及随后的SQLite)也支持REPLACE INTO语法:
REPLACE INTO my_table (pk_id, col1) VALUES (5, '123');
这会自动识别主键并找到要更新的匹配行,如果没有找到则插入新行.
SQLite支持替换已存在的行:
INSERT OR REPLACE INTO [...blah...]
你可以缩短这个
REPLACE INTO [...blah...]
添加此快捷方式以与MySQL REPLACE INTO
表达式兼容.
我会做类似以下的事情:
INSERT INTO cache VALUES (key, generation) ON DUPLICATE KEY UPDATE (key = key, generation = generation + 1);
在代码或sql中将生成值设置为0,但使用ON DUP ...来增加值.无论如何,我认为这就是语法.
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) ;
我不知道你会找到一个平台中立的解决方案.
这通常称为"UPSERT".
看一些相关的讨论:
SQL Server上INSERT或UPDATE的解决方案
SQL REP 2005实现MySQL REPLACE INTO?
在PostgreSQL中没有合并命令,实际上编写它并不是一件容易的事 - 实际上有一些奇怪的边缘情况使得任务"有趣".
最好的(如:在最可能的条件下工作)方法是使用函数 - 例如手动(merge_db)中显示的函数.
如果您不想使用功能,通常可以逃脱:
updated = db.execute(UPDATE ... RETURNING 1) if (!updated) db.execute(INSERT...)
请记住,它不是故障证明,最终会失败.