Implementazione di SQL Server 2005 di MySQL REPLACE INTO?


86

MySQL ha questo REPLACE INTOcomando SQL incredibilmente utile ma proprietario .

Può essere facilmente emulato in SQL Server 2005?

Avviare una nuova Transazione, fare un Select()e poi o UPDATEo INSERTed COMMITè sempre un po 'una seccatura, specialmente quando lo si fa nell'applicazione e quindi si conservano sempre 2 versioni dell'istruzione.

Mi chiedo se esista un modo semplice e universale per implementare una tale funzione in SQL Server 2005?

Risposte:


60

Questo è qualcosa che mi infastidisce di MSSQL ( sfogo sul mio blog ). Vorrei che MSSQL fosse supportato upsert.

Il codice di @ Dillie-O è un buon metodo nelle versioni SQL precedenti (+1 voto), ma fondamentalmente sono ancora due operazioni di I / O (il existse poi il updateo insert)

C'è un modo leggermente migliore in questo post , fondamentalmente:

--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', ... )

Questo lo riduce a una operazione di I / O se si tratta di un aggiornamento, o due se si tratta di un inserimento.

MS Sql2008 introduce mergedallo standard SQL: 2003:

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, ... )

Ora è davvero solo un'operazione IO, ma codice orribile :-(


Grazie mille! Salva il Select e spesso non necessita nemmeno di una teransazione in situazioni in cui posso essere certo che tra Update e "mio" insert non ci siano altri inserti per quella chiave.
Michael Stum

2
@Michael È meglio avere un indice univoco su questa tabella e gestire gli errori di chiave duplicati se si intende utilizzare questa soluzione.
Sam Saffron,

3
@ Keith La tua dichiarazione di unione non funziona. MERGEnon supporta la WHEREclausola, è necessario riscriverla utilizzando USINGe ON. Inoltre, a meno che tu non aggiunga WITH (HOLDLOCK), c'è una gara e INSERTpotrebbero verificarsi s concorrenti , con uno di loro che fallisce a causa dello scontro chiave.
Evgeniy Berezovsky

Sì, come sottolineato qui: weblogs.sqlteam.com/dang/archive/2009/01/31/… MERGE non è atomico. Elimina un blocco di aggiornamento implicito, ma lo rilascia prima di eseguire un inserimento, il che causa una condizione di competizione che può causare violazioni della chiave primaria. È necessario utilizzare un HOLDLOCK esplicito oltre all'UPDLOCK implicito affinché l'operazione sia atomica. Allo stato attuale, non è atomico, nonostante sembri essere una singola affermazione.
Triynko

1
La sintassi MERGE è sbagliata ed è stata corretta in una risposta più recente dello stesso autore: stackoverflow.com/a/243670/24472
Larry

21

La funzionalità che stai cercando è tradizionalmente chiamata UPSERT. Almeno sapere come si chiama potrebbe aiutarti a trovare quello che stai cercando.

Non credo che SQL Server 2005 abbia ottimi modi per farlo. Il 2008 introduce l'istruzione MERGE che può essere utilizzata per ottenere ciò come mostrato in: http://www.databasejournal.com/features/mssql/article.php/3739131 o http://blogs.conchango.com/davidportas/archive/ 2007/11/14 / SQL-Server-2008-MERGE.aspx

Merge era disponibile nella beta del 2005, ma l'hanno rimossa nella versione finale.


18

Ciò che sta facendo l'upsert / merge è qualcosa che ha l'effetto di ...

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

Quindi si spera che la combinazione di questi articoli e questo pseudo codice possa far muovere le cose.


10

Ho scritto un post sul blog su questo problema.

La conclusione è che se desideri aggiornamenti economici e vuoi essere sicuro per l'utilizzo simultaneo, prova:

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

In questo modo hai 1 operazione per gli aggiornamenti e un massimo di 3 operazioni per gli inserimenti. Quindi, se stai aggiornando generalmente, questa è un'opzione economica e sicura.

Sarei anche molto attento a non usare nulla che non sia sicuro per l'utilizzo simultaneo. È davvero facile ottenere violazioni della chiave primaria o duplicare righe in produzione.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.