Nehmen Sie eine Tabellenstruktur von an MyTable(KEY, datafield1, datafield2...)
.
Oft möchte ich entweder einen vorhandenen Datensatz aktualisieren oder einen neuen Datensatz einfügen, wenn dieser nicht vorhanden ist.
Im Wesentlichen:
IF (key exists)
run update command
ELSE
run insert command
Was ist die beste Art, dies zu schreiben?
Antworten:
Transaktionen nicht vergessen. Die Leistung ist gut, aber ein einfacher Ansatz (WENN EXISTIERT ..) ist sehr gefährlich.
Wenn mehrere Threads versuchen, Einfügen oder Aktualisieren durchzuführen, kann dies leicht zu einer Verletzung des Primärschlüssels führen.
Die von @Beau Crawford & @Esteban bereitgestellten Lösungen zeigen eine allgemeine Idee, sind jedoch fehleranfällig.
Um Deadlocks und PK-Verstöße zu vermeiden, können Sie Folgendes verwenden:
oder
quelle
Siehe meine ausführliche Antwort auf eine sehr ähnliche vorherige Frage
@Beau Crawford's ist ein guter Weg in SQL 2005 und darunter, aber wenn Sie rep gewähren, sollte es an den ersten gehen, der es SO macht . Das einzige Problem ist, dass es für Einfügungen immer noch zwei E / A-Operationen sind.
MS Sql2008 führt
merge
aus dem SQL: 2003-Standard ein:Jetzt ist es wirklich nur eine E / A-Operation, aber schrecklicher Code :-(
quelle
upsert
, die fast alle anderen DB-Anbieter stattdessen unterstützen wollten. Dieupsert
Syntax ist ein weitaus besserer Weg, dies zu tun, also hätte MS sie zumindest auch unterstützen sollen - es ist nicht so, als wäre es das einzige nicht standardmäßige Schlüsselwort in T-SQLMERGE
Syntax auftreten können.HOLDLOCK
für Zusammenführungsvorgänge in Situationen mit hoher Parallelität einen benötigen .Mach ein UPSERT:
http://en.wikipedia.org/wiki/Upsert
quelle
Viele Leute werden vorschlagen, dass Sie verwenden
MERGE
, aber ich warne Sie davor. Standardmäßig schützt es Sie nicht mehr als mehrere Anweisungen vor Parallelität und Race-Bedingungen und bringt andere Gefahren mit sich:http://www.mssqltips.com/sqlservertip/3074/use-caution-with-sql-servers-merge-statement/
Selbst mit dieser "einfacheren" Syntax bevorzuge ich diesen Ansatz (Fehlerbehandlung der Kürze halber weggelassen):
Viele Leute werden diesen Weg vorschlagen:
Damit können Sie jedoch sicherstellen, dass Sie die Tabelle möglicherweise zweimal lesen müssen, um die zu aktualisierenden Zeilen zu finden. Im ersten Beispiel müssen Sie die Zeile (n) immer nur einmal suchen. (In beiden Fällen erfolgt eine Einfügung, wenn beim ersten Lesen keine Zeilen gefunden wurden.)
Andere schlagen diesen Weg vor:
Dies ist jedoch problematisch, wenn es aus keinem anderen Grund viel teurer ist, SQL Server Ausnahmen abfangen zu lassen, die Sie an erster Stelle hätten verhindern können, außer in dem seltenen Szenario, in dem fast jede Einfügung fehlschlägt. Das beweise ich hier:
quelle
UPDATE target SET col = tmp.col FROM target INNER JOIN #tmp ON <key clause>; INSERT target(...) SELECT ... FROM #tmp AS t WHERE NOT EXISTS (SELECT 1 FROM target WHERE key = t.key);
Bearbeiten:
Leider muss ich auch zu meinem eigenen Nachteil zugeben, dass die Lösungen, die dies ohne Auswahl tun, besser zu sein scheinen, da sie die Aufgabe mit einem Schritt weniger erfüllen.
quelle
Wenn Sie mehr als einen Datensatz gleichzeitig UPSERTIEREN möchten, können Sie die ANSI SQL: 2003 DML-Anweisung MERGE verwenden.
Lesen Sie die Mimicking MERGE-Anweisung in SQL Server 2005 .
quelle
Obwohl es ziemlich spät ist, dies zu kommentieren, möchte ich mit MERGE ein vollständigeres Beispiel hinzufügen.
Solche Insert + Update-Anweisungen werden normalerweise als "Upsert" -Anweisungen bezeichnet und können mit MERGE in SQL Server implementiert werden.
Ein sehr gutes Beispiel finden Sie hier: http://weblogs.sqlteam.com/dang/archive/2009/01/31/UPSERT-Race-Condition-With-MERGE.aspx
Im obigen Abschnitt werden auch Sperr- und Parallelitätsszenarien erläutert.
Ich werde dasselbe als Referenz zitieren:
quelle
Ersetzen Sie Tabellen- und Feldnamen durch die gewünschten Elemente. Achten Sie auf den EIN- Zustand. Stellen Sie dann den entsprechenden Wert (und Typ) für die Variablen in der Zeile DECLARE ein.
Prost.
quelle
Sie können
MERGE
Anweisung verwenden. Diese Anweisung wird verwendet, um Daten einzufügen, wenn sie nicht vorhanden sind, oder um sie zu aktualisieren, wenn sie vorhanden sind.quelle
Wenn Sie das UPDATE ausführen, wenn keine Zeilen aktualisiert wurden, und dann die INSERT-Route, sollten Sie zuerst das INSERT ausführen, um eine Racebedingung zu verhindern (vorausgesetzt, es wird kein dazwischen liegendes DELETE verwendet).
Abgesehen von der Vermeidung einer Race-Bedingung führt dies in den meisten Fällen dazu, dass der INSERT fehlschlägt und die CPU verschwendet wird.
Die Verwendung von MERGE ist wahrscheinlich ab SQL2008 vorzuziehen.
quelle
Das hängt vom Nutzungsmuster ab. Man muss das Gesamtbild der Nutzung betrachten, ohne sich in den Details zu verlieren. Wenn das Verwendungsmuster beispielsweise zu 99% aktualisiert wird, nachdem der Datensatz erstellt wurde, ist "UPSERT" die beste Lösung.
Nach dem ersten Einfügen (Treffer) werden alle einzelnen Anweisungen aktualisiert, ohne Wenn und Aber. Die 'where'-Bedingung für die Einfügung ist erforderlich, da sonst Duplikate eingefügt werden und Sie sich nicht mit dem Sperren befassen möchten.
quelle
MS SQL Server 2008 führt die MERGE-Anweisung ein, die meines Erachtens Teil des SQL: 2003-Standards ist. Wie viele gezeigt haben, ist es keine große Sache, Fälle mit einer Zeile zu behandeln, aber wenn man sich mit großen Datenmengen befasst, benötigt man einen Cursor mit allen damit verbundenen Leistungsproblemen. Die MERGE-Anweisung wird beim Umgang mit großen Datenmengen sehr begrüßt.
quelle
Bevor alle aus Angst vor diesen nafarious Benutzern, die Ihre Sprocs direkt ausführen, zu HOLDLOCK-s springen :-) Lassen Sie mich darauf hinweisen, dass Sie die Eindeutigkeit neuer PK-s durch Design garantieren müssen (Identitätsschlüssel, Sequenzgeneratoren in Oracle, eindeutige Indizes für externe IDs, Abfragen, die von Indizes abgedeckt werden). Das ist das A und O des Problems. Wenn Sie das nicht haben, werden Sie keine HOLDLOCK-s des Universums retten, und wenn Sie das haben, brauchen Sie bei der ersten Auswahl nichts anderes als UPDLOCK (oder um zuerst das Update zu verwenden).
Sprocs werden normalerweise unter sehr kontrollierten Bedingungen und unter der Annahme eines vertrauenswürdigen Anrufers (Mid Tier) ausgeführt. Das heißt, wenn ein einfaches Upsert-Muster (Aktualisieren + Einfügen oder Zusammenführen) jemals doppelte PK sieht, bedeutet dies einen Fehler in Ihrem Mid-Tier- oder Tabellen-Design und es ist gut, dass SQL in einem solchen Fall einen Fehler schreit und den Datensatz ablehnt. Das Platzieren eines HOLDLOCK ist in diesem Fall gleichbedeutend mit Essensausnahmen und der Aufnahme potenziell fehlerhafter Daten sowie der Reduzierung Ihrer Leistung.
Wenn Sie jedoch MERGE oder UPDATE verwenden, ist INSERT auf Ihrem Server einfacher und weniger fehleranfällig, da Sie nicht daran denken müssen, UPDLOCK hinzuzufügen, um es zuerst auszuwählen. Wenn Sie Einfügungen / Aktualisierungen in kleinen Stapeln durchführen, müssen Sie Ihre Daten kennen, um zu entscheiden, ob eine Transaktion angemessen ist oder nicht. Es ist nur eine Sammlung von nicht verwandten Aufzeichnungen, dann wird eine zusätzliche "Umhüllung" der Transaktion nachteilig sein.
quelle
Sind die Rennbedingungen wirklich wichtig, wenn Sie zuerst ein Update gefolgt von einer Einfügung versuchen? Lassen Sie uns sagen Sie zwei Threads haben , die einen Wert für Schlüssel festlegen möchten Schlüssel :
Thread 1: Wert = 1
Thread 2: Wert = 2
Beispiel für ein Szenario mit Rennbedingungen
Der andere Thread schlägt mit Einfügen fehl (mit Fehler-Duplikatschlüssel) - Thread 2.
Aber; In einer Multithread-Umgebung entscheidet der OS-Scheduler über die Reihenfolge der Thread-Ausführung. Im obigen Szenario, in dem diese Race-Bedingung vorliegt, hat das Betriebssystem über die Reihenfolge der Ausführung entschieden. Dh: Es ist falsch zu sagen, dass "Thread 1" oder "Thread 2" aus Systemsicht "zuerst" war.
Wenn die Ausführungszeit für Thread 1 und Thread 2 so kurz ist, spielt das Ergebnis der Race-Bedingung keine Rolle. Die einzige Anforderung sollte sein, dass einer der Threads den resultierenden Wert definiert.
Für die Implementierung: Wenn eine Aktualisierung gefolgt von Einfügen zu dem Fehler "Schlüssel duplizieren" führt, sollte dies als Erfolg behandelt werden.
Außerdem sollte man natürlich niemals davon ausgehen, dass der Wert in der Datenbank mit dem Wert übereinstimmt, den Sie zuletzt geschrieben haben.
quelle
In SQL Server 2008 können Sie die MERGE-Anweisung verwenden
quelle
Ich hatte unten die Lösung ausprobiert und es funktioniert für mich, wenn gleichzeitig eine Anforderung zum Einfügen einer Anweisung auftritt.
quelle
Sie können diese Abfrage verwenden. Arbeiten Sie in allen SQL Server-Editionen. Es ist einfach und klar. Sie müssen jedoch 2 Abfragen verwenden. Sie können verwenden, wenn Sie MERGE nicht verwenden können
HINWEIS: Bitte erläutern Sie die Antwortnegative
quelle
Wenn Sie ADO.NET verwenden, übernimmt der DataAdapter dies.
Wenn Sie es selbst erledigen möchten, ist dies der Weg:
Stellen Sie sicher, dass Ihre Schlüsselspalte eine Primärschlüsseleinschränkung enthält.
Dann Sie:
Sie können es auch umgekehrt machen, dh zuerst die Einfügung durchführen und die Aktualisierung durchführen, wenn die Einfügung fehlschlägt. Normalerweise ist der erste Weg besser, da Aktualisierungen häufiger durchgeführt werden als Einfügungen.
quelle
Wenn ein if vorhanden ist ... else ... müssen mindestens zwei Anforderungen ausgeführt werden (eine zum Überprüfen, eine zum Ergreifen von Maßnahmen). Der folgende Ansatz erfordert nur einen, wenn der Datensatz vorhanden ist, zwei, wenn eine Einfügung erforderlich ist:
quelle
Normalerweise mache ich das, was einige der anderen Poster gesagt haben, um zuerst zu überprüfen, ob es vorhanden ist, und dann, was auch immer der richtige Weg ist. Eine Sache, die Sie dabei beachten sollten, ist, dass der von SQL zwischengespeicherte Ausführungsplan für den einen oder anderen Pfad möglicherweise nicht optimal ist. Ich glaube, der beste Weg, dies zu tun, besteht darin, zwei verschiedene gespeicherte Prozeduren aufzurufen.
Jetzt folge ich meinen eigenen Rat nicht sehr oft, also nimm es mit einem Körnchen Salz.
quelle
Wählen Sie aus, wenn Sie ein Ergebnis erhalten, aktualisieren Sie es. Wenn nicht, erstellen Sie es.
quelle