2010-11-19 6 views
1

Je démarre un projet dans lequel je dois m'assurer qu'un grand nombre d'utilisateurs peuvent obtenir un code promotionnel pour plusieurs promotions. Les codes ont une valeur monétaire qui leur est attachée. Il est donc vital qu'un seul code soit envoyé à chaque utilisateur et que deux utilisateurs ne puissent jamais recevoir le code.SQL Server obtient des enregistrements uniques

Mon plan jusqu'à présent est de créer une table dans SQL Server connue sous le nom code_pool et de faire un encart en bloc de codes à chaque fois.

table SQL Server 2005 ressemblerait à ceci ...

[id](int pk), [promo_code] varchar(150), [promotion_id](int fk) 

Les utilisateurs seraient alors retreive chaque code à l'aide d'un proc stocké qui obtiendrait le premier enregistrement de la table pour que la promotion et supprimer (ou mettre à jour) l'enregistrement avant de retourner le code à la suite de la procédure.

Ma question est la suivante: comment puis-je m'assurer que l'enregistrement est correctement verrouillé pour qu'un seul utilisateur puisse obtenir chaque enregistrement et que deux utilisateurs accédant simultanément au proc ne reçoivent jamais le même code? Dois-je verrouiller la table/les enregistrements et si oui, comment cela se cumulerait-il dans un environnement de production occupé?

Répondre

1

ne souhaitez-vous pas ajouter une colonne, par exemple? in_use (int)? Lorsque vous générez un nouveau code promotionnel, in_use = 0, lorsque votre proc mémorisée obtient le code promotionnel non utilisé, elle sélectionne le premier code où in_use = 0, puis le met à jour 1

0

Pourquoi ne pas utiliser quelque chose de similaire, mais comme ceci:

Table UsedCodes 
[id] int identity PK, 
[userId] whatever, 
[PromoId] int FK 

Table Promotions 
[PromoId] int pk, 
[PromoCode] nvarchar 

Lorsqu'un utilisateur reçoit un code promo, vous insérerait une valeur dans les codes utilisés, et le code de promotion livrés à eux serait une concaténation du code promo et l'ID dans le tableau des codes utilisés.

Vous pouvez également appliquer une contrainte unique sur UserId | PromoId sur la table des codes utilisés, pour garantir un seul code par promo par utilisateur. Ceci a l'avantage de conserver une trace des codes utilisés, et réduit la complexité d'avoir besoin de votre encart en vrac, ce qui pourrait potentiellement introduire des doublons accidentellement. Il a également l'avantage de ne nécessiter aucun verrouillage ...

2

Un type de données intégré très pratique pour générer des codes uniques difficilement concevables est le type de données uniqueidentifier. Vous pouvez l'utiliser pour générer un code unique en lui donnant une valeur générée automatiquement (en utilisant la fonction newid()). Parce que les GUID sont dans HEX et ne sont pas générés séquentiellement contrairement aux colonnes d'identité, il n'est pas possible de prédire quels codes ont été ou seront générés, ce qui rendra votre processus moins vulnérable à quelqu'un qui essaie les codes en séquence. Le nombre d'identifiants uniques possibles est très important.

J'ai supposé que vous ne souhaitiez qu'un seul code promotionnel par personne pour chacune de vos promotions. La façon dont vous pouvez faire cela dans votre base de données est d'avoir une table, mon exemple l'appelle PromoTest, qui a une clé primaire sur ces deux colonnes, ce qui garantit qu'elles restent uniques. Je n'ai pas ajouté un concept de 'Utilisé' pour indiquer si la personne a utilisé le code mais c'est assez trivial à faire.

Pour créer votre table avec la contrainte de clé primaire et la valeur exécutez la commande suivante généré automatiquement:

CREATE TABLE [dbo].[PromoTest](
[personid] [bigint] NOT NULL, [promocategory] [int] NOT NULL, 
[promocode] [uniqueidentifier] NOT NULL, 
    CONSTRAINT [PK_PromoTest] PRIMARY KEY CLUSTERED ( 
     [personid] ASC,  
     [promocategory] ASC) 
     WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS 
= ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]) ON [PRIMARY] 

GO 

ALTER TABLE [dbo].[PromoTest] ADD CONSTRAINT [DF_PromoTest_promocode] DEFAULT (newid()) FOR [promocode] 

Pour avoir ensuite une procédure stockée qui insère un nouveau code promo ou sélectionne l'existant est tout à fait trivial , et en raison de la contrainte de clé primaire, vous ne pouvez pas insérer physiquement deux codes du même type pour la même personne.

La procédure stockée peut être définie comme suit:

CREATE PROCEDURE GetOrCreatePromoCode 
    -- Add the parameters for the stored procedure here 
    @PersonId bigint, 
    @PromoCategory int, 
    @PromoCode uniqueidentifier OUT 
AS 
BEGIN 
    -- SET NOCOUNT ON added to prevent extra result sets from 
    -- interfering with SELECT statements. 
    SET NOCOUNT ON; 

    -- Insert statements for procedure here 
    IF (NOT EXISTS(SELECT PromoCode FROM PromoTest WHERE personid = @PersonId AND promocategory = @PromoCategory)) 
     BEGIN 
     INSERT INTO PromoTest (personid, promocategory) VALUES (@PersonId, @PromoCategory)  
     END 
    SET @PromoCode = (SELECT PromoCode FROM PromoTest WHERE personid = @PersonId AND promocategory = @PromoCategory) 
END 
GO 
0

John P vous a donné une excellente réponse mais je trouve GUIDs sont difficiles à manier pour l'utiliser comme un code de bon en raison de la longueur de la chaîne. Jetez un oeil à la question how to generate a voucher code in c#? et bien sûr ma réponse :) Bien que vous demandiez une solution SQL, vous pouvez probablement adapter les idées qui y sont données.

Je vous recommande également de ne pas supprimer les codes qui ont été utilisés; Comment allez-vous être sûr que le code présenté par le client a été créé par votre système?

Questions connexes