Asp.net 避免重复条目,而不是关键字段

Asp.net 避免重复条目,而不是关键字段,asp.net,sql,sql-server,Asp.net,Sql,Sql Server,我有一个表,它有两列 key -> Primary Key, auto incremented reqNumber -> This is a custom generated value reqNumber的结构为年/月/日/数,其中数是当天发出请求的次数。例如,如果今天提出了3个请求,昨天提出了2个请求,那么该表将 key reqNumber 1 2013/07/16/001 2 2013/07/16/002 3 2013/07/17/001

我有一个表,它有两列

key -> Primary Key, auto incremented
reqNumber -> This is a custom generated value
reqNumber的结构为年/月/日/数,其中数是当天发出请求的次数。例如,如果今天提出了3个请求,昨天提出了2个请求,那么该表将

key    reqNumber
1      2013/07/16/001
2      2013/07/16/002
3      2013/07/17/001
4      2013/07/17/002
5      2013/07/17/003
我遇到的问题是有时多个用户同时保存。执行保存的过程首先检查今天创建了多少个,然后再向其中添加1,因此上面的过程将看到今天创建了3个,下一个将是4个。然后再进行插入

但是如果两个用户同时点击save,他们都会得到4,这意味着当插入发生时,我会得到2013/07/17/004两次


在SQL或.Net中有没有避免这种情况的方法?锁是唯一会降低性能的方法吗?

首先,我会在选择和更新周围放置一个事务

其次,我只会使用乐观断开锁定进行更新(意思是
更新表,其中key=TheKey,reqNumber=TheValueYouSelectedToCount
),并确保更新了一行。如果您已更新0行,则表示其他人已对其进行了更新


通过这种方式,您不能“丢失”更新。首先,我将在选择和更新周围放置一个事务

其次,我只会使用乐观断开锁定进行更新(意思是
更新表,其中key=TheKey,reqNumber=TheValueYouSelectedToCount
),并确保更新了一行。如果您已更新0行,则表示其他人已对其进行了更新


通过这种方式,您不能“丢失”更新

如果您希望应用程序能够扩展,您需要创建一个新表:

CREATE TABLE DayCounter (
    LastReset DateTime NOT NULL,
    NextValue INT NOT NULL
)
然后,您需要一个
函数
,以便可以随意获取下一个可用值:

CREATE FUNCTION NextCounter()
    RETURNS CHAR(3)
AS
BEGIN
    BEGIN TRANSACTION
        DECLARE @LastReset DATETIME
        DECLARE @NextValue INT

        SELECT @LastReset = LastReset FROM DayCounter
        IF (DATEPART(DAY, DATEADD(DAY, 1, @LastReset)) = DATEPART(DAY, GETDATE())
            UPDATE DayCounter SET LastReset = GETDATE(), NextValue = 1

        SELECT @NextValue = NextValue FROM DayCounter
        UPDATE DayCounter SET NextValue = NextValue + 1
    COMMIT TRANSACTION

    RETURN RIGHT('000' + CAST(@NextValue AS CHAR), 3)
END

因此,现在您可以构建一个update语句,将您的值串在一起,但它不会发生冲突,并且它每天管理重置。

如果您希望应用程序能够扩展,您需要创建一个新表:

CREATE TABLE DayCounter (
    LastReset DateTime NOT NULL,
    NextValue INT NOT NULL
)
然后,您需要一个
函数
,以便可以随意获取下一个可用值:

CREATE FUNCTION NextCounter()
    RETURNS CHAR(3)
AS
BEGIN
    BEGIN TRANSACTION
        DECLARE @LastReset DATETIME
        DECLARE @NextValue INT

        SELECT @LastReset = LastReset FROM DayCounter
        IF (DATEPART(DAY, DATEADD(DAY, 1, @LastReset)) = DATEPART(DAY, GETDATE())
            UPDATE DayCounter SET LastReset = GETDATE(), NextValue = 1

        SELECT @NextValue = NextValue FROM DayCounter
        UPDATE DayCounter SET NextValue = NextValue + 1
    COMMIT TRANSACTION

    RETURN RIGHT('000' + CAST(@NextValue AS CHAR), 3)
END

因此,现在您可以构建一个update语句,将您的值串在一起,但它不会发生冲突,并且它每天管理重置。

如果reqNumber必须是唯一的,那么数据库应该在列上有一个
唯一的
约束。添加此选项将提供另一个选项:在不使用锁的情况下重试一次,如果插入失败,请使用锁重试。通过高读/写比率和低冲突频率,这可以比每次事务解决方案执行得更好。

如果reqNumber必须是唯一的,那么数据库应该在列上有一个
唯一的
约束。添加此选项将提供另一个选项:在不使用锁的情况下重试一次,如果插入失败,请使用锁重试。通过高读/写比率和低冲突频率,这比每次事务解决方案的性能要好得多。

您应该在同一事务中执行
选择
更新
,并锁定表,直到
更新完成。这样,一次只能有一个用户访问表,其他用户等待事务完成。您应该在同一事务中执行
选择
更新
,并锁定表,直到
更新
完成。这样,一次只有一个用户可以访问表,而其他用户则等待事务完成。使用事务是否可以避免冲突?如果没有,那么如果两个用户同时点击该函数,他们是否有可能得到相同的计数器?@Paritosh,通过输入事务,它锁定表并防止脏读发生。因此,这消除了发生冲突的机会。没有充分的理由将业务逻辑移动到数据库中。所有的逻辑应该放在一起layer@MichaelPerrenoud,1:函数不会带来任何附加值,2:数据库层最难扩展,因此逻辑应该留在业务层,或者基本上在数据库层以外的任何其他层,最后:您不能对sql函数进行单元测试,因此这应该是最后的手段,特别是为了解决业务需求(这里我考虑每天创建一个新条目,一个业务需求)使用事务使其免受冲突吗?如果没有,那么如果两个用户同时点击该函数,他们是否有可能得到相同的计数器?@Paritosh,通过输入事务,它锁定表并防止脏读发生。因此,这消除了发生冲突的机会。没有充分的理由将业务逻辑移动到数据库中。所有的逻辑应该放在一起layer@MichaelPerrenoud,1:函数不会带来任何附加值,2:数据库层最难扩展,因此逻辑应该留在业务层,或者基本上在数据库层以外的任何其他层,最后:您不能对sql函数进行单元测试,因此这应该是最后的手段,特别是为了解决业务需求(这里我考虑每天创建一个新条目一个业务需求)