最近发布的脚本,有那种防止重复插入数据(包括存在时更新,不存在是插入的处理,判断的方向可能与下面的示例相反)
使用类似下面的 SQL
declare @id int, @value int
if not exists( select * from tb where id = @id )
insert tb values( @id, @value );
--else
-- update tb set value = @value where id = @id;
或者是使用这种单句的
declare @id int, @value int
insert tb select @id, @value
where not exists( select * from tb where id = @id )
--if @@rowcount = 0
-- update tb set value = @value where id = @id;
或者是用 MERGE 的
declare @id int, @value int
merge tb
using( values(@id, @value) ) data( id, value)
on data.id = tb.id
when not matched by target then insert values( id, value )
--when matched then update set value = data.value
;
这几种方法均已说明是无法防止插入重复数据的,只是发生的概率高低有一定的区别而已
针对这种处理需求,我专门做了一些测试,有效的处理方法如下,大家在处理这类问题时,请参考此方法进行:
declare @id int, @value int
begin tran -- 使查询的更新锁保留到事务结束
if not exists( select * from tb with(holdlock, updlock) where id = @id )
insert tb values( @id, @value );
--else
-- update tb set value = @value where id = @id;
commit tran
推荐使用这种单句的处理方式,不用显式的事务控制,避免考虑与其他会显式使用事务的地方的交互性
declare @id int, @value int
insert tb select @id, @value
where not exists( select * from tb with(holdlock, updlock) where id = @id )
--if @@rowcount = 0
-- update tb set value = @value where id = @id;
不推荐这种方式,容易出来死锁
declare @id int, @value int
merge tb with(holdlock, updlock)
using( values(@id, @value) ) data( id, value)
on data.id = tb.id
when not matched by target then insert values( id, value )
--when matched then update set value = data.value
另外,where 这个判断是否重复的条件列,需要创建索引,否则因为锁的关系,数据处理的效率会极低
如果表中本来就没有重复数据,应该直接建立唯一索引(UNIQUE INDEX),这个对于查询优化有很大的帮助