Domanda

Ho scritto una stored proc che farà un aggiornamento se un record esistente, altrimenti farà un inserto.Sembra qualcosa di simile a questo:

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

La mia logica dietro la scrittura in questo modo è che l'aggiornamento verrà eseguito un implicito selezionare utilizzando la clausola where e se restituisce 0 inserisci avrà luogo.

L'alternativa a questo modo sarebbe quello di fare una selezione e poi in base al numero di righe restituite fare un aggiornamento o di inserimento.Questo l'ho considerato inefficiente, perché se sei di fare un aggiornamento sarà causa di 2 seleziona (la prima esplicita chiamata di selezione e la seconda implicita in cui l'aggiornamento).Se il proc stati a fare un inserto quindi non ci sarebbe nessuna differenza in termini di efficienza.

È la mia logica, il suono qui?È questo come si potrebbe combinare un insert e update in una stored proc?

È stato utile?

Soluzione

La tua ipotesi è giusta, questo è il modo migliore per farlo, ed è chiamato upsert/unione.

Importanza di UPSERT - da sqlservercentral.com:

Per ogni aggiornamento, nel caso di cui sopra ci sono la rimozione di un ulteriore lettura da tavolo, se ci utilizzare il UPSERT invece ESISTE.Purtroppo per un Inserimento, sia il UPSERT e SE ESISTE metodi di utilizzare il stesso numero di letture sul tavolo.Pertanto, il controllo per l'esistenza dovrebbe essere fatto solo quando c'è un molto valido motivo per giustificare l' ulteriori I/O.Il metodo ottimizzato per fare le cose è quello di assicurarsi che si hanno poco legge è possibile" DB.

La strategia migliore è quella di tentare la aggiornamento.Se no righe interessate dalla aggiornamento quindi inserire.In più circostanze, la riga già esiste e solo uno di I/O sarà richiesto.

Modifica:Si prega di controllare questa risposta e i connessi post di blog per conoscere i problemi con questo modello e come fare un lavoro sicuro.

Altri suggerimenti

Si prega di leggere il post sul mio blog per una buona, sicura di modello è possibile utilizzare.Ci sono un sacco di considerazioni, e accettato risposta a questa domanda non è lontano dalla sicurezza.

Per una risposta veloce, provare il seguente schema.Funziona benissimo in SQL 2000 e superiori.SQL 2005 dà un errore di gestione che apre altre opzioni e SQL 2008 dà un comando MERGE.

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

Se per essere utilizzato con SQL Server 2000/2005 codice originale deve essere racchiuso in una transazione per assicurarsi che i dati rimangono costanti in contemporanea scenario.

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

Ciò comporterà ulteriori costi a livello di prestazioni, ma garantisce l'integrità dei dati.

Aggiungere, come già suggerito, UNIONE dovrebbe essere utilizzato quando disponibili.

MERGE è una delle nuove funzionalità di SQL Server 2008, by the way.

Non solo bisogno di eseguire delle transazioni, necessita anche di alto livello di isolamento.Ho fatto predefinito livello di isolamento è Read Committed e questa necessità di codice Serializable.

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

Magari aggiungendo anche le @@di errore di controllo e il ripristino potrebbe essere una buona idea.

Se non si fa un merge in SQL 2008 si deve cambiare a:

se @@rowcount = 0 e @@error=0

in caso contrario, se l'aggiornamento non riesce, per qualche ragione, poi si cercherà e a un inserimento dopo, perché il conteggio delle righe su un fallito istruzione 0

Grande fan del UPSERT, davvero riduce il codice per gestire.Qui è un altro modo per farlo:Uno dei parametri di input è l'ID, se l'ID è NULL o 0, sai che si tratta di un INSERTO, altrimenti si tratta di un aggiornamento.Presuppone l'applicazione sa se c'è un ID, così non funzionare in tutte le situazioni, ma tagliare la esegue a metà, se necessario.

La logica sembra il suono, ma si potrebbe desiderare di prendere in considerazione l'aggiunta di un po ' di codice per evitare che inserisci se erano passati in una specifica chiave primaria.

In caso contrario, se stai sempre a fare un inserimento se l'aggiornamento non influiscono in alcun record, cosa succede quando qualcuno cancella il record prima di voi "UPSERT" corre?Ora il record si stavano cercando di aggiornamento non esiste, quindi creare un record.Che probabilmente non è il comportamento che state cercando.

Modificato Dima Malenko post:

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 

È possibile intercettare l'errore e inviare il record di un fallito inserisci tabella.
Avevo bisogno di fare questo perché si sta assumendo qualsiasi tipo di dati è inviare tramite WSDL e, se possibile, di fissaggio internamente.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top