SQL Server – History Table (Audit/Archive Table)
前言
续上一篇的 Soft Delete 后, 我们继续来看看 History Table (Audit/Archive Table).
Archive Table 市场上有了这样叫, 但我觉得它比较杂, 因为它既保存了 History, 也多少记入 operation 和时间, 所以带有一点 Audit 的味道.
我们姑且叫它 HIstory Table 就好呗.
上回说到我们的需求就是不希望任何数据被删除, 找不回来.
除了 Soft Delete, Temporal Table 能完成这个需求, 还有就是 History Table.
也就就是自己做一个 table 然后把所有 delete/update 移除的数据放以 json 的方式存进去. (和 temporal 差不多, 只是 temporal 是每一个表都一个 history table, 然后它不是 json 而是完整的表结构)
这个方案的优点就是没有 Temporal 那么重, 又比 Soft Delete 轻.
Step 1: Create History Table
大概长这样
Step 2: Trigger After Delete
GO CREATE OR ALTER TRIGGER [TR_Country_AfterDelete_ForAuditData] ON [Country] AFTER DELETE AS IF (ROWCOUNT_BIG() = 0) RETURN; SET NOCOUNT ON; DECLARE @i int = 0, @count int; SELECT @count = COUNT(*) FROM deleted; DECLARE @dateCreated datetimeoffset(3) = CAST(SYSDATETIMEOFFSET() AT TIME ZONE 'Singapore Standard Time' AS datetimeoffset(3)); WHILE(@i < @count) BEGIN INSERT INTO AuditDataLog (TableName, Operation, DeletedDataJson, InsertedDataJson, DateCreated) VALUES ( 'Country', 'Delete', (SELECT * FROM (SELECT * FROM deleted ORDER BY CountryId OFFSET @i ROWS FETCH NEXT 1 ROWS ONLY) Temp FOR JSON AUTO, INCLUDE_NULL_VALUES, WITHOUT_ARRAY_WRAPPER), NULL, @dateCreated ); SET @i += 1; END GO
要记得哦, deleted 是一个表, 里面会有很多 deleted rows 的. 这里把 deleted row 一条一条 insert 是为了以后方便 filter 查看. 但是会让性能慢的哦.
Step 3: Trigger After Update
和上面差不多, 只是多了一句 for inserted 而已.
GO CREATE OR ALTER TRIGGER [TR_Country_AfterDelete_ForAuditData] ON [Country] AFTER UPDATE AS IF (ROWCOUNT_BIG() = 0) RETURN; SET NOCOUNT ON; DECLARE @i int = 0, @count int; SELECT @count = COUNT(*) FROM deleted; DECLARE @dateCreated datetimeoffset(3) = CAST(SYSDATETIMEOFFSET() AT TIME ZONE 'Singapore Standard Time' AS datetimeoffset(3)); WHILE(@i < @count) BEGIN INSERT INTO AuditDataLog (TableName, Operation, DeletedDataJson, InsertedDataJson, DateCreated) VALUES ( 'Country', 'Update', (SELECT * FROM (SELECT * FROM deleted ORDER BY CountryId OFFSET @i ROWS FETCH NEXT 1 ROWS ONLY) Temp FOR JSON AUTO, INCLUDE_NULL_VALUES, WITHOUT_ARRAY_WRAPPER), (SELECT * FROM (SELECT * FROM inserted ORDER BY CountryId OFFSET @i ROWS FETCH NEXT 1 ROWS ONLY) Temp FOR JSON AUTO, INCLUDE_NULL_VALUES, WITHOUT_ARRAY_WRAPPER), @dateCreated ); SET @i += 1; END GO
题外话:
本来想把这个表叫 AuditDataLog 的, 但由于 deleted 没有办法记入 DeletedBy (除非搞一些 stored procedure)
所以还是把它定位为 History Table 就好了. 如果想知道是谁 delete 了, 那么这里获取到时间后, 在去查 Audit Api Log, 有了时间范围, 要找出谁 performance 了相关 action 就容易多了.