将更新记录到审核表中的SQL触发器
我有一个表,其中列businessname、sortcode和accountnumber都已填充,名称、国籍和DOB都当前未填充。我需要创建一个触发器,在更新任何空字段时触发对审计表的每次更新,因此如果我只更改名称,我将获得时间戳、用户ID、更改的字段、旧值和新值。。如果我更改了所有3个空字段,我将向审核表发送3行 有人能给我一个关于这个逻辑的指针吗 以一种非常基本的形式进行测试将更新记录到审核表中的SQL触发器,sql,sql-server-2008-r2,Sql,Sql Server 2008 R2,我有一个表,其中列businessname、sortcode和accountnumber都已填充,名称、国籍和DOB都当前未填充。我需要创建一个触发器,在更新任何空字段时触发对审计表的每次更新,因此如果我只更改名称,我将获得时间戳、用户ID、更改的字段、旧值和新值。。如果我更改了所有3个空字段,我将向审核表发送3行 有人能给我一个关于这个逻辑的指针吗 以一种非常基本的形式进行测试 CREATE TRIGGER RM_UPDATE_TRIGGER ON RM_BASE ON UPDATE AS
CREATE TRIGGER RM_UPDATE_TRIGGER ON RM_BASE
ON UPDATE
AS
INSERT INTO RM_AUDITLOG
SELECT CURRENT_TIMESTAMP, SRT_CD
FROM RM_BASE
但这是在更新任何一行之后发送所有当前行,我只想要受影响的行。我不确定是应该构建更多的表来连接在一起以获得最终答案,还是使用INSERT/DELETE表。。我在以前的角色中看到过这种格式的审计表,所以我知道它可以工作,但无法理解
谢谢是的,您需要使用插入和/或删除的伪表,因为这些伪表只包含已修改的行。例如:
CREATE TRIGGER RM_UPDATE_TRIGGER ON RM_BASE
ON UPDATE
AS
INSERT INTO RM_AUDITLOG
SELECT CURRENT_TIMESTAMP, SRT_CD
FROM INSERTED
插入的表具有每行的新版本或当前版本,而删除的表具有已通过更新操作替换的旧版本。所有版本的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触发器中使用
如果您希望将这些数据转换为一行,使每组旧列和新列成为一行,那么应该可以很容易地从上面进行调整。在这种情况下,您还可以添加WHERE ISNULLins.column、~~~~'ISNULLdel.column、~~~~'COLLATE Latin1\u General\u BIN,以避免捕获未更改的字段。COLLATE确保区分大小写/区分重音/etc比较
当然,取消激活会使重建整行变得非常困难,因为您需要永远保存完整的历史记录。您需要从所有字段的基值开始,并以增量方式应用更改。典型的审核场景是只捕获一行,该行的每个源字段都有新旧字段,如我已经展示的。如果您的审核表看起来更像:
PKfield,DateModified,businessname_old,businessname_new,sortcode_old,sortcode_new
然后,您可以编写一个查询,通过比较每个集合来确定实际更改的字段,因为在相同的更新操作中可以更改多个字段,例如:
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;
抱歉-SQLServer2008R2AH太棒了,谢谢-我从未使用过伪表。如何获取已从插入或删除中更新的字段名?@user3294008,首先,我只是重新阅读了这个问题,听起来您只想在字段从NULL变为具有值时捕获字段,而不是在非NULL值变为另一个非NULL值时捕获字段。这是正确的还是问题中的措辞错误?不-我想捕获从现在开始的所有更改,无论是空的还是填充的,只是它们都是当前的null@user3294008,这更有意义,这也是我最初的设想。唯一不完全是您想要的部分是需要首先取消伪表。我可以将我的答案更新为基本结构,但您必须从那里完成它。这很好-我理解所有的unpivot,老实说,这不是我的解决方案的要求,但我真的想引入已更新的列名-有没有快速简单的方法来做到这一点?
;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;
WHERE ISNULL(CONVERT(VARCHAR(1000), del.Value), '~~~~') <>
ISNULL(CONVERT(VARCHAR(1000), ins.Value), '~~~~')
COLLATE Latin1_General_BIN;