I have the following table of counters:
CREATE TABLE cache (
key text PRIMARY KEY,
generation int
);
I would like to increment one of the counters, or set it to zero if the corresponding row doesn't exist yet. Is there a way to do this without concurrency issues in standard SQL? The operation is sometimes part of a transaction, sometimes separate.
The SQL must run unmodified on SQLite, PostgreSQL and MySQL, if possible.
A search yielded several ideas which either suffer from concurrency issues, or are specific to a database:
Try to INSERT
a new row, and UPDATE
if there was an error. Unfortunately, the error on INSERT
aborts the current transaction.
UPDATE
the row, and if no rows were modified, INSERT
a new row.
MySQL has an ON DUPLICATE KEY UPDATE
clause.
EDIT: Thanks for all the great replies. It looks like Paul is right, and there's not a single, portable way of doing this. That's quite surprising to me, as it sounds like a very basic operation.
MySQL (and subsequently SQLite) also support the REPLACE INTO syntax:
REPLACE INTO my_table (pk_id, col1) VALUES (5, '123');
This automatically identifies the primary key and finds a matching row to update, inserting a new one if none is found. But as pointed out in the comments below, you need to make sure you supply values for all columns because even if the row already exists it will replace it with the values you supply (don't assume the new row will have the same primary key value as the old row either).
Documentation: https://dev.mysql.com/doc/refman/8.0/en/replace.html