Sqlserver
 sql >> Database >  >> RDS >> Sqlserver

Come implementare una stored procedure Upsert condizionale?

Ho messo insieme il seguente script per provare questo trucco che ho usato negli anni passati. Se lo usi, dovrai modificarlo per adattarlo ai tuoi scopi. I commenti seguono:

/*
CREATE TABLE Item
 (
   Title      varchar(255)  not null
  ,Teaser     varchar(255)  not null
  ,ContentId  varchar(30)  not null
  ,RowLocked  bit  not null
)


UPDATE item
 set RowLocked = 1
 where ContentId = 'Test01'

*/


DECLARE
  @Check varchar(30)
 ,@pContentID varchar(30)
 ,@pTitle varchar(255)
 ,@pTeaser varchar(255)

set @pContentID = 'Test01'
set @pTitle     = 'TestingTitle'
set @pTeaser    = 'TestingTeasier'

set @check = null

UPDATE dbo.Item
 set
   @Check = ContentId
  ,Title  = @pTitle
  ,Teaser = @pTeaser
 where ContentID = @pContentID
  and RowLocked = 0

print isnull(@check, '<check is null>')

IF @Check is null
    INSERT dbo.Item (ContentID, Title, Teaser, RowLocked)
     values (@pContentID, @pTitle, @pTeaser, 0)

select * from Item

Il trucco qui è che puoi impostare valori nelle variabili locali all'interno di un'istruzione Update. Sopra, il valore "flag" viene impostato solo se l'aggiornamento funziona (ovvero, i criteri di aggiornamento sono soddisfatti); in caso contrario, non verrà modificato (qui, lasciato su null), puoi verificarlo ed elaborare di conseguenza.

Per quanto riguarda la transazione e la sua serializzazione, vorrei sapere di più su cosa deve essere incapsulato all'interno della transazione prima di suggerire come procedere.

-- Addenda, follow-up dal secondo commento qui sotto -----------

Le idee del signor Saffron sono un modo completo e solido per implementare questa routine poiché le tue chiavi primarie sono definite all'esterno e passate al database (cioè non stai usando colonne di identità - per me va bene, sono spesso abusate).

Ho eseguito altri test (aggiunto un vincolo di chiave primaria sulla colonna ContentId, avvolgere UPDATE e INSERT in una transazione, aggiungere il suggerimento serializzabile all'aggiornamento) e sì, dovrebbe fare tutto ciò che vuoi. L'aggiornamento non riuscito applica un blocco dell'intervallo su quella parte dell'indice e ciò bloccherà tutti i tentativi simultanei di inserire quel nuovo valore nella colonna. Naturalmente, se N richieste vengono inviate contemporaneamente, la "prima" creerà la riga e verrà immediatamente aggiornata dalla seconda, terza, ecc., a meno che non si imposti il ​​"blocco" da qualche parte lungo la linea. Bel trucco!

(Nota che senza l'indice sulla colonna chiave, bloccheresti l'intera tabella. Inoltre, il blocco dell'intervallo potrebbe bloccare le righe su "entrambi i lati" del nuovo valore, o forse no, non l'ho fatto provalo. Non dovrebbe importare, dal momento che la durata dell'operazione dovrebbe essere [?] in millisecondi a una cifra.)