我有两个表定义为
CREATE TABLE [dbo].[Foo](
FOO_GUID uniqueidentifier NOT NULL PRIMARY KEY CLUSTERED,
SECOND_COLUMN nvarchar(10) NOT NULL,
THIRD_COLUMN nvarchar(10) NOT NULL)
CREATE TABLE [dbo].[FooChanged](
[FooGuid] uniqueidentifier NOT NULL PRIMARY KEY CLUSTERED,
[IsNewRecord] bit NOT NULL)
触发器
CREATE TRIGGER dbo.[trg_FooChanged]
ON [dbo].[Foo]
AFTER INSERT,UPDATE
NOT FOR REPLICATION
AS
BEGIN
SET NOCOUNT ON;
DECLARE @isNewRecord as bit;
IF EXISTS(SELECT * FROM DELETED)
BEGIN
--We only want to make a record if the guid or one other column in Foo changed.
IF NOT(UPDATE(FOO_GUID) OR UPDATE(SECOND_COLUMN))
RETURN;
SET @isNewRecord = 0
END
ELSE
SET @isNewRecord = 1;
insert into FooChanged(FooGuid, IsNewRecord) SELECT FOO_GUID, @isNewRecord from INSERTED
END
以下简单测试脚本在最后一批具有主键约束违规(如预期)
的情况下失败INSERT INTO [dbo].[Foo] ([FOO_GUID],[SECOND_COLUMN],[THIRD_COLUMN]) VALUES (cast(0x1 as uniqueidentifier), '1', '1')
GO
INSERT INTO [dbo].[Foo]([FOO_GUID],[SECOND_COLUMN],[THIRD_COLUMN]) VALUES (cast(0x2 as uniqueidentifier), '2', '2')
GO
UPDATE Foo SET THIRD_COLUMN = '1a' WHERE FOO_GUID = cast(0x1 as uniqueidentifier)
GO
UPDATE Foo SET SECOND_COLUMN = '1a' WHERE FOO_GUID = cast(0x1 as uniqueidentifier)
GO
我的问题是,"适当的"没有将该错误传播给用户的方法,并且不会弄乱用户可能拥有的任何当前事务(可能具有XACT_ABORT ON
之类的设置。)
我看到的两个选项是在插入之前检查
declare @guid uniqueidentifier
select @guid = FOO_GUID from INSERTED
BEGIN TRANSACTION
if NOT EXISTS(select * from FooChanged WITH (UPDLOCK, HOLDLOCK) where FooGuid = @guid)
insert into FooChanged(FooGuid, IsNewRecord) SELECT @guid, @isNewRecord from INSERTED
COMMIT TRANSACTION
但我需要锁定表格以防止任何竞争条件,我认为这会导致繁忙的桌子出现性能问题
或捕获TRY / CATCH中的错误
BEGIN TRY
insert into FooChanged(FooGuid, IsNewRecord) SELECT @guid, @isNewRecord from INSERTED
END TRY
BEGIN CATCH
DECLARE @ErrorMessage NVARCHAR(4000);
DECLARE @ErrorSeverity INT;
DECLARE @ErrorState INT;
SELECT
@ErrorMessage = ERROR_MESSAGE(),
@ErrorSeverity = ERROR_SEVERITY(),
@ErrorState = ERROR_STATE();
if(ERROR_NUMBER() != 2627)
RAISERROR (@ErrorMessage, -- Message text.
@ErrorSeverity, -- Severity.
@ErrorState -- State.
);
END CATCH
但是我担心在XACT_ABORT事务中运行的代码会让它XACT_STATE
被删除。
使用的正确方法是什么?
答案 0 :(得分:3)
使用的正确方法是什么?
检查是否存在,然后相应地插入或更新。
错误处理非常昂贵 - 一旦发生错误,就无法返回并做一些与众不同的事情。
修改强>
老实说,我可能错了#34;昂贵的"部分(并没有任何证据支持它),因为我不是SQL专家,但不能回头的原则适用。
Here's来自Aaron Bertrand的一篇文章,他是一个比我更好的SQL来源。乍一看似乎表明这两种方法都没有明显优于其他表现方式。