SQL Server 2005-Implementierung von MySQL REPLACE INTO?

86

MySQL hat dies unglaublich nützlich und doch proprietär REPLACE INTO SQL-Befehl.

Kann dies in SQL Server 2005 einfach emuliert werden?

Das Starten einer neuen Transaktion, das Ausführen eines Select()und dann entweder UPDATEoder INSERTund und COMMITist immer ein wenig mühsam, insbesondere wenn Sie dies in der Anwendung tun und daher immer zwei Versionen der Anweisung behalten.

Ich frage mich, ob es eine einfache und universelle Möglichkeit gibt, eine solche Funktion in SQL Server 2005 zu implementieren.

Michael Stum
quelle

Antworten:

60

Das nervt mich an MSSQL ( schimpfen auf meinem Blog ). Ich wünsche MSSQL unterstützt upsert.

Der Code von @ Dillie-O ist ein guter Weg in älteren SQL-Versionen (+1 Stimmen), aber es handelt sich im Grunde immer noch um zwei E / A-Operationen (die existsund dann die updateoder insert)

Grundsätzlich gibt es einen etwas besseren Weg für diesen Beitrag :

--try an update
update tablename 
set field1 = 'new value',
    field2 = 'different value',
    ...
where idfield = 7

--insert if failed
if @@rowcount = 0 and @@error = 0
    insert into tablename 
           ( idfield, field1, field2, ... )
    values ( 7, 'value one', 'another value', ... )

Dies reduziert es auf eine E / A-Operation, wenn es sich um ein Update handelt, oder auf zwei, wenn es sich um eine Einfügung handelt.

MS Sql2008 führt mergeaus dem SQL: 2003-Standard ein:

merge tablename as target
using (values ('new value', 'different value'))
    as source (field1, field2)
    on target.idfield = 7
when matched then
    update
    set field1 = source.field1,
        field2 = source.field2,
        ...
when not matched then
    insert ( idfield, field1, field2, ... )
    values ( 7,  source.field1, source.field2, ... )

Jetzt ist es wirklich nur eine E / A-Operation, aber schrecklicher Code :-(

Keith
quelle
Vielen Dank! Speichert die Auswahl und benötigt oft nicht einmal eine Teransaktion in Situationen, in denen ich sicher sein kann, dass zwischen dem Update und "meiner" Einfügung keine andere Einfügung für diesen Schlüssel vorhanden ist.
Michael Stum
2
@Michael Sie sollten einen eindeutigen Index für diese Tabelle haben und doppelte Schlüsselfehler behandeln, wenn Sie diese Lösung verwenden möchten.
Sam Saffron
3
@Keith Ihre Merge-Anweisung funktioniert nicht. MERGEunterstützt die WHEREKlausel nicht, müssen Sie diese mit USINGund umschreiben ON. Wenn Sie nicht hinzufügen WITH (HOLDLOCK), gibt es auch ein Rennen und es kann gleichzeitig zu INSERTs kommen, wobei eines davon aufgrund des Schlüsselkonflikts fehlschlägt.
Evgeniy Berezovsky
Ja, wie hier ausgeführt: weblogs.sqlteam.com/dang/archive/2009/01/31/… MERGE ist nicht atomar. Es nimmt eine implizite Aktualisierungssperre auf, gibt sie jedoch frei, bevor eine Einfügung durchgeführt wird. Dies führt zu einer Race-Bedingung, die zu Verletzungen des Primärschlüssels führen kann. Sie müssen zusätzlich zum impliziten UPDLOCK ein explizites HOLDLOCK verwenden, damit die Operation atomar ist. So wie es aussieht, ist es nicht atomar, obwohl es eine einzige Aussage zu sein scheint.
Triynko
1
Die MERGE-Syntax ist falsch und wurde in einer neueren Antwort desselben Autors behoben
Larry
21

Die gesuchte Funktionalität wird traditionell als UPSERT bezeichnet. Wenn Sie zumindest wissen, wie es heißt, können Sie leichter finden, wonach Sie suchen.

Ich glaube nicht, dass SQL Server 2005 großartige Möglichkeiten bietet, dies zu tun. 2008 wird die MERGE-Anweisung eingeführt, mit der dies erreicht werden kann: http://www.databasejournal.com/features/mssql/article.php/3739131 oder http://blogs.conchango.com/davidportas/archive/ 2007/11/14 / SQL-Server-2008-MERGE.aspx

Merge war in der Beta von 2005 verfügbar, wurde jedoch in der endgültigen Version entfernt.

Karl Seguin
quelle
18

Was der Upsert / Merge tut, hat etwas mit der Wirkung von ...

IF EXISTS (SELECT * FROM [Table] WHERE Id = X)
   UPDATE [Table] SET...
ELSE
   INSERT INTO [Table]

Hoffentlich kann die Kombination dieser Artikel und dieses Pseudocodes die Dinge in Bewegung bringen.

Dillie-O
quelle
10

Ich habe einen Blog-Beitrag zu diesem Thema geschrieben.

Wenn Sie günstige Updates wünschen und für die gleichzeitige Verwendung sicher sein möchten, versuchen Sie Folgendes:

update t
set hitCount = hitCount + 1
where pk = @id

if @@rowcount < 1 
begin 
   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
end

Auf diese Weise haben Sie 1 Operation für Aktualisierungen und maximal 3 Operationen für Einfügungen. Wenn Sie also allgemein aktualisieren, ist dies eine sichere, günstige Option.

Ich würde auch sehr vorsichtig sein, nichts zu verwenden, was für die gleichzeitige Verwendung unsicher ist. Es ist wirklich einfach, Primärschlüsselverletzungen oder doppelte Zeilen in der Produktion zu erhalten.

Sam Safran
quelle