SQL Server获取唯一记录

时间:2010-11-19 09:49:52

标签: database sql-server-2005 stored-procedures locking

我正在启动一个项目,我需要确保大量用户可以获得多个促销活动的促销代码。

代码附有货币价值,因此每个用户只能输出一个代码,而且没有两个用户可以接收代码,这一点至关重要。

到目前为止,我的计划是在SQL Server中创建一个名为code_pool的表,并且每次都会批量插入代码。

SQL Server 2005表看起来像这样......

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

然后,用户将使用存储过程来检索每个代码,该过程将从该表中获取该促销的第一条记录,然后在作为过程结果返回代码之前删除(或更新)记录。

我的问题是如何确保记录被正确锁定,以便只有一个用户可以获得每个记录,并且没有两个用户同时访问proc会收到相同的代码?

我是否需要锁定表/记录,如果是这样,它将如何在繁忙的生产环境中堆叠?

4 个答案:

答案 0 :(得分:2)

用于生成不易猜测的唯一代码的一种非常方便的内置数据类型是uniqueidentifier数据类型。您可以使用它来生成唯一代码,方法是使其具有自动生成的值(使用newid()函数)。由于GUID是HEX并且不像标识列那样按顺序生成,因此无法预测已生成或将生成的代码,这将使您的流程不再容易受到依次尝试代码的人的攻击。可能的uniqueidentifier数量非常大。

我已经假设每个宣传片每个人只需要一个促销代码。你在数据库中这样做的方法是通过一个表,我的例子称之为PromoTest,它在这两列上都有一个主键,这将确保它们保持唯一。我没有添加“使用”的概念来表明该人是否使用过该代码,但这样做非常简单。

要使用主键约束和自动生成的值创建表,请运行以下命令:

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]

然后有一个插入新的促销代码或选择现有代码的存储过程非常简单,并且由于主键约束,您无法为同一个人物理插入两个相同类型的代码。

存储过程可以定义如下:

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

答案 1 :(得分:1)

你不想添加一个列,例如in_use(int)?当你生成新的promocode时,in_use = 0,当你的存储过程获得未使用的促销代码时,它选择第一个代码,其中in_use = 0,然后将其更新为1

答案 2 :(得分:0)

为什么不使用类似的东西,但是这样:

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

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

当用户获得促销代码时,您会在已使用的代码中插入一个值,并且提供给它们的促销代码将是促销代码与使用代码表中的ID的串联。

然后,您还可以对UserId |强制执行唯一约束使用代码表上的PromoId,以确保每个用户每个促销只有一个代码。

这样做的好处是可以保留所使用代码的记录,并降低需要批量插入的复杂性,这可能会意外地引入重复数据。它还具有无需锁定的优点......

答案 3 :(得分:0)

John P给了你一个很好的答案,但由于字符串的长度,我发现GU​​ID很难用作代金券代码。

查看问题how to generate a voucher code in c#?,当然还有我的回答:)虽然您要求SQL解决方案,但您可以调整其中的想法。

我还建议您不要删除已使用的代码;您如何确定客户提供的代码是由您的系统创建的?