次のカウンターの表があります。
CREATE TABLE cache (
key text PRIMARY KEY,
generation int
);
カウンターの1つをインクリメントするか、対応する行がまだ存在しない場合はゼロに設定します。標準SQLで並行性の問題なしにこれを行う方法はありますか?操作はトランザクションの一部である場合があり、場合によっては別個です。
可能であれば、SQLite、PostgreSQL、MySQLでSQLを変更せずに実行する必要があります。
検索により、並行性の問題があるか、データベースに固有のいくつかのアイデアが得られました。
新しい行を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 ... ON DUPLICATE UPDATE ...を使用します。
jmozのソリューションが最適です。ただし、括弧よりもSET構文の方が好きですが
INSERT INTO cache
SET key = 'key', generation = 'generation'
ON DUPLICATE KEY
UPDATE key = 'key', generation = (generation + 1)
;
プラットフォームに中立なソリューションを見つけることになるのかわかりません。
これは一般に「UPSERT」と呼ばれます。
関連する議論をご覧ください。
PostgreSQLにはマージコマンドはなく、実際にそれを記述することは簡単ではありません。実際、タスクを「興味深い」ものにするEdgeの奇妙なケースがあります。
最善の(最も可能性の高い条件での作業など)アプローチは、 manual (merge_db)に示されているような関数を使用することです。
関数を使用したくない場合は、通常はを回避できます:
updated = db.execute(UPDATE ... RETURNING 1)
if (!updated)
db.execute(INSERT...)
それはフォールトプルーフではなく、最終的に失敗することを忘れないでください。
標準SQLは、このタスクにMERGEステートメントを提供します。すべてのDBMSがMERGEステートメントをサポートしているわけではありません。
SQLを作成するライブラリを使用しても問題ない場合は、 psert (現在Ruby and Python =のみ):
Pet.upsert({:name => 'Jerry'}, :breed => 'beagle')
Pet.upsert({:name => 'Jerry'}, :color => 'brown')
これは、MySQL、Postgres、およびSQLite3で機能します。
ストアドプロシージャまたはユーザー定義関数(UDF)をMySQLおよびPostgresに書き込みます。それは使用しています INSERT OR REPLACE
SQLite3で。
アトミックに更新または挿入する一般的な方法がない場合(たとえば、トランザクションを介して)、別のロックスキームにフォールバックできます。 0バイトのファイル、システムミューテックス、名前付きパイプなど...
挿入トリガーを使用できますか?失敗した場合は、更新を行います。