SQL Server事务隔离级别与自定义年度格式JV_ID存储过程问题
关于SQL Server生成年度格式记账凭证ID的问题解答
问题1:设置SERIALIZABLE隔离级别能否避免获取最大ID时出现重复值?
是的,SERIALIZABLE是SQL Server中最严格的隔离级别,它会在查询的数据集上添加范围锁,阻止其他事务插入符合当前查询条件的新行。在按年度生成凭证ID的场景下,这个隔离级别会避免并发事务同时查询同一年度的最大ID并生成重复的序号,从根本上解决ID重复问题。不过要注意,强隔离会带来一定性能开销,高并发场景下需要评估对系统的影响。
问题2:针对给定表结构编写存储过程
以下是适配你表结构的存储过程,可生成YYYY00001格式的年度连续JV_ID:
CREATE PROCEDURE usp_CreateNewJV @JV_DATE DateTime, @JV_TOT Money, @JV_USER INT, @New_JV_ID NVARCHAR(25) OUTPUT AS BEGIN SET NOCOUNT ON; SET TRANSACTION ISOLATION LEVEL SERIALIZABLE; BEGIN TRANSACTION; BEGIN TRY DECLARE @JV_YEAR NVARCHAR(4); DECLARE @Max_Suffix INT; DECLARE @New_Suffix NVARCHAR(5); DECLARE @NewRow TABLE (JV_ID NVARCHAR(25)); -- 提取凭证所属年度 SET @JV_YEAR = CAST(YEAR(@JV_DATE) AS NVARCHAR(4)); -- 获取当前年度已有的最大序号后缀 SELECT @Max_Suffix = CASE WHEN MAX(CAST(RIGHT(JV_ID, 5) AS INT)) IS NULL THEN 0 ELSE MAX(CAST(RIGHT(JV_ID, 5) AS INT)) END FROM GL记账凭证表 WHERE JV_YEAR = @JV_YEAR; -- 生成5位补零的新序号 SET @New_Suffix = RIGHT('0000' + CAST(@Max_Suffix + 1 AS NVARCHAR(5)), 5); -- 拼接成完整JV_ID SET @New_JV_ID = @JV_YEAR + @New_Suffix; -- 插入新凭证记录 INSERT INTO GL记账凭证表 (JV_ID, JV_YEAR, JV_DATE, JV_TOT, JV_USER) VALUES (@New_JV_ID, @JV_YEAR, @JV_DATE, @JV_TOT, @JV_USER) OUTPUT INSERTED.JV_ID INTO @NewRow; -- 同步输出的ID(确保和生成值一致) SELECT @New_JV_ID = JV_ID FROM @NewRow; COMMIT TRANSACTION; END TRY BEGIN CATCH ROLLBACK TRANSACTION; -- 抛出错误,让调用方处理 THROW; END CATCH END
关键逻辑说明
- 用
TRY/CATCH包裹事务,确保异常发生时回滚,避免脏数据 - 提取JV_ID的后5位转换为整数计算最大值,避免字符串排序的逻辑错误(比如
202110000字符串排序比20219999靠前,但实际序号更大) - 通过补零操作保证序号始终为5位,符合
YYYY00001的格式要求 - 输出参数
@New_JV_ID返回生成的新凭证ID,方便调用方直接使用
内容的提问来源于stack exchange,提问作者A.J
相关产品推荐
相关产品推荐

