CREATE TABLE dbo.SomeTable
(
ID int NOT NULL,
SomeText varchar(10) NOT NULL
) ON [PRIMARY]
GO
ALTER TABLE dbo.SomeTable
ADD CONSTRAINT PK_SomeTable
PRIMARY KEY CLUSTERED (ID)
WITH(STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF,
ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
GO
CREATE PROCEDURE [dbo].[InsertSomeText]
@ID int,
@SomeText varchar(10)
AS
BEGIN
BEGIN TRANSACTION
DELETE FROM SomeTable WHERE ID = @ID
INSERT INTO SomeTable (ID, SomeText)
VALUES (@ID, @SomeText)
COMMIT TRANSACTION
END
上述存储过程有时会抛出错误
违反PRIMARY KEY约束
同时使用相同的@ID
值进行调用。
删除/插入操作在事务内部,所以我认为这将是一个原子操作,但仍有一段时间它会抛出异常。
答案 0 :(得分:2)
实际上很少有避免竞争条件的万无一失的方法,除非你指定锁定级别,否则使用事务不是其中之一。不幸的是,我从未见过使用删除和插入管理的upsert,因此我无法找到任何文档或测试来显示竞争条件的发生方式和原因。
我建议只使用线程安全的MERGE WITH (HOLDLOCK)
,而不是修复一个有点奇怪的方法(删除/插入):
CREATE PROCEDURE [dbo].[InsertSomeText]
@ID int,
@SomeText varchar(10)
AS
BEGIN
MERGE dbo.SomeTable WITH (HOLDLOCK) AS t
USING (VALUES (@ID, @SomeText)) s (ID, SomeText)
ON s.ID = t.ID
WHEN MATCHED THEN
UPDATE SET SomeText = s.SomeText
WHEN NOT MATCHED THEN
INSERT (ID, SomeText) VALUES (s.ID, s.SomeText);
END;
答案 1 :(得分:0)
尝试将ISOLATION LEVEL更改为SERIALIZABLE(最具限制性)以避免任何并发问题。 详情请参考MSDN: https://msdn.microsoft.com/en-IN/library/ms173763.aspx