好的,所以我从其他帖子/网站上了解到,以下样式的查询将以原子方式执行:
INSERT INTO Foo(field1, field2)
SELECT @field1, @field2
WHERE NOT EXISTS (SELECT * FROM Foo
WHERE Field1 = @field1
OR Field2 = @field2)
这将用于满足field1和field2都是唯一的设计要求。到目前为止,一切都很好,花花公子。
但是,如果这样的记录已经存在(查询插入0行),如果我想知道WHICH字段已经有一个具有相同值的记录。 (即,以下哪项陈述属实?
您需要知道哪个字段有问题才能显示错误消息,告知用户需要将哪个字段更改为唯一值。我总是可以检查第二个查询,但那不是原子的。尝试插入和第二个查询之间的数据可能已更改,以确定哪个字段出错,并且错误消息可能无法反映数据库的实际状态。
有关如何处理此案的任何想法?
答案 0 :(得分:0)
假设:
您不关心匹配NULL。
set transaction isolation level serializable;
begin transaction;
begin try
insert into Foo ( Field1, Field2 ) values ( @Field1, @Field2 );
end try
begin catch
-- Really ought to confirm that the error was the expected one here.
select
@Field1Match = case when Field1 = @Field1 then Cast( 1 as Bit ) else Cast( 0 as Bit ) end,
@Field2Match = case when Field2 = @Field2 then Cast( 1 as Bit ) else Cast( 0 as Bit ) end
from Foo
where Field1 = @Field1 or Field2 = @Field2;
end catch;
commit transaction;
答案 1 :(得分:0)
根据我的研究(here& here),除非您非常有信心超过95%的插入成功,否则我会使用相反的方法作为HABO。借用相同的假设,你不关心匹配NULL:
DECLARE @Col1Match VARCHAR(20), @Col2Match VARCHAR(20), @msg NVARCHAR(4000);
SET ANSI_WARNINGS OFF;
BEGIN TRANSACTION;
SELECT @Col1Match = MAX(CASE Field1 WHEN @Field1 THEN 'Field1 exists:' END),
@Col2Match = MAX(CASE Field2 WHEN @Field2 THEN 'Field2 exists:' END)
FROM dbo.Foo WITH (HOLDLOCK)
WHERE Field1 = @Field1 OR Field2 = @Field2;
IF @Col1Match IS NULL AND @Col2Match IS NULL
BEGIN
INSERT dbo.Foo(Field1, Field2) SELECT @Field1, @Field2;
END
ELSE
BEGIN
SET @msg = LTRIM(COALESCE(' ' + @Col1Match + ' (' + @Field1 + ')', '')
+ COALESCE(' ' + @Col2Match + ' (' + @Field2 + ')', ''));
END
COMMIT TRANSACTION;
IF @msg IS NOT NULL
BEGIN
RAISERROR(@msg, 11, 1);
END