Frage

Ich habe eine gespeicherte Prozedur geschrieben, die eine Aktualisierung durchführt, wenn ein Datensatz vorhanden ist, andernfalls eine Einfügung.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 dahinter, es auf diese Weise zu schreiben, ist, dass das Update eine implizite Auswahl mithilfe der Where-Klausel durchführt und wenn diese 0 zurückgibt, findet die Einfügung statt.

Die Alternative zu dieser Vorgehensweise wäre, eine Auswahl durchzuführen und dann basierend auf der Anzahl der zurückgegebenen Zeilen entweder eine Aktualisierung oder eine Einfügung durchzuführen.Dies hielt ich für ineffizient, da bei einer Aktualisierung zwei Auswahlvorgänge erforderlich sind (der erste explizite Auswahlaufruf und der zweite implizite im Verzeichnis des Aktualisierungsvorgangs).Wenn der Prozess eine Einfügung durchführen würde, gäbe es keinen Unterschied in der Effizienz.

Stimmt meine Logik hier?Würden Sie auf diese Weise eine Einfügung und Aktualisierung in einer gespeicherten Prozedur kombinieren?

War es hilfreich?

Lösung

Ihre Annahme ist richtig, dies ist der optimale Weg und heißt Upsert/Merge.

Bedeutung von UPSERT – von sqlservercentral.com:

Für jedes Update in dem oben genannten Fall entfernen wir eine zusätzliche Lektüre aus der Tabelle, wenn wir das Upsert anstelle von existieren.Leider verwenden sowohl für einen Beilagen die Upsert als auch wenn Methoden die gleiche Anzahl von Lesevorgängen auf der Tabelle verwenden.Daher sollte der Überprüfung der Existenz nur dann durchgeführt werden, wenn es einen sehr gültigen Grund gibt, den zusätzlichen E/A zu rechtfertigen.Die optimierte Möglichkeit, Dinge zu tun, besteht darin, sicherzustellen, dass Sie auf der DB wenig wie möglich gelesen haben.

Die beste Strategie ist, das Update zu versuchen.Wenn vom Update keine Zeilen betroffen sind, geben Sie ein.In den meisten Fällen wird die Reihe bereits existieren und nur ein E/A ist erforderlich.

Bearbeiten:Bitte checken sie aus diese Antwort und den verlinkten Blog-Beitrag, um mehr über die Probleme mit diesem Muster zu erfahren und wie man dafür sorgt, dass es sicher funktioniert.

Andere Tipps

Bitte lesen Sie die Beitrag auf 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.

Für eine schnelle Antwort versuchen Sie es mit dem folgenden Muster.Es funktioniert einwandfrei 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.

begin tran
   update t with (serializable)
   set hitCount = hitCount + 1
   where pk = @id
   if @@rowcount = 0
   begin
      insert t (pk, hitCount)
      values (@id,1)
   end
commit tran

Bei Verwendung mit SQL Server 2000/2005 muss der Originalcode in die Transaktion eingeschlossen werden, um sicherzustellen, dass die Daten im gleichzeitigen Szenario konsistent bleiben.

BEGIN TRANSACTION Upsert
update myTable set Col1=@col1, Col2=@col2 where ID=@ID
if @@rowcount = 0
insert into myTable (Col1, Col2) values (@col1, @col2)
COMMIT TRANSACTION Upsert

Dies führt zu zusätzlichen Leistungseinbußen, stellt jedoch die Datenintegrität sicher.

Fügen Sie hinzu, wie bereits vorgeschlagen, MERGE sollte verwendet werden, sofern verfügbar.

MERGE ist übrigens eine der neuen Funktionen in SQL Server 2008.

Sie müssen es nicht nur in einer Transaktion ausführen, es benötigt auch eine hohe Isolationsstufe.Tatsächlich ist die Standardisolationsstufe Read Committed und dieser Code muss serialisierbar sein.

SET transaction isolation level SERIALIZABLE
BEGIN TRANSACTION Upsert
UPDATE myTable set Col1=@col1, Col2=@col2 where ID=@ID
if @@rowcount = 0
  begin
    INSERT into myTable (ID, Col1, Col2) values (@ID @col1, @col2)
  end
COMMIT TRANSACTION Upsert

Vielleicht wäre es eine gute Idee, auch die @@Fehlerprüfung und das Rollback hinzuzufügen.

Wenn Sie in SQL 2008 keine Zusammenführung durchführen, müssen Sie Folgendes ändern:

wenn @@rowcount = 0 und @@error=0

Andernfalls wird, wenn die Aktualisierung aus irgendeinem Grund fehlschlägt, anschließend versucht, eine Einfügung durchzuführen, da die Zeilenanzahl einer fehlgeschlagenen Anweisung 0 ist

Großer Fan von UPSERT, der den zu verwaltenden Code wirklich einschränkt.Hier ist eine andere Möglichkeit, wie ich es mache:Einer der Eingabeparameter ist die ID. Wenn die ID NULL oder 0 ist, wissen Sie, dass es sich um einen INSERT handelt, andernfalls handelt es sich um eine Aktualisierung.Geht davon aus, dass die Anwendung weiß, ob eine ID vorhanden ist, funktioniert also nicht in allen Situationen, halbiert aber in diesem Fall die Anzahl der Ausführungen.

Ihre Logik scheint vernünftig 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 eine Einfügung durchführen, wenn sich die Aktualisierung auf keine Datensätze ausgewirkt hat, was passiert dann, wenn jemand den Datensatz löscht, bevor Sie „UPSERT“ ausführen?Da der Datensatz, den Sie aktualisieren wollten, nun nicht mehr vorhanden ist, wird stattdessen ein Datensatz erstellt.Das ist wahrscheinlich nicht das Verhalten, nach dem Sie gesucht haben.

Geänderter Beitrag von Dima Malenko:

SET TRANSACTION ISOLATION LEVEL SERIALIZABLE 

BEGIN TRANSACTION UPSERT 

UPDATE MYTABLE 
SET    COL1 = @col1, 
       COL2 = @col2 
WHERE  ID = @ID 

IF @@rowcount = 0 
  BEGIN 
      INSERT INTO MYTABLE 
                  (ID, 
                   COL1, 
                   COL2) 
      VALUES      (@ID, 
                   @col1, 
                   @col2) 
  END 

IF @@Error > 0 
  BEGIN 
      INSERT INTO MYERRORTABLE 
                  (ID, 
                   COL1, 
                   COL2) 
      VALUES      (@ID, 
                   @col1, 
                   @col2) 
  END 

COMMIT TRANSACTION UPSERT 

Sie können den Fehler abfangen und den Datensatz an eine Tabelle mit fehlgeschlagenen Einfügungen senden.
Ich musste dies tun, weil wir alle Daten, die über WSDL gesendet werden, nehmen und sie, wenn möglich, intern reparieren.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top