我有一个SQLServer 2008数据库,其中有一个Tags表。标签只是一个id和一个名字。标签表的定义如下:
CREATE TABLE [dbo].[Tag](
[ID] [int] IDENTITY(1,1) NOT NULL,
[Name] [varchar](255) NOT NULL
CONSTRAINT [PK_Tag] PRIMARY KEY CLUSTERED
(
[ID] ASC
)WITH (PAD_INDEX = OFF,
STATISTICS_NORECOMPUTE = OFF,
IGNORE_DUP_KEY = OFF,
ALLOW_ROW_LOCKS = ON,
ALLOW_PAGE_LOCKS = ON)
)
名称也是唯一索引。此外,我有几个进程以非常快的速度向该表添加数据。这些进程使用如下所示的存储过程:
ALTER PROC [dbo].[lg_Tag_Insert]
@Name varchar(255)
AS
DECLARE @ID int
SET @ID = (select ID from Tag where Name=@Name )
if @ID is null
begin
INSERT Tag(Name)
VALUES (@Name)
RETURN SCOPE_IDENTITY()
end
else
begin
return @ID
end
我的问题是,除了作为并发数据库设计的新手之外,似乎有一种竞争条件导致我偶尔得到一个错误,我正在尝试将重复键(名称)输入到数据库中。错误是:
无法在对象'dbo.Tag'中插入具有唯一索引'IX_Tag_Name'的重复键行。
这是有道理的,我只是不确定如何解决这个问题。如果它代码我会知道如何锁定正确的区域。 SQLServer是一个完全不同的野兽。
第一个问题是对此'检查,然后更新模式'进行编码的正确方法是什么?看来我需要在检查期间获取排的锁,而不是共享锁,但我不清楚这样做的最佳方法。任何正确方向的帮助将不胜感激。提前谢谢。
答案 0 :(得分:1)
我更喜欢输出参数(所以我用这种方式编码),但这应该是最快的,表上点击次数最少:
ALTER PROC [dbo].[lg_Tag_Insert]
@Name varchar(255)
,@ID int OUTPUT
AS
BEGIN TRY
SET @ID=NULL
INSERT Tag (Name) VALUES (@Name)
SET @ID=SCOPE_IDENTITY()
END TRY
BEGIN CATCH
SELECT @ID=ID from Tag where Name=@Name
END CATCH
IF @ID IS NULL
BEGIN
RETURN 1
END
RETURN 0
GO
答案 1 :(得分:0)
正确的代码是:
事务隔离将确保事务序列化。
缓存标记客户端,因此当客户端已经知道它们存在时,您不会插入。性能损失将是最小的。
看起来你这样做,所以唯一的问题可能是你的事务隔离级别。
你能做的是:
答案 2 :(得分:0)