我自己也遇到过这个问题(或者更确切地说,一个类似的问题),因此复活了......
我最终的方法是简单地禁止更新 PK 字段,因为它会破坏触发器。谢天谢地,我没有支持更新主键列的业务案例(无论如何,这些都是代理 ID),所以我可以侥幸逃脱。
SQL Server 提供了UPDATE
在触发器中使用的函数来检查这种边缘情况:
CREATE TRIGGER your_trigger
ON your_table
INSTEAD OF UPDATE
AS BEGIN
IF UPDATE(pk1) BEGIN
ROLLBACK
DECLARE @proc SYSNAME, @table SYSNAME
SELECT TOP 1
@proc = OBJECT_NAME(@@PROCID)
,@table = OBJECT_NAME(parent_id)
FROM sys.triggers
WHERE object_id = @@PROCID
RAISERROR ('Trigger %s prevents UPDATE of table %s due to locked primary key', 16, -1, @proc, @table) WITH NOWAIT
END
ELSE UPDATE t SET
col1 = i.col1
,col2 = i.col2
,col3 = i.col3
FROM your_table t
INNER JOIN inserted i ON t.pk1 = i.pk1
END
GO
(请注意,以上内容未经测试,可能包含关于XACT_STATE
或的各种问题TRIGGER_NESTLEVEL
- 它只是为了展示原理)
不过,它有点混乱,所以我肯定会考虑为此生成代码,以在开发期间处理对表的更改(甚至可能由 CREATE/ALTER 表上的 DDL 触发器完成)。
如果您有一个复合主键,您可以使用IF UPDATE(pk1) OR UPDATE(pk2)...
该函数或对该函数进行一些按位工作COLUMNS_UPDATED
,这将为您提供一个基于列序号的位掩码(但我不打算在这里介绍 - 请参阅 MSDN/BOL)。
另一个(更简单的)选项是 to DENY UPDATE ON your_table(pk) TO public
,但请记住sysadmins
(并且可能dbo
)的任何成员都不会尊重这一点。