我不知道这些要求是否是标准的,但我想知道是否有可以执行以下操作的解决方案:
- 对于一组指定的表,在相关表的审计版本中更改记录之前保留一份记录的副本。
我宁愿不必为每个表都编写代码。我想知道是否有一个可以安装在 SQL Server 之上的解决方案来为您执行此操作?
我不知道这些要求是否是标准的,但我想知道是否有可以执行以下操作的解决方案:
我宁愿不必为每个表都编写代码。我想知道是否有一个可以安装在 SQL Server 之上的解决方案来为您执行此操作?
我创建了为 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 中调用来为数据库中的每个表创建它,但我们目前不需要它,只要记住更改你的表名
干杯
假设我们有一个Book
表,其审计日志信息必须存储在一个BookAuditLog
表中,如下面的类图所示:
该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
存储记录状态的 JSON 表示。Book
NewRowData
存储记录状态的 JSON 表示。Book
DmlType
是一个枚举列,存储创建、更新或删除给定Book
行的 DML 语句类型。DmlTimestamp
存储 DML 语句执行时间戳。DmlCreatedBy
存储发出 INSERT、UPDATE 或 DELETE DML 语句的用户。TrxTimestamp
存储更改Book
记录的事务的时间戳。要捕获 INSERT、UPDATE 和 DELETE DML 语句,我们需要创建三个将在BookAuditLog
表中插入记录的数据库触发器。
为了拦截表上的 INSERT 语句Book
,我们将创建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 |
您可以尝试基于第三方点击式触发器的解决方案,例如ApexSQL Audit - SQL Server 数据库的审计工具,它捕获数据库上发生的数据更改,包括有关谁进行更改的信息,哪些对象是受影响的时间,以及有关用于进行更改的 SQL 登录、应用程序和主机的信息。它将所有捕获的信息存储在中央存储库中,并以打印友好的格式导出
免责声明:我在 ApexSQL 担任产品支持工程师
看看触发器。这些可用于实现满足您要求的某些东西。
看看这篇文章——SQL Server 2008中的审计,它完美地利用了 SQL Server 2008 中已经存在的审计功能。
我还必须提到@Microtechie 的答案指向了一些很棒的文章。阅读它们并决定哪一个更容易适应。
我看到了 3 种方法来实现这一点:
使用选项“2”,如果出现任何问题,您可以立即关闭复制/日志传送并获得几秒钟前的准确数据副本。使用选项“3”,例如,您的差异备份频率为每 5 分钟一次,那么如果出现任何问题,您可以恢复 5 分钟前的正确数据的旧副本。
使用数据库快照,您可以在那一刻保留数据的只读副本。如果需要,您还可以通过日志备份在特定时间段恢复您的信息。
您还可以从日志中读取信息以检索更改的信息。
您不喜欢的另一个解决方案是使用触发器跟踪更改,但它可能需要在每个表上工作。您还可以启用 Change Data Capture 功能来检测更改,该功能也需要为每个表启用,但它需要的代码比触发器少。
最后,像Apex SQL Trigger这样的第三方工具可以通过几次点击和配置自动完成这项工作。