我需要检查表中是否存在唯一(非主键)字段,如果不存在 - 请插入:
ID unqiueidentifier, --PK
name varchar(100),
otherID int --Unique
也许使用条件插入:
DECLARE @OtherID int
INSERT INTO TableA (OtherID)
SELECT @OtherID
WHERE NOT EXIST (SELECT * from TableA where OtherID = @OtherID)
SELECT MainID from TableA where OtherID = @OtherID
我需要哪种锁/提示来确保并发进程在检查和插入之间的时间内不插入相同的OtherID
(显然并发性仍然是一个问题,即使在单个语句中,例如上文)。
参考:http://kejser.org/race-condition-when-creating-unique-values/
参考:http://weblogs.sqlteam.com/dang/archive/2007/10/28/Conditional-INSERTUPDATE-Race-Condition.aspx
我在HOLDLOCK
/ UPDLOCK
找到了很多信息。我理解他们如何能够暂停选择或更新现有行。什么是没有意义的是他们如何锁定 INSERTING 新行而基本上只是锁定整个表 - 又名TABLOCK
。
如何锁定不存在的行?!或者UPDLOCK
只是锁定整个表格,因为你是SELECT *
?
如果它确实锁定整个表 - 这似乎可能非常具有破坏性并且不适合缩放?!也许我只是强制执行UNIQUE
约束并允许偶尔出现异常?
我在这里看不到任何好的选择?!
答案 0 :(得分:3)
这是upserts的一个很好的参考,但它也适用于这种情况:Sam Saffron upsert approach
要获取key range lock
(以便不锁定整个表格),您需要使用OtherId
作为关键字的索引,优先使用with (updlock, serializable)
(相当于with (updlock, holdlock)
)。
declare @OtherId int;
insert into TableA (OtherId)
select @OtherId
where not exists (
select OtherId
from TableA with (updlock, serializable)
where OtherId = @OtherId
)
select Mainid
from TableA
where OtherId = @OtherId;
参考:
答案 1 :(得分:2)
这里有两个选择。
悲观方法(期望最差)将使用可序列化隔离级别:SET TRANSACTION ISOLATION LEVEL SERIALIZABLE
,这将阻止并发事务在您查找时插入冲突数据(幻像行)并可能插入您的独特价值。这会降低并发性,因为其他事务在完成查找和插入时都必须等待。
乐观方法(希望最好)是简单地插入您的ID并捕获并处理异常(如果发生)。这提供了更高的并发性,但使代码更复杂。
选择哪种方法取决于您的性能要求和冲突的可能性;如果您认为冲突很少或需要最高的并发性,那么乐观的方法将更为可取。否则,您将不得不承受悲观方法的并发后果,以确保数据完整性。