Wstaw, jeśli nie istnieje, jednocześnie

13

Mam problemy z współbieżnością z moimi wstawkami w procedurze przechowywanej. Odpowiednia część procedury jest następująca:

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

Kiedy uruchamiamy 3 lub 4 z tych przechowywanych proc jednocześnie, otrzymujemy od czasu do czasu wiele wstawek.

Planuję to naprawić tak:

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

Pytanie brzmi, czy to jak wstawiać jednocześnie bez duplikatów na serwerze SQL? Niepokoi mnie fakt, że muszę używać TOP, aby wstawić tylko raz.

Chris
źródło
1
Nie musisz używać TOP. Usuń odwołanie do tabeli FROM z instrukcji SELECT.
ErikE
możliwy duplikat
plusów i minusów
@GSerg Myślę, że masz rację.
Chris

Odpowiedzi:

7

Możesz użyć instrukcji scalania z serializablepodpowiedziami.

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);
Mikael Eriksson
źródło
Czy testowałeś swoje podejście z dwóch lub więcej połączeń?
AK
2
@AlexKuznetsov - Zrobiłem to jakiś czas temu dla innego pytania na temat SO. Użyłem dwóch kart w SSMS. Najpierw przetestowałem insert ... where not exist ...wzorzec i odkryłem, że można uzyskać zakleszczenia i kluczowe naruszenia, więc konieczne było użycie aktualizacji i szeregowania. Następnie przetestowałem instrukcję scalenia i pomyślałem, że poradzi sobie ona trochę lepiej i zrobiło to, ponieważ tam nie było żadnych zakleszczeń, ale nadal musiałem używać serializowania, aby nie mieć kluczowych naruszeń.
Mikael Eriksson
1
To naprawdę niesamowita odpowiedź.
Chris Marisic
5

Jeśli nie chcesz duplikatów w kolumnie „inna wartość”, możesz to zrobić, tworząc unique constraintkolumnę w tej kolumnie. Zapytanie byłoby:

 ALTER TABLE table1
 ADD CONSTRAINT unique_c_othervalue UNIQUE(othervalue)

Spowoduje to zwrócenie błędu, jeśli zapytanie spróbuje wstawić zduplikowaną wartość w kolumnie „inna wartość”.

StanleyJohns
źródło
Jak by to działało, gdyby unikalnym ograniczeniem była krotka dwurzędowa?
Chris
1
@Chris W jaki sposób masz unikalne ograniczenie, które obejmuje wiersze?
Aaron Bertrand
@Aaron Prawdopodobnie mam wyłączoną terminologię, ale mamy dwa wiersze, które razem muszą być unikalne. Nie sądzę, że jest to wymuszone w naszym schemacie.
Chris
2

Użyj unikalnego ograniczenia, takiego jak zalecane przez @StanleyJohns. Następnie użyj BEGIN TRY END TRY wokół instrukcji insert.

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
mrdenny
źródło