Question

J'ai une application qui utilise des numéros d'incidents (entre autres types de numéros). Ces chiffres sont stockés dans une table appelée « Number_Setup », qui contient la valeur courante du compteur.

Lorsque l'application génère un nouvel incident, il number_setup table et obtient la ligne de compteur de nombre requis (compteurs peuvent être remis à zéro tous les jours, toutes les semaines, etc et sont stockées sous forme de int). Il incremenets le compteur et met à jour la ligne avec la nouvelle valeur.

L'application est multi-utilisateur (environ 100 utilisateurs à tout moment, ainsi que des emplois sql qui fonctionnent et sirotez 100 de dossiers d'incidents et le nombre d'incidents de demande pour chacun). Le tableau incident a quelques numéros d'incident en double où ils ne devraient pas être double.

Une procédure stockée est utilisée pour récupérer le compteur suivant.

SELECT @Counter = counter, @ShareId=share_id, @Id=id
FROM Number_Setup
WHERE LinkTo_ID=@LinkToId
AND Counter_Type='I'

IF isnull(@ShareId,0) > 0
BEGIN 
    -- use parent counter
    SELECT @Counter = counter, @ID=id
    FROM Number_Setup
    WHERE Id=@ShareID
END

SELECT @NewCounter = @Counter + 1

UPDATE Number_Setup SET Counter = @NewCounter
WHERE id=@Id

Je suis maintenant entouré ce bloc avec une transaction, mais je ne suis pas tout à fait sûr que ce sera 100% de résoudre le problème, car je pense qu'il ya des verrous encore partagés, de sorte que le compteur peut être lu de toute façon.

Peut-être que je peux vérifier que le compteur n'a pas été mis à jour, dans la déclaration de mise à jour

UPDATE Number_Setup SET Counter = @NewCounter
WHERE Counter = @Counter
IF @@ERROR = 0 AND @@ROWCOUNT > 0 
    COMMIT TRANSACTION
ELSE
    ROLLBACK TRANSACTION

Je suis sûr que cela est un problème commun avec les numéros de facture dans les applications financières, etc.
Je ne peux pas mettre la logique dans le code soit et utiliser le verrouillage à ce niveau. J'ai aussi fermé à clef à HOLDLOCK mais je ne suis pas sûr de l'application. Faut-il mettre les deux instructions SELECT?

Comment puis-je assurer pas de doublons sont créés?

Était-ce utile?

La solution

L'astuce consiste à faire la mise à jour du compteur et lu en une seule opération atomique:

UPDATE Number_Setup SET Counter = Counter+1
OUTPUT INSERTED.Counter 
WHERE id=@Id;

Ce que n'attribue pas le nouveau compteur à @NewCounter, mais il retourne à la place un jeu de résultats au client. Si vous devez assigner, utiliser une variable de table intermédiaire pour la sortie du nouveau compteur en:

declare @NewCounter int;
declare @tabCounter table (NewCounter int);
UPDATE Number_Setup SET Counter = Counter+1
OUTPUT INSERTED.Counter INTO @tabCounter (NewCounter)
WHERE id=@Id
SELECT @NewCounter = NewCounter FROM @tabCounter;

Cela résout le problème de faire l'incrément du compteur atomique. Vous avez encore d'autres conditions de course dans votre procédure parce que le LinkTo_Id et share_id peuvent encore être mis à jour après la première sélection, vous pouvez donc incrémenter le compteur du lien erroné à l'article, mais qui ne peut pas être résolu seulement à partir de cet échantillon de code tel qu'il dépend aussi sur le code qui met à jour le shared_id et actualy / ou LinkTo_Id.

BTW vous devez entrer dans le Habitude du nom de vos champs avec la jurisprudence constante. Si elles sont nommé constamment vous doit utiliser le cas de correspondance exacte dans le code T-SQL. Vos scripts fonctionnent très bien maintenant parce que vous avez un cas serveur de classement insensible, si vous déployez sur un cas serveur de classement sensible et vos scripts ne correspondent pas le cas exact des erreurs de noms de champs / de tables suivront à gogo.

Autres conseils

avez-vous essayé d'utiliser GUIDs au lieu de autoincrements comme identifiant unique?

Si vous avez le ablity de modifier votre travail qui obtient mutiple records, je changerais la pensée afin que votre compteur est une colonne d'identité. Puis, quand vous obtenez l'enregistrement suivant, vous pouvez simplement faire un insert et d'obtenir l'identité @@ de la table. Cela vous assurer que vous obtenez le plus grand nombre. Vous auriez aussi faire un dbccReseed pour réinitialiser le compteur au lieu de juste mettre à jour la table lorsque vous souhaitez réinitialiser l'identité. Le seul problème est que vous auriez à faire quelque 100 insertions dans le cadre de votre travail sql pour obtenir un groupe d'identités. Cela peut être trop frais généraux, mais en utilisant une colonne d'identité est un moyen guarenteed d'obtenir des chiffres uniques.

Je pourrais manquer quelque chose, mais il semble que vous essayez de réinventer la technologie qui a déjà été résolu par la plupart des bases de données.

au lieu de la lecture et la mise à jour de la colonne « compteur » dans le tableau Number_Setup, pourquoi utilisez-vous pas seulement une clé primaire pour votre auto-incrémentation compteur? Vous aurez jamais une valeur en double pour une clé primaire.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top