【问题标题】:How can I track the flow of a change caused by a stored procedure in Microsoft SQL Server?如何跟踪由 Microsoft SQL Server 中的存储过程引起的更改流?
【发布时间】:2014-06-16 00:51:06
【问题描述】:

附下表:

CREATE TABLE [Test]
(
    [Id] BIGINT IDENTITY NOT NULL,
    [Name] CHARACTER VARYING(255) NOT NULL,
    [Description] CHARACTER VARYING(MAX) NOT NULL,

    PRIMARY KEY([Id])
);

我使用存储过程控制新记录的创建:

CREATE PROCEDURE [Test_Create]
    @SessionId BINARY(32),
    @Name CHARACTER VARYING(255),
    @Description CHARACTER VARYING(MAX)
AS
BEGIN
    BEGIN TRANSACTION;
        BEGIN TRY
            INSERT INTO [Test] ([Name], [Description])
            VALUES (@Name, @Description);
        END TRY
        BEGIN CATCH
            ROLLBACK TRANSACTION;
            THROW;
        END CATCH
    COMMIT TRANSACTION;
END

我有一个触发器,它只记录该表的前后值,用于所有更改。当我的存储过程被调用时,这个触发器被触发并记录到另一个表中。但是,我的触发器显然不能使用 @SessionId 变量来记录哪个网站用户会话导致存储过程被运行在 Web 服务器上的 SQL 客户端调用。我不想使用SET CONTEXT_INFO,因为它在连接池和并行执行计划中不稳定。

也就是说,我认为我可以将传递给存储过程的@SessionId@@SPID 值关联起来,但这对于连接池来说还是有点棘手。据我所知,它不会起作用。

假设我有一个唯一的会话号传递给我的存储过程,我如何跟踪我网站上的哪个用户对我的任何表造成了特定的更改?

如果他们调用存储过程,我需要知道调用的存储过程以及它如何更改表(之前和之后的值)。

是否有替代方法可以检索以下信息将它们链接在一起以形成审计跟踪?

  • 导致调用存储过程的网站用户/会话
  • 调用的存储过程及其参数
  • 生成的数据查询
  • 查询的效果(数据前后)

【问题讨论】:

  • 您不能只将日志添加到Test_Create 本身吗?
  • @David 如果我这样做,那么我需要为每个插入、更新或删除写入日志条目,然后还要弄清楚旧值是什么。这将添加许多可以通过触发器解决的额外代码。我认为最好弄清楚如何将存储过程上下文中的@SessionId 与触发器上下文中的@@SPID 关联起来。

标签: sql-server tsql audit


【解决方案1】:

我建议您修改表结构,以便将 @SessionId 值存储在表中以供创建和修改。

我有一个 ASP.Net 应用程序,它还必须记录所有更改以用于审计目的,并且需要审计的表都有

CreatedDateTime DateTime NOT NULL
CreatedBy nvarchar(50) NOT NULL
ModifiedDateTime DateTime NULL
ModifiedBy nvarchar(50) NULL

用户使用 Windows 身份验证登录,我们将用户的 domain\username 传递给所有存储的 proc,以便它可以存储在 CreatedByModifiedBy 列中。 CreatedDateTime/ModifiedDateTime 值由存储的过程设置。

我们的更改日志审核由 CLR 触发器完成,该触发器仅提取新记录上的 ModifiedBy 值,并将其记录为进行更改的人。

使用您的 @SessionId 值会像我想的那样工作。

【讨论】:

  • 当UserA修改记录,然后UserB修改记录时会发生什么?您的设计不支持审计跟踪,仅支持“最新”方法。
  • 对不起,我不清楚。虽然实际表只显示最后一个更改记录的人(这对于显示目的很有用)审计触发器记录到一个单独的审计数据库/表中通过记录用户/更改日期/表名称/字段名称而更改的每个字段/旧值 /新值。因此,如果用户在一次更新中更改了 20 个字段,那么日志表将有 20 行显示发生了什么。
  • 在我上一条评论的基础上,这在过去 7 年中为我们提供了良好的服务,并允许我们在必要时恢复更改,还可以进行检测工作以解决错误或验证/否认用户的声称“我没有那样做!”。唯一的缺点是我们的审计表现在让真实的数据库相形见绌。
【解决方案2】:

我的第一个问题是“为什么”?

根据您的潜在答案的几个选项:

如果您只是好奇并想对某个问题进行一些调查,您可以使用 SQL Server Profiler 或数据库引擎跟踪捕获和跟踪重放以及正确的日志记录事件。这将准确地告诉您调用了哪些过程,由用户调用的原因以及使用正确的参数。

如果您想立即将此信息用于管理报告,我建议您将其传递到存储过程中并将其存储在一个表中,以便您可以根据需要进行查询。

如果您想在将来的某个时间使用此信息,您可以在您的数据访问层周围启用日志记录,以便将查询前后的信息存储在日志文件中,或者如果您确实需要再次存储在数据库中到。

【讨论】:

  • 目的是为了安全审计和恢复。我们需要知道我们网站上的哪个用户更改了一些数据。当恶意用户删除大量内容时,我们正在尝试构建恢复功能,我们需要暂停用户,然后仅回滚他们所做的事情,并在交火中捕获任何合法的编辑/添加。我们希望在数据库中尽可能多地控制它的原因是因为我们有这个系统的分散部分;许多受信任的客户端连接到 SQL Server 实例并代表其用户群进行更改。
  • 在这种情况下,我会选择上面的中间选项。为可用于回滚更改的表创建审计表。有很多方法可以做到这一点。一种可能是使用审计表来镜像表结构,但这非常脆弱。您可以创建一个更通用的结构并在通过触发器时更新它,例如属性(ID、名称、描述)、PropertyChanges(ID、PropertyIdChanged、UserId、更改日期、ValueBefore、ValueAfter)。这有利于审计,但需要仔细考虑才能使用它来恢复数据。可能还有其他解决方案!
  • 所以,我尝试了触发方法并添加了属性更改等等。我什至让它回滚。我发现触发器的困难是我无法将@SessionId 传达给触发器,除了我的问题中提到的SET CONTEXT_INFO
  • 不要使用触发器,而是在存储过程中进行插入,然后您可以将 sessionid 存储在您的审计表中,而不会弄脏您的主数据表。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-03-31
  • 2011-07-01
  • 1970-01-01
  • 1970-01-01
  • 2011-01-15
相关资源
最近更新 更多