sql >> Base de Datos >  >> RDS >> Sqlserver

Seleccionar/Insertar versión de un Upsert:¿existe un patrón de diseño para alta concurrencia?

Puede usar LOCK para hacer que las cosas sean SERIALIZABLES, pero esto reduce la concurrencia. ¿Por qué no probar primero la condición común ("en su mayoría insertar o en su mayoría seleccionar") seguido de un manejo seguro de la acción "remedial"? Es decir, el patrón "JFDI"...

En su mayoría INSERT esperados (parque de béisbol 70-80%+):

Solo intenta insertar. Si falla, la fila ya se ha creado. No debe preocuparse por la concurrencia porque TRY/CATCH se ocupa de los duplicados por usted.

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

Mayormente SELECCIONA:

Similar, pero trate de obtener datos primero. Sin datos =se necesita INSERTAR. Nuevamente, si 2 llamadas simultáneas intentan INSERTAR porque ambas encontraron que a la fila le faltan los identificadores 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

El segundo parece repetirse, pero es muy concurrente. Los bloqueos lograrían lo mismo pero a expensas de la concurrencia...

Editar:

Por qué no para usar COMBINAR...

Si usa la cláusula OUTPUT, solo devolverá lo que se actualiza. Por lo tanto, necesita una ACTUALIZACIÓN ficticia para generar la tabla INSERTADA para la cláusula OUTPUT. Si tiene que hacer actualizaciones ficticias con muchas llamadas (como lo implica OP), eso es una gran cantidad de escrituras de registro solo para poder usar MERGE.