是否有可能明确地确定是否从存储过程发出DML命令?

时间:2010-05-14 13:36:19

标签: sql-server sql-server-2008

我继承了一个SQL Server 2008数据库,调用应用程序可以通过存储过程访问该数据库。

数据库中的每个表都有一个影子审核表,其中记录了插入/更新/删除操作。

填充审计表的性能测试表明,使用OUTPUT子句插入审计记录比使用触发器快20%左右,因此已在存储过程中实现。

但是,由于此设计无法通过直接针对表发出的DML语句跟踪直接对表进行的更改,因此还实现了使用@@NESTLEVEL的值来确定是否运行触发器的触发器(假设所有通过存储过程运行的DML都有@@NESTLEVEL> 1)。 即触发器代码的主体看起来像:

IF  @@NESTLEVEL =   1   -- implies call is direct sql so generate history from here
    BEGIN
... insert into audit table

此设计存在缺陷,因为它不会跟踪在动态SQL中执行DML语句的更新,也不会跟踪@@NESTLEVEL高于1的任何其他上下文。

任何人都可以建议一个完全可靠的方法,我们可以在触发器中使用它们,只有在不是由存储过程触发时才能执行它们吗?

或者这(我怀疑)不可能?

2 个答案:

答案 0 :(得分:4)

使用CONTEXT_INFO (Transact-SQL)。在该过程中设置一个值以警告触发器不记录任何内容:

--in the procedure doing the insert/update/delete

DECLARE @CONTEXT_INFO  varbinary(128)
SET @CONTEXT_INFO =cast('SkipTrigger=Y'+REPLICATE(' ',128) as varbinary(128))
SET CONTEXT_INFO @CONTEXT_INFO

--do insert/update/delete that will fire the trigger

SET CONTEXT_INFO 0x0 

在触发器中检查CONTEXT_INFO并确定是否需要执行任何操作:

--here is the portion of the trigger to retrieve the value:

IF CAST(CONTEXT_INFO() AS VARCHAR(128))='SkipTrigger=Y'
BEGIN
    --log your data here
END

对于刚刚进行流氓插入/更新/删除的任何人,他们都不会设置CONTEXT_INFO,触发器将记录更改。如果您认为流氓代码也会尝试使用CONTEXT_INFO,您可能会对您在CONTEXT_INFO中输入的值感兴趣,例如表名或@@ SPID等。

答案 1 :(得分:0)

我不这么认为。有一个longstanding Connect item可以访问调用堆栈