Ich denke, ich wollte diesen Kommentar wahrscheinlich zur vorherigen Antwort hinzufügen, zu zwei getrennten Aussagen. Es war vor über einem Jahr, also bin ich mir nicht mehr ganz sicher.
Die wCTE-basierte Abfrage löst das Problem nicht wirklich, aber wenn ich sie über ein Jahr später erneut überprüfe, sehe ich keine Möglichkeit, dass Updates in der wCTE-Version verloren gehen.
(Beachten Sie, dass alle diese Lösungen nur dann gut funktionieren, wenn Sie versuchen, mit jeder Transaktion genau eine Zeile zu ändern. Sobald Sie versuchen, mehrere Änderungen in einer Transaktion vorzunehmen, werden die Dinge unordentlich, da bei Rollbacks Wiederholungsschleifen erforderlich sind. Zumindest Sie müssten zwischen jeder Änderung einen Sicherungspunkt verwenden.)
Version mit zwei Anweisungen, vorbehaltlich verlorener Aktualisierungen.
Die Version, die zwei separate Anweisungen verwendet, unterliegt verlorenen Aktualisierungen, es sei denn, die Anwendung überprüft die Anzahl der betroffenen Zeilen aus der UPDATE
Anweisung und der INSERT
Anweisung und versucht es erneut, wenn beide Null sind.
Stellen Sie sich vor, was passiert, wenn Sie zwei Transaktionen READ COMMITTED
isoliert haben.
- TX1 führt die aus
UPDATE
(kein Effekt)
- TX1 führt die aus
INSERT
(fügt eine Zeile ein)
- TX2 führt das aus
UPDATE
(kein Effekt, von TX1 eingefügte Zeile ist noch nicht sichtbar)
- TX1
COMMIT
s.
- TX2 führt das
INSERT
, * aus, das einen neuen Snapshot erhält, der die von TX1 festgeschriebene Zeile sehen kann. Die EXISTS
Klausel gibt true zurück, da TX2 jetzt die von TX1 eingefügte Zeile sehen kann.
TX2 hat also keine Wirkung. Wenn die App nicht die Zeilenanzahl aus dem Update und dem Einfügen überprüft und erneut versucht, wenn beide keine Zeilen melden, weiß sie nicht, dass die Transaktion keine Auswirkungen hatte, und wird fröhlich fortgesetzt.
Die einzige Möglichkeit, die betroffenen Zeilenzahlen zu überprüfen, besteht darin, sie als zwei separate Anweisungen anstatt als Mehrfachanweisung auszuführen oder eine Prozedur zu verwenden.
Sie können die SERIALIZABLE
Isolation verwenden, benötigen jedoch noch eine Wiederholungsschleife, um Serialisierungsfehler zu beheben.
Die wCTE-Version schützt vor dem Problem mit verlorenen Updates, da dies davon INSERT
abhängt, ob die UPDATE
Zeilen betroffen sind, und nicht von einer separaten Abfrage.
Der wCTE beseitigt keine eindeutigen Verstöße
Die beschreibbare CTE-Version ist immer noch kein zuverlässiger Upsert.
Betrachten Sie zwei Transaktionen, die dies gleichzeitig ausführen.
Beide führen die VALUES-Klausel aus.
Jetzt führen beide den UPDATE
Teil aus. Da es keine Zeilen gibt, die mit der UPDATE
s where-Klausel übereinstimmen , geben beide eine leere Ergebnismenge aus dem Update zurück und nehmen keine Änderungen vor.
Jetzt führen beide die INSERT
Portion aus. Da UPDATE
für beide Abfragen keine Zeilen zurückgegeben wurden, versuchen beide, INSERT
die Zeile abzurufen.
Man schafft es. Man wirft eine einzigartige Verletzung und bricht ab.
Dies ist kein Grund zur Besorgnis über Datenverlust, solange die App auf Fehlerergebnisse aus ihren Abfragen (dh jeder anständig geschriebenen App) prüft und es erneut versucht. Die Lösung ist jedoch nicht besser als die vorhandenen Versionen mit zwei Anweisungen. Eine Wiederholungsschleife ist nicht erforderlich.
Der Vorteil, den der wCTE gegenüber der vorhandenen Version mit zwei Anweisungen bietet, besteht darin, dass er anhand der Ausgabe von UPDATE
entscheidet, ob dies der Fall ist INSERT
, anstatt eine separate Abfrage für die Tabelle zu verwenden. Dies ist teilweise eine Optimierung, schützt jedoch teilweise vor einem Problem mit der Version mit zwei Anweisungen, das zu verlorenen Updates führt. siehe unten.
Sie können den wCTE SERIALIZABLE
isoliert ausführen , aber dann erhalten Sie nur Serialisierungsfehler anstelle eindeutiger Verstöße. Die Notwendigkeit einer Wiederholungsschleife wird dadurch nicht geändert.
Der wCTE scheint nicht anfällig für verlorene Updates zu sein
Mein Kommentar deutete darauf hin, dass diese Lösung zu verlorenen Updates führen könnte, aber nach Überprüfung, dass ich mich möglicherweise geirrt habe.
Es ist über ein Jahr her und ich kann mich nicht an die genauen Umstände erinnern, aber ich glaube, ich habe wahrscheinlich die Tatsache übersehen, dass eindeutige Indizes eine teilweise Ausnahme von den Regeln für die Sichtbarkeit von Transaktionen aufweisen, damit eine einfügende Transaktion auf das Einfügen oder Rollen einer anderen warten kann zurück, bevor Sie fortfahren.
Oder vielleicht habe ich die Tatsache übersehen, dass das INSERT
im wCTE davon abhängt, ob die UPDATE
betroffenen Zeilen betroffen sind, nicht davon, ob die Kandidatenzeile in der Tabelle vorhanden ist.
Konflikte INSERT
mit einem eindeutigen Index warten auf Commit / Rollback
Angenommen, eine Kopie der Abfrage wird ausgeführt und eine Zeile eingefügt. Die Änderung ist noch nicht festgeschrieben. Das neue Tupel ist im Heap und im eindeutigen Index vorhanden, für andere Transaktionen jedoch unabhängig von der Isolationsstufe noch nicht sichtbar.
Nun wird eine weitere Kopie der Abfrage ausgeführt. Die eingefügte Zeile ist noch nicht sichtbar, da die erste Kopie nicht festgeschrieben wurde und das Update daher mit nichts übereinstimmt. Bei der Abfrage wird eine Einfügung versucht, bei der festgestellt wird, dass eine andere laufende Transaktion denselben Schlüssel einfügt, und das Warten auf das Festschreiben oder Zurücksetzen dieser Transaktion blockiert wird .
Wenn die erste Transaktion festgeschrieben wird, schlägt die zweite Transaktion mit einer eindeutigen Verletzung fehl, wie oben beschrieben. Wenn die erste Transaktion zurückgesetzt wird, wird die zweite stattdessen eingefügt.
Die INSERT
Abhängigkeit von der UPDATE
Zeilenanzahl schützt vor verlorenen Updates
Anders als im Fall mit zwei Anweisungen glaube ich nicht, dass der wCTE anfällig für verlorene Updates ist.
Wenn das UPDATE
keine Auswirkung hat, INSERT
wird das immer ausgeführt, da es streng davon abhängig ist, ob das UPDATE
etwas getan hat, nicht vom externen Tabellenstatus. Es kann also immer noch mit einer eindeutigen Verletzung fehlschlagen, aber es kann nicht stillschweigend fehlschlagen und das Update vollständig verlieren.