Inserisci se non esiste, contemporaneamente


13

Sto riscontrando problemi di concorrenza con i miei inserti in una procedura memorizzata. La parte rilevante della procedura è questa:

select @_id = Id from table1 where othervalue = @_othervalue
IF( @_id IS NULL)
BEGIN
    insert into table1 (othervalue) values (@_othervalue)
    select @_id = Id from table1 where othervalue = @_othervalue
END

Quando eseguiamo 3 o 4 di questi processi memorizzati contemporaneamente, riceviamo più inserti in occasione.

Sto programmando di risolvere questo problema in questo modo:

insert into table1 (othervalue) 
    select TOP(1) @_othervalue as othervalue from table1 WITH(UPDLOCK) 
    where NOT EXISTS ( select * from table1 where othervalue = @_othervalue )

select @_id = Id from table1 where othervalue = @_othervalue

La domanda è: è come inserire contemporaneamente senza duplicati nel server SQL? Il fatto che devo usare TOP per inserire solo una volta mi disturba.


1
Non è necessario utilizzare TOP. Rimuovere il riferimento alla tabella FROM dall'istruzione SELECT.
ErikE,


@GSerg Penso che tu abbia ragione.
Chris,

Risposte:


7

È possibile utilizzare un'istruzione di unione con serializablehint.

merge table1 with (serializable) as T 
using (select @_othervalue as othervalue) as S
on T.othervalue = S.othervalue
when not matched then
  insert (othervalue) values (othervalue);

Hai stressato il tuo approccio da due o più connessioni?
AK,

2
@AlexKuznetsov - L'ho fatto poco fa per un'altra domanda su SO. Ho usato due schede in SSMS. Prima insert ... where not exist ...ho testato il modello e ho scoperto che è possibile ottenere deadlock e violazioni dei tasti, quindi è necessario utilizzare updlock e serializzabile. Ho quindi testato la dichiarazione di unione e ho pensato che avrebbe gestito le cose un po 'meglio e lo ha fatto perché lì dove non c'erano deadlock ma dovevo ancora usare la serializzazione per non avere violazioni chiave.
Mikael Eriksson,

1
Questa è una risposta davvero fantastica.
Chris Marisic,

5

Se non si desidera duplicati nella colonna "altro valore", è possibile farlo creando un unique constraintsu quella colonna. La query sarebbe:

 ALTER TABLE table1
 ADD CONSTRAINT unique_c_othervalue UNIQUE(othervalue)

Ciò restituirebbe un errore se una query tentasse di inserire un valore duplicato nella colonna "altro valore".


Come funzionerebbe se il vincolo univoco sia una tupla a due file?
Chris,

1
@Chris Come hai un vincolo unico che si estende su più file?
Aaron Bertrand

@Aaron Probabilmente ho terminato la mia terminologia, ma abbiamo due righe che insieme devono essere uniche. Non credo sia applicato nel nostro schema.
Chris,

2

Usa un vincolo unico come raccomanda @StanleyJohns. Quindi utilizza INIZIA PROVA FINE PROVA intorno alla tua dichiarazione di inserimento.

select @_id = Id from table1 where othervalue = @_othervalue
IF( @_id IS NULL)
BEGIN
    BEGIN TRY
        insert into table1 (othervalue) values (@_othervalue)
        select @_id = Id from table1 where othervalue = @_othervalue        
    END TRY
    BEGIN CATCH
        select @_id = Id from table1 where othervalue = @_othervalue        
    END CATCH
END
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.