首页 > 代码库 > 在数据库并发情况下避免插入重复数据的一个解决方法
在数据库并发情况下避免插入重复数据的一个解决方法
目前公司的项目中碰到一个情况:需要向一个数据表table1中插入记录,该表的结构类似于下面的定义:
列名 | 类型 | 是否允许为空 |
Id | int | no |
Area | string | no |
AreaIndex | int | no |
Name | string | no |
其中Name的值由Area和AreaIndex拼接而成,形式类似于“Area+AreaIndex”。对于相同的Area,AreaIndex从1开始计数,所以对于Area分别为“AA”,“BB”,“CC”的情况,Name的值类似下面这样:
AA001 AA002 BB001 AA003 BB002 CC001这种形式。
当插入新值的时候,需要判断数据表中该Area的最大AreaIndex,在此基础上加1做为新行的AreaIndex,同时拼接Name到数据库中。在数据库并发的情况下,会出现大量相同记录的情况。
目前想到的一个比较好的解决方法就是:
1,创建一个新表Table2来存储Area的最大AreaIndex,当插入新记录时,从新表中获取最大AreaIndex,加一作为新记录的AreaIndex,同时更新新表的最大AreaIndex。
2,创建一个新的存储过程来添加记录到表中。在该存储过程中,先查询Table2并锁定该表。获取插入记录对应Area的最大AreaIndex并更新Table2中的记录后,插入记录到Table1中。存储过程的代码类似下面这样:
BEGIN
BEGIN TRAN
DECLARE @MaxAreaIndex int
--查询并锁定Table2
SELECT @MaxAreaIndex = AreaIndex
FROM Table2 WITH (TABLOCKX)
WHERE Area= @Area
--获取到最大的AreaIndex后,更新Table2
IF @MaxAreaIndex IS NULL
BEGIN
--如果Table2中没有记录,则新增一条记录
SET @MaxAreaIndex = 1
INSERT INTO [Table2]
([MaxAreaIndex] ,[Area])
VALUES
(@MaxAreaIndex, @Area)
END
ELSE
BEGIN
SET @MaxAreaIndex = @MaxAreaIndex + 1
UPDATE Table2
SET MaxAreaIndex = @MaxAreaIndex
WHERE Area = @Area
END
INSERT INTO Table1
([Name] ,[Area] ,[AreaIndex])
VALUES
(@Area+ RTRIM(LTRIM(STR(@MaxAreaIndex))) ,@Area ,@MaxAreaIndex)
COMMIT TRAN
关键的代码在:
SELECT @MaxAreaIndex = AreaIndex
FROM Table2 WITH (TABLOCKX)
WHERE Area= @Area
当在存储过程中对表Table2进行加锁后,其他对该存储过程的调用就必须等待前一次执行完成并释放对表的锁定后才能继续执行。
在Area不是很多的情况下,效率没有大的影响。