在sql-server中实现审计跟踪的最佳方法?

时间:2013-07-09 11:03:49

标签: sql-server

我不知道这些要求是否标准,但我想知道是否有解决办法可以做到以下几点:

  • 对于指定的一组表,在相关表的审计版本中更改记录之前,请保留该记录的副本。

我宁愿不必为每个表编写代码。我想知道是否有一个可以安装在SQL Server之上的解决方案,它会为你做这个吗?

8 个答案:

答案 0 :(得分:35)

有很多方法可以做到这一点;它取决于您使用的SQL Server版本。

这里有一些

答案 1 :(得分:7)

我创建触发器,以XML方式执行此操作,我们可以将所有表记录到同一个表中,使其更灵活

CREATE TABLE [dbo].[AuditAll] (
    AuditId    int           NOT NULL IDENTITY(1,1),
    [DateTime] datetime      NOT NULL,
    TableName  nvarchar(255) NOT NULL,
    AuditEntry xml           NULL,

    CONSTRAINT [PK_AuditAll] PRIMARY KEY CLUSTERED ( AuditId ASC )
)

我只需要'旧'值,所以我只存储删除的表,无论如何都可以在表中看到插入的表。

CREATE TRIGGER AuditSimple 
    ON Simple
    AFTER INSERT,DELETE,UPDATE
AS 
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;

IF (SELECT COUNT(*) FROM deleted) > 0 
begin
    Declare @AuditMessage XML
    --set valut to all xml from deleted table
    set @AuditMessage = (select * from deleted for xml auto) 

    insert into AuditAll( DateTime, TableName, AuditEntry ) 
        values ( GetDate(), 'Simple', @AuditMessage )
end

END
GO

我想这可以很容易地在sp_foreach中调用,为数据库中的每个表创建它,但我们现在不需要它,只记得更改你的表名

欢呼声

答案 2 :(得分:5)

您可以尝试基于第三方点击触发器的解决方案,例如ApexSQL Audit - SQL Server数据库的审核工具,它捕获数据库中发生的数据更改,包括有关谁制作的信息更改,对象受影响的时间,以及用于进行更改的SQL登录,应用程序和主机的信息。它将所有捕获的信息存储在中央存储库中,并以打印友好格式导出它们

免责声明:我在ApexSQL担任产品支持工程师

答案 3 :(得分:2)

由于这是许多企业应用程序中非常普遍的要求,因此以下解决方案基于我在博客上撰写的this article

数据库表

假设我们有一个Book表,其审核日志信息必须存储在BookAuditLog表中,如以下类图所示:

SQL Server audit logging using triggers

BookAuditLog表的创建方式如下:

CREATE TABLE BookAuditLog (
    BookId bigint NOT NULL,
    OldRowData nvarchar(1000) CHECK(ISJSON(OldRowData) = 1),
    NewRowData nvarchar(1000) CHECK(ISJSON(NewRowData) = 1),
    DmlType varchar(10) NOT NULL CHECK (DmlType IN ('INSERT', 'UPDATE', 'DELETE')),
    DmlTimestamp datetime NOT NULL,
    DmlCreatedBy varchar(255) NOT NULL,
    TrxTimestamp datetime NOT NULL,
    PRIMARY KEY (BookId, DmlType, DmlTimestamp)
) 

BookAuditLog表列存储以下数据:

  • BookId列存储为此日志事件创建的关联的Book行的标识符。
  • OldRowData在执行INSERT,UPDATE或DELETE语句之前存储Book记录状态的JSON表示形式。
  • 在执行INSERT,UPDATE或DELETE语句后,NewRowData存储Book记录状态的JSON表示形式。
  • DmlType是一个枚举列,存储创建,更新或删除给定Book行的DML语句类型。
  • DmlTimestamp存储DML语句执行时间戳。
  • DmlCreatedBy存储发出INSERT,UPDATE或DELETE DML语句的用户。
  • TrxTimestamp存储更改了Book记录的事务的时间戳。

INSERT,UPDATE和DELETE触发器

要捕获INSERT,UPDATE和DELETE DML语句,我们需要创建三个数据库触发器,这些触发器将在BookAuditLog表中插入记录。

要拦截Book表上的INSERT语句,我们将创建TR_Book_Insert_AuditLog触发器:

CREATE TRIGGER TR_Book_Insert_AuditLog ON Book
FOR INSERT AS 
BEGIN
    DECLARE @loggedUser varchar(255)
    SELECT @loggedUser = CAST(SESSION_CONTEXT(N'loggedUser') AS varchar(255))
    
    DECLARE @transactionTimestamp datetime = SYSUTCdatetime()
    
    INSERT INTO BookAuditLog (
        BookId,
        OldRowData,
        NewRowData,
        DmlType,
        DmlTimestamp,
        DmlCreatedBy,
        TrxTimestamp
    )
    VALUES(
        (SELECT id FROM Inserted),
        null,
        (SELECT * FROM Inserted FOR JSON PATH, WITHOUT_ARRAY_WRAPPER),
        'INSERT',
        CURRENT_TIMESTAMP,
        @loggedUser,
        @transactionTimestamp
    );
END

要捕获Book记录上的UPDATE语句,我们将创建以下TR_Book_Update_AuditLog触发器:

CREATE TRIGGER TR_Book_Update_AuditLog ON Book
FOR UPDATE AS 
BEGIN
    DECLARE @loggedUser varchar(255)
    SELECT @loggedUser = CAST(SESSION_CONTEXT(N'loggedUser') AS varchar(255))
    
    DECLARE @transactionTimestamp datetime = SYSUTCdatetime()
    
    INSERT INTO BookAuditLog (
        BookId,
        OldRowData,
        NewRowData,
        DmlType,
        DmlTimestamp,
        DmlCreatedBy,
        TrxTimestamp
    )
    VALUES(
        (SELECT id FROM Inserted),
        (SELECT * FROM Deleted FOR JSON PATH, WITHOUT_ARRAY_WRAPPER),
        (SELECT * FROM Inserted FOR JSON PATH, WITHOUT_ARRAY_WRAPPER),
        'UPDATE',
        CURRENT_TIMESTAMP,
        @loggedUser,
        @transactionTimestamp
    );
END

要拦截Book表行上的DELETE语句,我们将创建以下TR_Book_Delete_AuditLog触发器:

CREATE TRIGGER TR_Book_Delete_AuditLog ON Book
FOR DELETE AS 
BEGIN
    DECLARE @loggedUser varchar(255)
    SELECT @loggedUser = CAST(SESSION_CONTEXT(N'loggedUser') AS varchar(255))
    
    DECLARE @transactionTimestamp datetime = SYSUTCdatetime()
    
    INSERT INTO BookAuditLog (
        BookId,
        OldRowData,
        NewRowData,
        DmlType,
        DmlTimestamp,
        DmlCreatedBy,
        TrxTimestamp
    )
    VALUES(
        (SELECT id FROM Deleted),
        (SELECT * FROM Deleted FOR JSON PATH, WITHOUT_ARRAY_WRAPPER),
        null,
        'DELETE',
        CURRENT_TIMESTAMP,
        @loggedUser,
        @transactionTimestamp
    );
END

演示时间

Book表上执行INSERT语句时:

INSERT INTO Book (
    Author, 
    PriceInCents, 
    Publisher, 
    Title, 
    Id
)
VALUES (
    'Vlad Mihalcea', 
    3990, 
    'Amazon', 
    'High-Performance Java Persistence 1st edition', 
    1
)

我们可以看到BookAuditLog中插入了一条记录,该记录捕获了刚刚在Book表上执行的INSERT语句:

| BookId | OldRowData | NewRowData                                                                                                                         | DmlType | DmlTimestamp            | DmlCreatedBy  | TrxTimestamp            |
|--------|------------|------------------------------------------------------------------------------------------------------------------------------------|---------|-------------------------|---------------|-------------------------|
| 1      |            | {"Id":1,"Author":"Vlad Mihalcea","PriceInCents":3990,"Publisher":"Amazon","Title":"High-Performance Java Persistence 1st edition"} | INSERT  | 2020-11-08 08:40:28.343 | Vlad Mihalcea | 2020-11-08 06:40:28.347 |

在更新Book表行时:

UPDATE Book 
SET PriceInCents = 4499 
WHERE Id = 1

我们可以看到BookAuditLog表上的AFTER UPDATE触发器将新记录添加到Book

| BookId | OldRowData                                                                                                                         | NewRowData                                                                                                                         | DmlType | DmlTimestamp            | DmlCreatedBy  | TrxTimestamp            |
|--------|------------------------------------------------------------------------------------------------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------|---------|-------------------------|---------------|-------------------------|
| 1      |                                                                                                                                    | {"Id":1,"Author":"Vlad Mihalcea","PriceInCents":3990,"Publisher":"Amazon","Title":"High-Performance Java Persistence 1st edition"} | INSERT  | 2020-11-08 08:40:28.343 | Vlad Mihalcea | 2020-11-08 06:40:28.347 |
| 1      | {"Id":1,"Author":"Vlad Mihalcea","PriceInCents":3990,"Publisher":"Amazon","Title":"High-Performance Java Persistence 1st edition"} | {"Id":1,"Author":"Vlad Mihalcea","PriceInCents":4499,"Publisher":"Amazon","Title":"High-Performance Java Persistence 1st edition"} | UPDATE  | 2020-11-08 08:43:22.803 | Vlad Mihalcea | 2020-11-08 06:43:22.807 |

删除Book表行时:

DELETE FROM Book 
WHERE Id = 1

通过BookAuditLog表上的AFTER DELETE触发器将新记录添加到Book

| BookId | OldRowData                                                                                                                         | NewRowData                                                                                                                         | DmlType | DmlTimestamp            | DmlCreatedBy  | TrxTimestamp            |
|--------|------------------------------------------------------------------------------------------------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------|---------|-------------------------|---------------|-------------------------|
| 1      |                                                                                                                                    | {"Id":1,"Author":"Vlad Mihalcea","PriceInCents":3990,"Publisher":"Amazon","Title":"High-Performance Java Persistence 1st edition"} | INSERT  | 2020-11-08 08:40:28.343 | Vlad Mihalcea | 2020-11-08 06:40:28.347 |
| 1      | {"Id":1,"Author":"Vlad Mihalcea","PriceInCents":3990,"Publisher":"Amazon","Title":"High-Performance Java Persistence 1st edition"} | {"Id":1,"Author":"Vlad Mihalcea","PriceInCents":4499,"Publisher":"Amazon","Title":"High-Performance Java Persistence 1st edition"} | UPDATE  | 2020-11-08 08:43:22.803 | Vlad Mihalcea | 2020-11-08 06:43:22.807 |
| 1      | {"Id":1,"Author":"Vlad Mihalcea","PriceInCents":4499,"Publisher":"Amazon","Title":"High-Performance Java Persistence 1st edition"} |                                                                                                                                    | DELETE  | 2020-11-08 08:44:25.630 | Vlad Mihalcea | 2020-11-08 06:44:25.633 |

答案 4 :(得分:0)

看看触发器。这些可用于实现符合您要求的东西。

答案 5 :(得分:0)

看一下这篇文章 - Auditing in SQL Server 2008,它精美地利用了SQL Server 2008中已经存在的审计功能。

我还必须提到@Microtechie回答了一些很棒的文章。阅读它们并确定哪一个更容易适应。

答案 6 :(得分:0)

我看到3种方法来实现这一目标:

  1. 触发器是最好的解决方案。
  2. 您可以为表或数据库实施复制/日志传送,这始终是该表/数据库的旧副本数毫秒/秒。
  3. 根据所需的旧副本的时间安排差异备份。

使用选项'2',万一出现任何问题,您可以关闭复制/日志传送,并获得几秒钟的精确数据备份。 例如,如果使用选项“ 3”,则每5分钟具有不同的备份频率,那么,如果出现任何问题,您可以恢复5分钟之久的正确数据副本。

答案 7 :(得分:-2)

使用数据库快照,您可以在该瞬间保留数据的只读副本。此外,通过备份日志,您可以在特定时间段恢复信息。

您还可以从日志中读取信息以检索更改的信息。

您不喜欢的另一个解决方案是使用触发器跟踪更改,但可能需要处理每个表。您还可以启用“更改数据捕获”功能来检测更改,还需要为每个表启用此功能,但它需要的代码少于触发器。

最后,还有第三方工具,例如Apex SQL Trigger,只需点击几下,配置就会自动完成这项工作。