Émuler une séquence TSQL via une procédure stockée

17

J'ai besoin de créer une procédure stockée qui émule une séquence TSQL. Autrement dit, il donne toujours une valeur entière distincte croissante à chaque appel. De plus, si un entier est passé, il devrait retourner cette valeur s'il n'y a jamais eu de résultat supérieur ou le prochain entier le plus élevé disponible. Il va sans dire qu'il peut y avoir plusieurs clients appelant ce SP en même temps.

Étant donné une table MetaInfo avec les colonnes MetaKey varchar (max) et MeatValueLong bigInt. Il est prévu que la ligne avec la méta-clé «Internal-ID-Last» contienne la dernière valeur la plus élevée attribuée. J'ai créé la procédure stockée suivante:

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

Ma question est simplement: cette procédure stockée fonctionne-t-elle comme prévu (tous les appelants se verront attribuer un résultat unique)?

Hogan
la source
@all: FYI, engendré par ce Q sur SO: stackoverflow.com/q/6342732/27535
gbn

Réponses:

8

J'ai jeté un coup d'œil et MS propose lui-même une solution sans verrouillage

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

Il s'agit d'une simple mise à jour sans indice de verrouillage, mais ils disent que cela verrouille / interbloque.

Rien de bien spécial à ce sujet non plus.

Je serais enclin à ajouter UPDLOCK à votre ROWLOCK (selon "table as a queue" (SO) mais sans READPAST). Cela augmentera l'isolement au cas où un deuxième processus commencerait la lecture.

Cependant, le fait que tous vos processus souhaitent lire / écrire la même ligne me fait me deviner. READPAST permet une concurrence sécurisée mais dans ce cas, il est inutile.

Remarque: vous pouvez utiliser la clause OUTPUT au lieu d'une 2e sélection, alors vous n'avez pas besoin de la transaction.

HTH ...

gbn
la source
1
Tu m'as battu. Notez que SQL Server 2011 inclut la fonctionnalité SEQUENCE, donc l'exigence d'inventer la vôtre devrait disparaître rapidement (pas avant l'heure).
nvogel
@dportas: en effet. Et fonctionne mieux aussi: dba.stackexchange.com/q/1635/630
GBN
@dportas - SEQUENCE peut-il prendre en compte les exigences d'entrée? Dans ma lecture rapide de la fonctionnalité, je n'ai pas vu cette fonctionnalité.
Hogan
1

La chose suivante manque

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

Oui, il devrait répondre à votre condition. Une fois que de telles situations surviennent dans les transactions, il crée ses multiples instances et par la suite, tous les appelants se verront attribuer un résultat unique

Pankaj
la source
Si je m'engage avant la sélection, n'y a-t-il pas une possibilité que je sélectionne le résultat enregistré par un autre appel?
Hogan
Je n'en suis pas sûr. Mais il semble que vous ayez raison. Je dois vérifier. Merci. BTW +1 pour, bonne question ...
0

Une solution plus évolutive qui ne nécessite pas de sérialisation est la suivante:

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
la source
OP a l'obligation de permettre aux utilisateurs de passer une valeur qui le complique ...
gbn
@dportas - Merci. J'étais au courant de cette approche, mais cela ne fonctionne pas ici à cause du paramètre d'entrée.
Hogan
@Hogan, j'ai modifié ma suggestion pour gérer le paramètre d'entrée. Ce n'est cependant pas testé.
nvogel
@dportas - A appelé avec inID de 500, B appelé avec inID de 50 - action - A revient avec 51, B avec 52. Exigence échouée.
Hogan
Intéressant. J'obtiens des résultats différents (sur 2008r2). Pouvez-vous publier la repro complète avec DDL et indiquer votre build / version.
nvogel