Ich habe einen gespeicherten Prozess geschrieben, der ein Update ausführt, wenn ein Datensatz vorhanden ist, andernfalls wird ein Insert ausgeführt. Es sieht ungefähr so aus:
update myTable set Col1=@col1, Col2=@col2 where ID=@ID
if @@rowcount = 0
insert into myTable (Col1, Col2) values (@col1, @col2)
Meine Logik hinter dem Schreiben auf diese Weise ist, dass das Update eine implizite Auswahl unter Verwendung der where-Klausel ausführt. Wenn diese 0 zurückgibt, erfolgt die Einfügung.
Die Alternative dazu wäre, eine Auswahl zu treffen und dann basierend auf der Anzahl der zurückgegebenen Zeilen entweder eine Aktualisierung oder eine Einfügung durchzuführen. Dies habe ich als ineffizient angesehen, da bei einem Update zwei Auswahlen durchgeführt werden (der erste explizite Auswahlaufruf und der zweite implizit im Wo des Updates). Wenn der Proc eine Einfügung machen würde, gäbe es keinen Unterschied in der Effizienz.
Ist meine Logik hier? Würden Sie auf diese Weise eine Einfügung und ein Update in einem gespeicherten Prozess kombinieren?
Bitte lesen Sie den Beitrag in meinem Blog für ein gutes, sicheres Muster, das Sie verwenden können. Es gibt viele Überlegungen, und die akzeptierte Antwort auf diese Frage ist alles andere als sicher.
Versuchen Sie für eine schnelle Antwort das folgende Muster. Es funktioniert gut unter SQL 2000 und höher. SQL 2005 bietet Ihnen eine Fehlerbehandlung, die andere Optionen eröffnet, und SQL 2008 bietet Ihnen einen MERGE-Befehl.
quelle
Wenn mit SQL Server 2000/2005 verwendet werden soll, muss der ursprüngliche Code in die Transaktion eingeschlossen werden, um sicherzustellen, dass die Daten im gleichzeitigen Szenario konsistent bleiben.
Dies verursacht zusätzliche Leistungskosten, stellt jedoch die Datenintegrität sicher.
Fügen Sie, wie bereits vorgeschlagen, MERGE hinzu, sofern verfügbar.
quelle
MERGE ist übrigens eine der neuen Funktionen in SQL Server 2008.
quelle
Sie müssen es nicht nur in einer Transaktion ausführen, sondern auch eine hohe Isolationsstufe. Ich glaube, die Standardisolationsstufe ist Read Commited und dieser Code muss serialisierbar sein.
Vielleicht ist es eine gute Idee, auch die @@ Fehlerprüfung und das Rollback hinzuzufügen.
quelle
Wenn Sie in SQL 2008 keine Zusammenführung durchführen, müssen Sie Folgendes ändern:
wenn @@ rowcount = 0 und @@ error = 0
Wenn andernfalls das Update aus irgendeinem Grund fehlschlägt, wird versucht, es anschließend einzufügen, da die Zeilenanzahl für eine fehlgeschlagene Anweisung 0 ist
quelle
Der große Fan von UPSERT reduziert den zu verwaltenden Code erheblich. Hier ist eine andere Möglichkeit, wie ich es mache: Einer der Eingabeparameter ist ID. Wenn die ID NULL oder 0 ist, wissen Sie, dass es sich um ein INSERT handelt, andernfalls handelt es sich um ein Update. Angenommen, die Anwendung weiß, ob eine ID vorhanden ist, funktioniert also nicht in allen Situationen, halbiert jedoch die Ausführung, wenn Sie dies tun.
quelle
Geänderter Beitrag von Dima Malenko:
Sie können den Fehler abfangen und den Datensatz an eine fehlgeschlagene Einfügetabelle senden.
Ich musste dies tun, weil wir alle Daten, die über WSDL gesendet werden, nehmen und wenn möglich intern reparieren.
quelle
Ihre Logik scheint solide zu sein, aber Sie sollten in Betracht ziehen, Code hinzuzufügen, um das Einfügen zu verhindern, wenn Sie einen bestimmten Primärschlüssel übergeben haben.
Wenn Sie andernfalls immer einfügen, wenn das Update keine Datensätze beeinflusst hat, was passiert dann, wenn jemand den Datensatz löscht, bevor "UPSERT" ausgeführt wird? Jetzt existiert der Datensatz, den Sie aktualisieren wollten, nicht mehr, sodass stattdessen ein Datensatz erstellt wird. Das ist wahrscheinlich nicht das Verhalten, nach dem Sie gesucht haben.
quelle