如何保持对表的更改的审计/历史记录

时间:2009-11-25 16:27:38

标签: sql-server tsql ado.net triggers

我被要求创建一个简单的DataGrid风格的应用程序来编辑数据库的单个表,这很容易。但部分请求是创建一个审计跟踪,包括所做的更改,制作者以及日期/时间。

你怎么能解决这种事情?

(我将在VS2008中使用C#,ADO.NET连接到SQL Server 2005,WPF和Xceed的DataGrid,如果它有任何区别。)

8 个答案:

答案 0 :(得分:12)

创建审计跟踪有两种常用方法。

  1. 对您的数据访问层进行编码。
  2. 在数据库中使用触发器。
  3. 两者都有优点和缺点。有些人比较喜欢一个人。它通常取决于应用程序的类型以及您可以期待的数据库使用类型。

    如果你在DA层中这样做,那将非常适合你。您只需要为保存到数据库的每个方法添加代码,以便保存更改日志。如果您使用存储过程来处理所有内容,则此审核代码可能位于DA层代码中,甚至可能存储在数据库中的存储过程中。基本上前提是相同的,每当您对数据库进行更改时,请记录该更改。

    如果你想沿着触发器路线走,你可以为每个表编写自定义触发器,或者设计一个在许多表上工作相同的更通用的触发器。查看有关审核触发器的this article。无论何时进行更改,触发器都会触发,触发器会记录更改。请记住,如果要审核SELECT语句,则不能使用触发器,您必须在代码/存储过程审计中执行此操作。值得记住的是,根据您的数据库,触发器可能无法在所有情况下触发。例如,大多数数据库在TRUNCATE语句期间不触发触发器。在任何需要审核的情况下检查您的触发器是否会被触发。

    或者,您还可以查看使用service broker在专用计算机上执行异步审核。这样更复杂,需要进行一些配置才能进行设置。

    无论您采用哪种方式,都需要确定审核日志的格式。通常,您会将此日志保存在数据库中,但您可以将其保存在日志文件中或任何符合您要求的日志中。您可以使用记录所有更改的单个审计表,也可以在每个要审计的主表上使用审计表。对于大规模实现,您甚至可以考虑将审计表放在完全独立的数据库中。如果您登录到表中,通常会有一个“更改类型”字段,指示审核的更改是插入,更新还是删除更改方式,以及更改的数据,进行更改的用户以及日期/时间改变了。不要忘记包含更新样式更改的旧数据和新数据。

答案 1 :(得分:8)

最普遍的方法是创建另一个表来存储第一个表中的记录版本。然后,您可以从主表中删除所有数据。假设您需要对表Person(PersonId,Name,Surname)进行版本控制:

CREATE TABLE Person 
(
   PersonId INT,                   // PK
   CurrentPersonVersion INT        // FK
);

CREATE TABLE PersonVersion
(
  PersonVersionId INT,             // PK
  PersonID                         // FK 
  Name VARCHAR,                    // actual data
  Surname VARCHAR,                 // actual data

  ChangeDate                       // logging data
  ChangeAuthor                     // logging data
)

现在任何更改都需要插入新的PersonVersion并更新CurrentPersonVersionID。

答案 2 :(得分:8)

同上使用触发器。

任何考虑软删除的人都应该阅读Richard Dingwall的The trouble with soft delete

答案 3 :(得分:2)

执行此操作的最佳方法是在数据库中设置写入审计表的触发器。

答案 4 :(得分:1)

除了触发器之外,另一种方法就是这样做,

  1. 为您要进行审计跟踪的每个表格提供四列UpdFlagDelFlagEffectiveDateTerminatedDate
  2. 以这样的方式对您的sproc进行编码:当您进行更新时,将所有行的列数据传递到sproc中,通过将TerminatedDate设置为更新的日期来更新行,并且标记UpdFlag并将日期时间放入列
  3. 然后使用新数据创建一个新行(实际更新)。现在为EffectiveDateTerminatedDate设置为最大日期的新日期。
  4. 同样,如果您想删除该行,只需将DelFlag标记为已设置即可更新该行,TerminatedDate现在标记日期时间。您实际上正在进行软删除而不是实际的sql删除。

    通过这种方式,当您想要审核数据并显示更改的跟踪时,您可以简单地为那些设置UpdFlagEffectiveDate之间的行筛选行。 TerminatedDate。同样,对于已删除的内容,您可以针对DelFlag设置或EffectiveDateTerminatedDate之间的内容进行过滤。对于当前行,筛选具有两个标志的行。优点是,在使用触发器时,您不必为审计创建另一个表!

答案 5 :(得分:1)

我最近遇到了审核某些表的要求,我选择使用触发器。与其他人一样,我只想在审计表中看到实际更改过的字段中的条目,但是,在更新表时,应用程序正在更新行中的所有字段,无论它们是否已更改,因此,检查这些字段是否已经更新或者没有使用任何东西 - 它们都有!

因此,我想要的是检查每个字段中的实际值以查看它是否已更改的方法,并且只有将其写入审计表(如果有)。由于无法在任何地方找到任何解决方案,我想出了自己的解决方案,如下:

CREATE TRIGGER [dbo].[MyTable_CREATE_AUDIT]
ON [dbo].[MyTable]
AFTER UPDATE

AS

INSERT INTO MyTable_Audit 
(ItemID,LastModifiedBy,LastModifiedDate,field1,field2,field3,
field4,field5,AuditDate)
SELECT i.ItemID,i.LastModifiedBy,i.LastModifiedDate,

field1 = 
  CASE i.field1
    WHEN d.field1 THEN NULL
    ELSE i.field1
  END,

field2 = 
  CASE i.field2
    WHEN d.field2 THEN NULL
    ELSE i.field2
  END,

field3 = 
  CASE i.field3
    WHEN d.field3 THEN NULL
    ELSE i.field3
  END,

field4 = 
  CASE i.field4
    WHEN d.field4 THEN NULL
    ELSE i.field4
  END,  

field5 = 
  CASE i.field5
    WHEN d.field5 THEN NULL
    ELSE i.field5
  END,

GETDATE()

FROM inserted i
INNER JOIN deleted d
ON i.ItemID = d.ItemID

正如您所看到的,我比较了已删除表和插入表中每个字段的值,只是将插入表中的字段值写入审计表(如果它们不同),否则我只写NULL。 / p>

这对我有用。任何人都可以看到这种方法的任何问题?我的团队拥有应用程序和数据库,因此可能会覆盖像模式更改这样的弯曲球。

答案 6 :(得分:1)

解决方案1:SQL Server更改数据捕获

https://docs.microsoft.com/en-us/sql/relational-databases/track-changes/enable-and-disable-change-data-capture-sql-server?view=sql-server-2017

首先,您需要在数据库上启用更改数据捕获

USE AdventureWorks2012
GO  
EXEC sys.sp_cdc_enable_db  
GO  

然后,您可以使用fn_cdc_get_all_changes_fn_cdc_get_net_changes_查询更改。

-- ========  
-- Enumerate All Changes for Valid Range Template
-- ========  
USE AdventureWorks2012;  
GO  

DECLARE @from_lsn binary(10), @to_lsn binary(10);  
SET @from_lsn = sys.fn_cdc_get_min_lsn('HR_Department');  
SET @to_lsn   = sys.fn_cdc_get_max_lsn();  

SELECT * FROM cdc.fn_cdc_get_all_changes_HR_Department  
(@from_lsn, @to_lsn, N'all');  

解决方案2:SQL Server数据库审核

来源:https://www.dbaservices.com.au/how-to-configure-sql-server-auditing/

  

启用数据库审核

     

数据库审核需要进行服务器审核(尽管不一定是服务器审核规范)。但是,DB审核是在要审核的用户数据库中创建的,而不是在创建服务器审核的主数据库中创建的。在数据库本身中的“安全性->数据库审核规范”下可以找到数据库审核规范。

     

要创建数据库审核,您首先需要USE数据库(以选择它),然后下面提供了审核SELECTUPDATE和{该数据库中特定表的{1}}操作;

DELETE
     

USE UserDatabase GO CREATE DATABASE AUDIT SPECIFICATION [User_Database_Audit_Specification] FOR SERVER AUDIT [SQL_Server_Audit] ADD (SELECT , UPDATE , DELETE ON UserDatabase.dbo.Customer_DeliveryAddress BY dbo ) ,ADD (SELECT , UPDATE , DELETE ON UserDatabase.dbo.DimCustomer_Email BY dbo ) ,ADD (SELECT , UPDATE , DELETE ON UserDatabase.dbo.DimCustomer_Phone BY dbo ) WITH (STATE = ON) ; GO SELECTUPDATE操作并不是您唯一可以添加到审核规范中的内容……

DELETE

您可以在其中记录数据库事件的完整列表:

https://docs.microsoft.com/en-us/sql/relational-databases/event-classes/security-audit-event-category-sql-server-profiler?view=sql-server-2017

答案 7 :(得分:0)

我将通过创建具有与更新的结构类似的结构的表来启动触发器路由,并使用其他列来跟踪像ModifiedAt等更改。然后添加将在该表中插入更改的更新触发器。 我发现维护比在应用程序代码中包含所有内容更容易。当然,很多人在涉及“wtf这张桌子正在改变”等问题时往往会忘记触发器;)干杯。