Emule una secuencia TSQL a través de un procedimiento almacenado

17

Tengo el requisito de crear un procedimiento almacenado que emule una secuencia TSQL. Es decir, siempre da un valor entero distinto y creciente en cada llamada. Además, si se pasa un entero, debe devolver ese valor si nunca ha habido un resultado mayor o el siguiente entero más alto disponible. No hace falta decir que puede haber varios clientes llamando a este SP al mismo tiempo.

Dada una tabla MetaInfo con columnas MetaKey varchar (max) y MeatValueLong bigInt. Se espera que la fila con la MetaKey de 'Internal-ID-Last' contenga el último valor más alto asignado. Creé el siguiente procedimiento almacenado:

CREATE PROCEDURE [dbo].[uspGetNextID]
(
  @inID bigInt 
)
AS
BEGIN
    SET NOCOUNT ON;

    BEGIN TRANSACTION

    UPDATE MetaInfo WITH (ROWLOCK) 
      SET MetaValueLong = CASE 
                            WHEN ISNULL(MetaValueLong,0) > @inID THEN MetaValueLong+1 
                            ELSE @inID+1
                          END 
    WHERE MetaKey = 'Internal-ID-Last'

    SELECT MetaValueLong 
    FROM MetaInfo
    WHERE MetaKey = 'Internal-ID-Last'

    COMMIT TRANSACTION 

END

Mi pregunta es simplemente: ¿funciona este procedimiento almacenado como se esperaba (a todas las personas que llamen se les asignará un resultado único)?

Hogan
fuente
@todos: FYI, generado por esta Q en SO: stackoverflow.com/q/6342732/27535
gbn

Respuestas:

8

He echado un vistazo y los propios MS ofrecen una solución sin bloqueos

http://blogs.msdn.com/b/sqlcat/archive/2006/04/10/sql-server-sequence-number.aspx

Esta es una actualización simple sin sugerencias de bloqueo, pero dicen que bloquea / bloquea.

No hay mucho en SO sobre esto tampoco.

Me inclinaría a agregar UPDLOCK a su ROWLOCK (según "table as a queue" (SO) pero sin READPAST). Esto aumentará el aislamiento en caso de que un segundo proceso comience a leer.

Sin embargo, el hecho de que todos sus procesos quieran leer / escribir la misma fila me hace dudar de mí mismo. READPAST permite concurrencia segura, pero en este caso es inútil.

Nota: puede usar la cláusula OUTPUT en lugar de una segunda selección, entonces no necesita la transacción.

HTH ...

gbn
fuente
1
Me ganaste a eso. Tenga en cuenta que SQL Server 2011 incluye la funcionalidad SECUENCIA, por lo que el requisito de inventar el suyo debe desaparecer pronto (no antes de tiempo).
nvogel
@dportas: de hecho. Y también funciona mejor: dba.stackexchange.com/q/1635/630
gbn
@dportas: ¿la SECUENCIA puede permitir el requisito de entrada? En mi lectura rápida de la función, no vi esa funcionalidad.
Hogan
1

Falta lo siguiente

1. SET XACT_ABORT
2. Exception Handling (Try Catch)

Sí, debe cumplir con su condición. Una vez que tales situaciones surgen en las transacciones, crea sus múltiples instancias y, posteriormente, a todos los que llaman se les asignará un resultado único

Pankaj
fuente
Si me comprometo antes de la selección, ¿no existe la posibilidad de seleccionar el resultado guardado por una llamada diferente?
Hogan
No estoy seguro de eso. Pero parece que tienes razón. Necesito verificarlo. Gracias. Por cierto +1 para, buena pregunta ...
0

Una solución más escalable que no requiere serialización es esta:

CREATE PROCEDURE [dbo].[uspGetNextID]
(
  @inID BIGINT OUT
)
AS
      SET NOCOUNT ON
      SET IDENTITY_INSERT SequenceTable ON;
      INSERT INTO SequenceTable (id) VALUES (@inID);
      SET IDENTITY_INSERT SequenceTable OFF;
      INSERT INTO SequenceTable DEFAULT VALUES;
      DELETE FROM SequenceTable WITH (READPAST);
      SET @inID = SCOPE_IDENTITY();
RETURN;
nvogel
fuente
OP tiene un requisito para permitir a los usuarios pasar de un valor que lo complica ...
gbn
@dportas - Gracias. Conocía este enfoque, pero no funciona aquí debido al parámetro de entrada.
Hogan
@Hogan, he modificado mi sugerencia para manejar el parámetro de entrada. Sin embargo, en su mayoría no ha sido probado.
nvogel
@dportas - A llamado con inID de 500, B llamado con inID de 50 - acción - A regresa con 51, B con 52. Fallo del requisito.
Hogan
Interesante. Obtengo resultados diferentes (en 2008r2). ¿Puede publicar la reproducción completa con DDL y establecer su compilación / versión.
nvogel