738 Stimmen

Lösungen für INSERT OR UPDATE auf SQL Server

Nehmen wir eine Tabellenstruktur von MyTable(KEY, datafield1, datafield2...) .

Häufig möchte ich entweder einen vorhandenen Datensatz aktualisieren oder einen neuen Datensatz einfügen, wenn dieser noch nicht existiert.

Im Wesentlichen:

IF (key exists)
  run update command
ELSE
  run insert command

Wie kann man das am besten schreiben?

53 Stimmen

Für alle, die diese Frage zum ersten Mal stellen - bitte lesen Sie unbedingt alle Antworten und Kommentare. Das Alter kann manchmal zu irreführenden Informationen führen...

1 Stimmen

Erwägen Sie die Verwendung des EXCEPT-Operators, der in SQL Server 2005 eingeführt wurde.

5voto

Kristen Punkte 4123

Wenn Sie den Weg über UPDATE if-no-rows-updated then INSERT gehen, sollten Sie erwägen, zuerst INSERT zu machen, um eine Race Condition zu verhindern (vorausgesetzt, es liegt kein DELETE dazwischen)

INSERT INTO MyTable (Key, FieldA)
   SELECT @Key, @FieldA
   WHERE NOT EXISTS
   (
       SELECT *
       FROM  MyTable
       WHERE Key = @Key
   )
IF @@ROWCOUNT = 0
BEGIN
   UPDATE MyTable
   SET FieldA=@FieldA
   WHERE Key=@Key
   IF @@ROWCOUNT = 0
   ... record was deleted, consider looping to re-run the INSERT, or RAISERROR ...
END

Abgesehen von der Vermeidung einer Wettlaufbedingung, wenn der Datensatz in den meisten Fällen bereits existiert, führt dies dazu, dass das INSERT fehlschlägt und CPU verschwendet wird.

Die Verwendung von MERGE ist wahrscheinlich für SQL2008 und höher vorzuziehen.

0 Stimmen

Interessante Idee, aber falsche Syntax. Das SELECT braucht ein FROM <Tabellenquelle> und ein TOP 1 (es sei denn, die gewählte Tabellenquelle hat nur eine Zeile).

0 Stimmen

Danke. Ich habe es in ein NOT EXISTS geändert. Es wird immer nur eine übereinstimmende Zeile geben, weil der Test für "Schlüssel" gemäß O/P (obwohl das ein mehrteiliger Schlüssel sein muss :) )

2voto

bjorsig Punkte 1057

MS SQL Server 2008 führt die MERGE-Anweisung ein, die meiner Meinung nach Teil des SQL:2003-Standards ist. Wie viele gezeigt haben, ist es keine große Sache, Fälle mit nur einer Zeile zu behandeln, aber wenn man mit großen Datenmengen zu tun hat, braucht man einen Cursor, mit all den damit verbundenen Leistungsproblemen. Die MERGE-Anweisung ist eine willkommene Ergänzung, wenn man mit großen Datenmengen arbeitet.

1 Stimmen

Ich habe noch nie einen Cursor benötigt, um dies mit großen Datensätzen zu tun. Sie brauchen nur eine Aktualisierung, die die übereinstimmenden Datensätze aktualisiert, und eine Einfügung mit einer select- statt einer values-Klausel, die eine Verbindung zur Tabelle herstellt.

2voto

ZXX Punkte 4586

Bevor jetzt jeder aus Angst vor diesen bösartigen Benutzern, die Ihre Sprocs direkt ausführen, zu HOLDLOCK-s übergeht, möchte ich darauf hinweisen, dass Sie müssen die Einzigartigkeit der neuen PK-s durch Design garantieren (Identitätsschlüssel, Sequenzgeneratoren in Oracle, eindeutige Indizes für externe IDs, durch Indizes abgedeckte Abfragen). Das ist das A und O des Problems. Wenn Sie das nicht haben, werden Sie von keinem HOLDLOCK des Universums gerettet, und wenn Sie das haben, brauchen Sie nichts anderes als UPDLOCK beim ersten Select (oder Update first) zu verwenden.

Sprocs laufen normalerweise unter sehr kontrollierten Bedingungen und unter der Annahme eines vertrauenswürdigen Anrufers (mittlere Ebene). Das heißt, wenn ein einfaches Upsert-Muster (Update+Insert oder Merge) jemals doppelte PK sieht, bedeutet das einen Fehler in Ihrem Mid-Tier- oder Tabellendesign und es ist gut, dass SQL in einem solchen Fall einen Fehler meldet und den Datensatz zurückweist. Das Setzen eines HOLDLOCKs in diesem Fall ist gleichbedeutend mit dem Verzehr von Ausnahmen und der Aufnahme potenziell fehlerhafter Daten, abgesehen von der Verringerung Ihrer Leistung.

Allerdings ist die Verwendung von MERGE oder UPDATE und dann INSERT einfacher für Ihren Server und weniger fehleranfällig, da Sie nicht daran denken müssen, (UPDLOCK) zum ersten Select hinzuzufügen. Wenn Sie außerdem Einfügungen/Aktualisierungen in kleinen Stapeln vornehmen, müssen Sie Ihre Daten kennen, um entscheiden zu können, ob eine Transaktion sinnvoll ist oder nicht. Wenn es sich nur um eine Sammlung von nicht zusammenhängenden Datensätzen handelt, ist eine zusätzliche "umhüllende" Transaktion nachteilig.

1 Stimmen

Wenn Sie nur eine Aktualisierung und dann eine Einfügung ohne Sperren oder erhöhte Isolierung vornehmen, könnten zwei Benutzer versuchen, dieselben Daten zurück zu übermitteln (ich würde es nicht als Fehler in der mittleren Ebene betrachten, wenn zwei Benutzer versuchen würden, genau dieselben Informationen zur gleichen Zeit zu übermitteln - es hängt sehr vom Kontext ab, nicht wahr?). Sie geben beide die Aktualisierung ein, die für beide 0 Zeilen zurückgibt, dann versuchen sie beide, die Daten einzufügen. Einer gewinnt, der andere bekommt eine Ausnahme. Das ist es, was die Leute normalerweise zu vermeiden versuchen.

2voto

runec Punkte 1587

Sind die Race Conditions wirklich von Bedeutung, wenn Sie zuerst eine Aktualisierung und dann eine Einfügung versuchen? Angenommen, Sie haben zwei Threads, die einen Wert für einen Schlüssel setzen wollen Schlüssel :

Thema 1: Wert = 1
Thema 2: Wert = 2

Beispiel für ein Race-Condition-Szenario

  1. Schlüssel ist nicht definiert
  2. Thread 1 schlägt beim Update fehl
  3. Thread 2 schlägt beim Update fehl
  4. Genau einer von Faden 1 oder Faden 2 hat Erfolg beim Einfügen. Z.B. Thread 1
  5. Der andere Thread scheitert beim Einfügen (mit dem Fehler Duplikatschlüssel) - Thread 2.

    • Ergebnis: Die "erste" der beiden einzufügenden Stufen, entscheidet über den Wert.
    • Gesuchtes Ergebnis: Der letzte der 2 Threads, der Daten schreibt (Update oder Insert), soll den Wert bestimmen

In einer Multithreading-Umgebung entscheidet jedoch der Scheduler des Betriebssystems über die Reihenfolge der Thread-Ausführung - in dem obigen Szenario, in dem wir diese Wettlaufbedingung haben, war es das Betriebssystem, das über die Reihenfolge der Ausführung entschieden hat. D.h.: Es ist falsch zu sagen, dass "Thread 1" oder "Thread 2" aus Sicht des Systems "zuerst" ausgeführt wurde.

Wenn der Ausführungszeitpunkt für Thread 1 und Thread 2 so nah beieinander liegt, spielt das Ergebnis der Race Condition keine Rolle. Die einzige Anforderung sollte sein, dass einer der Threads den resultierenden Wert definiert.

Für die Umsetzung: Wenn eine Aktualisierung gefolgt von einer Einfügung den Fehler "doppelter Schlüssel" ergibt, sollte dies als Erfolg behandelt werden.

Außerdem sollte man natürlich nie davon ausgehen, dass der Wert in der Datenbank derselbe ist wie der, den man zuletzt geschrieben hat.

1voto

Victor Sanchez Punkte 501

Sie können diese Abfrage verwenden. Sie funktioniert in allen SQL Server-Editionen. Sie ist einfach und übersichtlich. Aber Sie müssen 2 Abfragen verwenden. Sie können verwenden, wenn Sie MERGE nicht verwenden können

    BEGIN TRAN

    UPDATE table
    SET Id = @ID, Description = @Description
    WHERE Id = @Id

    INSERT INTO table(Id, Description)
    SELECT @Id, @Description
    WHERE NOT EXISTS (SELECT NULL FROM table WHERE Id = @Id)

    COMMIT TRAN

HINWEIS: Bitte erläutern Sie negative Antworten

0 Stimmen

Ich vermute, dass die Verriegelung fehlt?

0 Stimmen

Keine fehlende Verriegelung... Ich verwende "TRAN". Standard-SQL-Server-Transaktionen haben Sperren.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X