2010-05-14 49 views
2

我继承了SQL Server 2008数据库,调用应用程序可以通过存储过程访问该数据库。是否可以确定是否从存储过程发出DML命令?

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

对填充审计表进行性能测试表明,使用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 

这种设计是有缺陷的,因为它不会追踪DML语句在动态SQL执行更新,或者@@NESTLEVEL以上1提出的任何其他方面。

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

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

回答

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等。

+0

明智的做法是防守编程,并将您的代币“添加”到CONTEXT_INFO中已经设置的任何代码中?同样,因为它是基于连接的,不应该在程序结束时清除它们的标记吗?如果一个proc调用一个proc,你应该只添加/清除它是否已经存在?可能会变得棘手,但仍然,这听起来非常可靠。 – 2010-05-14 13:59:17

+1

@Philip Kelley,在上面的简单示例中,我通过执行'SET CONTEXT_INFO 0x0'来清除它,您可以在更改它之前存储它的值,然后将其设置回来。你也可以推/推动价值等等。可能性只是无尽的,这只是一个简单的例子,而不是一个完整的机制。 – 2010-05-14 14:05:47

+0

谢谢 - 这是一个很好的解决方案。 – 2010-05-15 08:55:29

相关问题