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

Seleziona/Inserisci versione di un Upsert:esiste un modello di progettazione per un'elevata concorrenza?

Puoi usare i LOCK per rendere le cose SERIALIZZABILI ma questo riduce la concorrenza. Perché non provare prima la condizione comune ("per lo più inserisci o per lo più seleziona") seguita da una gestione sicura dell'azione "riparatrice"? Cioè, il modello "JFDI"...

Per lo più INSERT previsti (parco palla 70-80%+):

Prova a inserire. Se fallisce, la riga è già stata creata. Non c'è bisogno di preoccuparsi della concorrenza perché TRY/CATCH si occupa dei duplicati per te.

BEGIN TRY
   INSERT Table VALUES (@Value)
   SELECT @id = SCOPE_IDENTITY()
END TRY
BEGIN CATCH
    IF ERROR_NUMBER() <> 2627
      RAISERROR etc
    ELSE -- only error was a dupe insert so must already have a row to select
      SELECT @id = RowID FROM Table WHERE RowValue = @VALUE
END CATCH

Principalmente SELECT:

Simile, ma prima prova a ottenere i dati. Nessun dato =INSERTO necessario. Di nuovo, se 2 chiamate simultanee provano a INSERT perché entrambe hanno trovato la riga mancante degli handle TRY/CATCH.

BEGIN TRY
   SELECT @id = RowID FROM Table WHERE RowValue = @VALUE
   IF @@ROWCOUNT = 0
   BEGIN
       INSERT Table VALUES (@Value)
       SELECT @id = SCOPE_IDENTITY()
   END
END TRY
BEGIN CATCH
    IF ERROR_NUMBER() <> 2627
      RAISERROR etc
    ELSE
      SELECT @id = RowID FROM Table WHERE RowValue = @VALUE
END CATCH

Il secondo sembra ripetersi, ma è altamente simultaneo. I blocchi otterrebbero lo stesso risultato ma a scapito della concorrenza...

Modifica:

Perché non per utilizzare UNISCI...

Se utilizzi la clausola OUTPUT, restituirà solo ciò che è aggiornato. Quindi è necessario un UPDATE fittizio per generare la tabella INSERTED per la clausola OUTPUT. Se devi eseguire aggiornamenti fittizi con molte chiamate (come implicito da OP) sono molte scritture di log solo per poter usare MERGE.