You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在C#和SQL Server生成唯一自定义RecordNo并确保并发下唯一性?

生成带年份的自增RecordNo并保障唯一性的最优方案

针对你需要生成ABC-YYXXXX格式编号、解决并发重复的需求,推荐优先在数据库端实现编号生成逻辑——这是保障并发安全最可靠的方式,以下是具体方案:

方案1:专用计数器表+存储过程(兼容所有SQL Server版本,推荐)

核心思路是用一张单独的表存储每年的编号最大值,通过带锁的事务获取并更新下一个编号,从根源上避免并发冲突。

步骤1:创建计数器表

CREATE TABLE RecordNoCounter (
    [Year] INT PRIMARY KEY,  -- 存储完整年份(如2024)
    CurrentMax INT NOT NULL DEFAULT 0  -- 当年已生成的最大编号后缀
);

步骤2:编写生成编号的存储过程

这个存储过程会在事务中锁定当前年份的记录,确保同一时间只有一个请求能获取下一个编号:

CREATE PROCEDURE GetNextRecordNo
    @RecordNo VARCHAR(10) OUTPUT
AS
BEGIN
    SET NOCOUNT ON;
    DECLARE @CurrentYear INT = YEAR(GETDATE());
    DECLARE @YearSuffix INT = @CurrentYear % 100;
    DECLARE @NextNum INT;

    BEGIN TRANSACTION;
    -- 使用UPDLOCK+HOLDLOCK锁定记录,防止并发读取导致重复
    SELECT @NextNum = CurrentMax + 1
    FROM RecordNoCounter WITH (UPDLOCK, HOLDLOCK)
    WHERE [Year] = @CurrentYear;

    -- 如果是当年第一条记录,初始化计数器
    IF @NextNum IS NULL
    BEGIN
        SET @NextNum = 1;
        INSERT INTO RecordNoCounter ([Year], CurrentMax)
        VALUES (@CurrentYear, @NextNum);
    END
    -- 否则更新计数器
    ELSE
    BEGIN
        UPDATE RecordNoCounter
        SET CurrentMax = @NextNum
        WHERE [Year] = @CurrentYear;
    END

    -- 格式化最终编号
    SET @RecordNo = 'ABC-' + RIGHT('0' + CAST(@YearSuffix AS VARCHAR(2)), 2) 
                  + RIGHT('0000' + CAST(@NextNum AS VARCHAR(4)), 4);

    COMMIT TRANSACTION;
END

步骤3:C#端调用存储过程

直接从数据库获取生成好的编号,再插入业务表:

public string GetNextRecordNo()
{
    string recordNo = string.Empty;
    var connectionString = "你的数据库连接字符串";
    
    using (var conn = new SqlConnection(connectionString))
    {
        conn.Open();
        using (var cmd = new SqlCommand("GetNextRecordNo", conn))
        {
            cmd.CommandType = CommandType.StoredProcedure;
            
            var outputParam = new SqlParameter("@RecordNo", SqlDbType.VarChar, 10)
            {
                Direction = ParameterDirection.Output
            };
            cmd.Parameters.Add(outputParam);
            
            cmd.ExecuteNonQuery();
            recordNo = outputParam.Value.ToString();
        }
    }
    return recordNo;
}

方案2:SQL Server序列(仅适用于2012及以上版本)

如果你的SQL Server版本在2012+,可以用序列实现自增,但需要额外处理跨年重置的问题:

创建序列

CREATE SEQUENCE RecordNoSeq
    START WITH 1
    INCREMENT BY 1
    MINVALUE 1
    MAXVALUE 9999
    NO CYCLE; -- 到达9999后停止生成,避免重复

每年重置序列

需要创建SQL Server代理作业,在每年1月1日执行以下语句重置序列起始值:

ALTER SEQUENCE RecordNoSeq RESTART WITH 1;

生成编号

在插入业务表时直接生成:

INSERT INTO YourBusinessTable (RecordNo, ...)
VALUES (
    'ABC-' + RIGHT('0' + CAST(YEAR(GETDATE())%100 AS VARCHAR(2)),2) 
    + RIGHT('0000' + CAST(NEXT VALUE FOR RecordNoSeq AS VARCHAR(4)),4),
    ...
);

唯一性保障的最后防线

无论使用哪种方案,都必须在业务表的RecordNo字段上创建唯一约束:

ALTER TABLE YourBusinessTable
ADD CONSTRAINT UQ_YourBusinessTable_RecordNo UNIQUE (RecordNo);

这样即使前端或存储过程逻辑出现异常,数据库会直接拒绝重复值插入,C#端可以捕获SqlException(错误号2601)并重试获取编号。

内容的提问来源于stack exchange,提问作者Frank Martin

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.03 20:25:17