外键的审计触发器

Posted

技术标签:

【中文标题】外键的审计触发器【英文标题】:Audit trigger for foreign key 【发布时间】:2020-12-05 01:57:18 【问题描述】:

下面的代码是我的Branch 表的审核触发器。它能够记录Branch 表中的任何编辑或插入更改。

但是,我有一个指向 BranchZone 表的外键 BranchZoneID。我想这样做,以便记录对此外键的任何更改,并且它将显示BranchZone 表中的BranchZoneName 而不是BranchZoneID

我尝试使用代码为外键属性BranchZoneID 创建触发器。但是,我无法为其创建工作触发器。

BranchZoneID 的触发器不起作用。我需要改变什么?

create trigger Branch_Audit
on dbo.Branch
after insert, update
not for replication
as
begin
  set nocount on;

  declare @operation char(10) = case when exists (select * from deleted) then 'Edit' else 'Insert' end;

  insert AuditLog
(TableName, ModifiedBy, AuditDateTime, AuditAction, ID, ChangedColumn, OldValue, NewValue)
    select
      'Branch', suser_sname(), getdate(), @operation, I.BranchZoneID,
      'BranchName', convert(varchar(21),D.BranchName), convert(varchar(21),I.BranchName)
    from inserted I
    left outer join deleted D on I.BranchID = D.BranchID
    where coalesce(I.BranchName,'') <> coalesce(D.BranchName,'')
    and update(BranchName)
    union all

    select
    'Branch', suser_sname(), getdate(), @operation, I.BranchID,
    'BranchAddress', D.BranchAddress, I.BranchAddress
    from inserted I
    left outer join deleted D on I.BranchID = D.BranchID
    where coalesce(I.BranchAddress,'') <> coalesce(D.BranchAddress,'')
    and update(BranchAddress)
    union all


    select
    'Branch', suser_sname(), getdate(), @operation, I.BranchID,
    'BranchGeoLocationLat', D.BranchGeoLocationLat, I.BranchGeoLocationLat
    from inserted I
    left outer join deleted D on I.BranchID = D.BranchID
    where coalesce(I.BranchGeoLocationLat,'') <> coalesce(D.BranchGeoLocationLat,'')
    and update(BranchGeoLocationLat)
    union all


    select
    'Branch', suser_sname(), getdate(), @operation, I.BranchID,
    'BranchGeoLocationLong', D.BranchGeoLocationLong, I.BranchGeoLocationLong
    from inserted I
    left outer join deleted D on I.BranchID = D.BranchID
    where coalesce(I.BranchGeoLocationLong,'') <> coalesce(D.BranchGeoLocationLong,'')
    and update(BranchGeoLocationLong)
    union all

    select
    'Branch', SUSER_SNAME(), GETDATE(), @operation, I.BranchID,
    'BranchZoneID', OWD.BranchZoneName, NWD.BranchZoneName
    from inserted I
    left outer join deleted D on I.BranchID = D.BranchID
    where coalesce(I.BranchZoneID,'') <> coalesce(D.BranchZoneID,'')
    and update(BranchZoneID)
    -- Fetch Branch Zone Name
    on deleted.BranchZoneID = OWD.BranchZoneID
    join dbo.BranchZone NWD
    on inserted.BranchZoneID = NWD.BranchZoneID

end;

【问题讨论】:

那么有什么大不了的?您加入适当的表。您不能做的只是简单地“发明”一个别名并在您的 select 语句中引用它。并且为了将来参考,当您的问题包含错误时,您应该发布完整和实际的错误消息。而且您实际上应该提到您的代码会产生错误 - 这是因为 OWD 和 NWD 是尚未定义的别名。 【参考方案1】:

您只是对您的加入感到困惑......正确加入并且它按预期工作。请记住,当使用表别名时,从那时起您只能使用别名引用表,例如I 而不是 Inserted

还假设BranchZoneIDint,您需要将coalesce 设置为未使用的int,例如0 不是空字符串。

select
    'Branch', suser_sname(), getdate(), @operation, I.BranchID
    , 'BranchZoneID', OWD.BranchZoneName, NWD.BranchZoneName
from inserted I
left outer join deleted D on I.BranchID = D.BranchID

-- Fetch Branch Zone Names
left join dbo.BranchZone OWD on OWD.BranchZoneID = D.BranchZoneID
left join dbo.BranchZone NWD on NWD.BranchZoneID = I.BranchZoneID

where coalesce(I.BranchZoneID,0) <> coalesce(D.BranchZoneID,0)
and update(BranchZoneID);

如果BranchZoneIDuniqueidentifer,您将使用:

where coalesce(I.BranchZoneID,'00000000-0000-0000-0000-000000000000') <> coalesce(D.BranchZoneID,'00000000-0000-0000-0000-000000000000')

当使用coalesce以这种方式比较值时,您需要选择一个对数据类型有效但在您的数据中无效的值。

【讨论】:

您好,如果 BranchZoneID 是唯一标识符,我必须将 (I.BranchZoneID,0) 中的“0”更改为什么? 当我尝试在我的网站中编辑 BranchZoneID 时,弹出此错误:“SqlException: Error convert data type nvarchar to numeric.” 'OldValue' 和 'NewValue' 的数据类型是 Nvarchar(250)。但是,我怀疑是否需要更改其数据类型。 如果BranchZoneID 是唯一标识符,我不知道会发生什么。在某处您有一个 int 列,但您没有正确转换 - 但没有看到您的表定义和示例数据,我无法提供任何建议。 我的表中没有 int 列。

以上是关于外键的审计触发器的主要内容,如果未能解决你的问题,请参考以下文章

Sybase ASE 15.7 中的审计表触发器

如何用java中取到作为数据库主键的值?

记录一下mysql触发器的一个小知识点

记录一下mysql触发器的一个小知识点

记录一下mysql触发器的一个小知识点

如何在导入过程中禁用Oracle约束条件和触发器