Ich habe die folgende Tabelle von Zählern:
CREATE TABLE cache (
key text PRIMARY KEY,
generation int
);
Ich möchte einen der Zähler erhöhen oder auf Null setzen, wenn die entsprechende Zeile noch nicht vorhanden ist. Gibt es eine Möglichkeit, dies ohne Parallelitätsprobleme in Standard-SQL zu tun? Die Operation ist manchmal Teil einer Transaktion, manchmal getrennt.
Wenn möglich, muss SQL unter SQLite, PostgreSQL und MySQL unverändert ausgeführt werden.
Eine Suche ergab mehrere Ideen, die entweder unter Parallelitätsproblemen leiden oder für eine Datenbank spezifisch sind:
Versuchen Sie es mit
INSERT
einer neuen Zeile, undUPDATE
wenn ein Fehler aufgetreten ist. LeiderINSERT
bricht der Fehler bei die aktuelle Transaktion ab.UPDATE
die Zeile, und wenn keine Zeilen geändert wurden,INSERT
eine neue Zeile.MySQL hat eine
ON DUPLICATE KEY UPDATE
Klausel.
EDIT: Danke für all die tollen Antworten. Es sieht so aus, als ob Paul Recht hat, und es gibt keinen einzigen tragbaren Weg, dies zu tun. Das ist für mich ziemlich überraschend, da es sich nach einer sehr einfachen Operation anhört.
Antworten:
MySQL (und anschließend SQLite) unterstützt auch die REPLACE INTO-Syntax:
Dadurch wird der Primärschlüssel automatisch identifiziert und eine passende Zeile zum Aktualisieren gefunden. Wenn keine gefunden wird, wird eine neue eingefügt.
quelle
SQLite unterstützt das Ersetzen einer Zeile, falls diese bereits vorhanden ist:
Sie können dies auf kürzen
Diese Verknüpfung wurde hinzugefügt, um mit dem MySQL-
REPLACE INTO
Ausdruck kompatibel zu sein .quelle
PRAMARY KEY
in Ihren Werten ein definiert haben.Ich würde so etwas machen:
Setzen Sie den Generierungswert im Code oder in SQL auf 0, verwenden Sie jedoch ON DUP ..., um den Wert zu erhöhen. Ich denke, das ist sowieso die Syntax.
quelle
Die ON DUPLICATE KEY UPDATE-Klausel ist die beste Lösung, weil: REPLACE ein DELETE gefolgt von einem INSERT ausführt, sodass der Datensatz für einen sehr kurzen Zeitraum entfernt wird, wodurch die geringste Wahrscheinlichkeit entsteht, dass eine Abfrage zurückkommt, wenn die Seite übersprungen wurde wird während der REPLACE-Abfrage angezeigt.
Aus diesem Grund bevorzuge ich INSERT ... ON DUPLICATE UPDATE.
Die Lösung von jmoz ist die beste: obwohl ich die SET-Syntax den Klammern vorziehe
quelle
Ich weiß nicht, dass Sie eine plattformneutrale Lösung finden werden.
Dies wird üblicherweise als "UPSERT" bezeichnet.
Siehe einige verwandte Diskussionen:
quelle
In PostgreSQL gibt es keinen Zusammenführungsbefehl, und das Schreiben ist nicht trivial - es gibt tatsächlich seltsame Randfälle, die die Aufgabe "interessant" machen.
Der beste Ansatz (wie in: Arbeiten unter den bestmöglichen Bedingungen) ist die Verwendung einer Funktion - wie sie im Handbuch (merge_db) gezeigt wird.
Wenn Sie die Funktion nicht verwenden möchten, können Sie normalerweise Folgendes tun :
Denken Sie daran, dass es nicht fehlerfrei ist und es auch wird fehlschlagen wird.
quelle
Standard SQL stellt die MERGE-Anweisung für diese Aufgabe bereit. Nicht alle DBMS unterstützen die MERGE-Anweisung.
quelle
Wenn Sie keine übliche Methode zum atomaren Aktualisieren oder Einfügen haben (z. B. über eine Transaktion), können Sie auf ein anderes Sperrschema zurückgreifen. Eine 0-Byte-Datei, ein Systemmutex, eine Named Pipe usw.
quelle
Könnten Sie einen Insert-Trigger verwenden? Wenn dies fehlschlägt, führen Sie ein Update durch.
quelle
Wenn Sie eine Bibliothek verwenden können, die SQL für Sie schreibt, können Sie Upsert verwenden (derzeit nur Ruby und Python):
Das funktioniert in MySQL, Postgres und SQLite3.
Es schreibt eine gespeicherte Prozedur oder eine benutzerdefinierte Funktion (UDF) in MySQL und Postgres. Es wird
INSERT OR REPLACE
in SQLite3 verwendet.quelle