我的表格中列businessname
,sortcode
和accountnumber
已填充,name
,nationality
和DOB
所有目前都未填充。我需要创建一个触发器,以便在更新任何空字段时将每次更新都发送到审计表中,这样如果我只更改名称,我将获得时间戳,用户ID,字段更改,旧值和新value ..如果我更改了所有3个空字段,我想将3行发送到审计表。
有人可以给我一个关于这个逻辑的指针吗?
用于测试的非常基本的格式我已经
CREATE TRIGGER RM_UPDATE_TRIGGER ON RM_BASE
ON UPDATE
AS
INSERT INTO RM_AUDITLOG
SELECT CURRENT_TIMESTAMP, SRT_CD
FROM RM_BASE
但这是在UPDATE之后将所有当前行发送到其中任何一行,我只想要受影响的行。我不确定是否应该构建更多的表来连接在一起以获得最终答案或使用INSERT / DELETE表。我以前的角色已经看过这种格式的审计表,所以我知道它有效,但无法弄清楚!
由于
答案 0 :(得分:0)
是的,您需要使用INSERTED
和/或DELETED
伪表,因为它们只包含已修改的行。如:
CREATE TRIGGER RM_UPDATE_TRIGGER ON RM_BASE
ON UPDATE
AS
INSERT INTO RM_AUDITLOG
SELECT CURRENT_TIMESTAMP, SRT_CD
FROM INSERTED
INSERTED
表具有每行的“新”或“当前”版本,而DELETED
表具有已通过UPDATE
操作替换的“旧”版本。所有版本的SQL Server都是这种情况,至少可以追溯到SQL Server 2000。
为了跟踪更改本身(“旧”和“新”值),您需要加入这两个伪表,如:
INSERT INTO RM_AUDITLOG
SELECT CURRENT_TIMESTAMP, ins.SRT_CD AS [SRT_CD_new], del.SRT_CD AS [SRT_CD_old]
FROM INSERTED ins
INNER JOIN DELETED del
ON del.PKfield = ins.PKfield
这是在DML触发器中捕获更改的基本操作(当然,除非您使用Change Data Capture)。
如果您希望unpivot这些数据使得每组“旧”和“新”列成为一行,那么应该可以很容易地从上面进行调整。在这种情况下,您还可以添加WHERE ISNULL(ins.column, '~~~~') <> ISNULL(del.column, '~~~~') COLLATE Latin1_General_BIN
以避免捕获未更改的字段。 COLLATE
可确保区分大小写/重音敏感/等比较。
当然,由于您需要永久保留完整的历史记录,因此对于整个行的重建非常困难。您需要从所有字段的基值开始并逐步应用更改。典型的审计方案是捕获每个源字段都有旧字段和新字段的行(就像我已经显示的那样)。如果您的审计表看起来更像:
PKfield, DateModified, businessname_old, businessname_new, sortcode_old, sortcode_new
然后你可以编写一个查询来通过比较每个集合来识别实际更改的字段(假设在同一个UPDATE
操作中可以更改多个字段),例如:
SELECT PKfield,
DateModified,
CASE
WHEN ISNULL(businessname_old, '~~~~') <> ISNULL(businessname_new, '~~~~')
COLLATE Latin1_General_BIN THEN 'BusinessName ' ELSE ''
END +
CASE
WHEN ISNULL(sortcode_old, '~~~~') <> ISNULL(sortcode_new, '~~~~')
COLLATE Latin1_General_BIN THEN 'SortCode ' ELSE ''
END AS [FieldsChanged]
FROM AuditTable
ORDER BY DateModified DESC;
但是,如果确实想要将数据展开以使每个实际更改的字段有一行,那么以下结构应该有效:
;WITH ins AS
(
SELECT PKfield, FieldName, Value
FROM (
SELECT PKfield, businessname, sortcode, accountnumber, name,
nationality, DOB
FROM INSERTED
) cols
UNPIVOT (Value FOR FieldName IN
(businessname, sortcode, accountnumber, name, nationality, DOB)
) colvals
), del AS
(
SELECT PKfield, FieldName, Value
FROM (
SELECT PKfield, businessname, sortcode, accountnumber, name,
nationality, DOB
FROM DELETED
) cols
UNPIVOT (Value FOR FieldName IN
(businessname, sortcode, accountnumber, name, nationality, DOB)
) colvals
)
INSERT INTO AuditTable (PKfield, DateModified, FieldName, OldValue, NewValue)
SELECT ins.PKfield, CURRENT_TIMESTAMP, ins.FieldName, del.Value, ins.Value
FROM ins
INNER JOIN del
ON del.PKfield = ins.PKfield
AND del.FieldName = ins.FieldName
WHERE ISNULL(del.Value, '~~~~') <>
ISNULL(ins.Value, '~~~~') COLLATE Latin1_General_BIN;
如果CONVERT(VARCHAR(1000), field)
是WHERE
或DOB
字段,可能需要向DATE
条件添加DATETIME
,或者如果SRT_CD
是INT或其他类型的数字字段:
WHERE ISNULL(CONVERT(VARCHAR(1000), del.Value), '~~~~') <>
ISNULL(CONVERT(VARCHAR(1000), ins.Value), '~~~~')
COLLATE Latin1_General_BIN;