sql-server - 是否可以明确识别 DML 命令是否是从存储过程发出的?

标签 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 的任何其他上下文。

任何人都可以建议一种完全可靠的方法,我们可以在触发器中使用它来仅在不由存储过程触发的情况下执行它们吗?

或者这(正如我怀疑的那样)不可能吗?

最佳答案

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

关于sql-server - 是否可以明确识别 DML 命令是否是从存储过程发出的?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/2834509/

相关文章:

sql-server - 奇怪的性能问题: Common Table Expressions in inline User-Defined Function

sql - 如何编写 SQL Server 存储过程以获得以下输出?

sql-server - SQL Server错误: Cannot drop the user because it does not exist or you do not have permission

SQL 过程在一行中返回结果

sql-server - 如何在 SQL Server 2008 中存储图像 blob 数据?

sql - 在 SQL Server 上的多个数据库中搜索列名

sql-server - DBCC SHRINKFILE 失败

sql-server - 如何在 SQL Server Management Studio 18 中调试或单步执行代码?

sql - 使用 Powershell 从 SQL Server 2008 R2 导出到固定宽度的文本文件

python - PyODBC 返回错误,但为什么呢?