【问题标题】:MS SQL trigger For Saved Old Data to another Table In UpdationMS SQL 触发器将旧数据保存到更新中的另一个表
【发布时间】:2017-03-20 03:34:21
【问题描述】:

我有一个员工表作为关注, https://i.stack.imgur.com/WjtM6.png

在每个更新(任何列删除,更新)中,出于安全目的,都需要将该详细信息保存到另一个表中。该 tbale 结构如下。 https://i.stack.imgur.com/Yv1YD.png

我可以在我的程序中编写代码来完成这项工作,但是我可以编写一个触发器来使用触发器将旧数据自动保存到员工更新表吗? 在 MySql 中,该功能可用,我不知道 MS SQL。 谢谢!

【问题讨论】:

  • 主键(service_id)可以改变吗?如果不是,那么在触发器中插入/删除时使用完全联接是一件简单的事情。如果是,那就更复杂了。
  • @ZLK 没有主键永远不会改变?你能给我看一个示例代码吗?
  • 好吧,首先,如果您的日志记录表是专门为您的员工表设计的,那么您应该在其中添加一个带有 PK 的列(这样您就可以确定哪条记录被更改了)。我不确定你想如何捕获 whodunnit 信息(例如,你想捕获 host_name()、sql 登录本身还是完全其他的东西?)所以我将专注于其他东西。像这样写一个触发器:
  • CREATE TRIGGER tr_Employee_Update_Delete ON myemployeetable AFTER UPDATE, DELETE AS BEGIN INSERT mylogtable (Service_ID, changed_column, old_value, change_date, used_ip) SELECT D.Service_ID, C.name, C.prev, GETDATE(), CONNECTIONPROPERTY('client_net_address') FROM inserted AS I FULL JOIN deleted AS D ON D.ServiceID = I.ServiceID CROSS APPLY (VALUES ('Title', I.Title, D.Title), ('Name', I.Name, D.Name), ('Initials', I.Initials, D.Initials)...) AS C(name, cur, prev) WHERE C.prev != C.cur AND C.prev IS NOT NULL; END 您需要在交叉申请中手动填写其余列。
  • 如果您需要从空值捕获更改,您需要SET ANSI_NULLS OFF(或者如果可以这样做,则使用 ISNULL 的一些变体)并从触发器中删除 AND C.prev IS NOT NULL

标签: mysql sql-server triggers


【解决方案1】:

我建议创建类似于原始表的审计表

IF NOT EXISTS
      (SELECT * FROM sysobjects WHERE id = OBJECT_ID(N'[dbo].[Audit]') 
               AND OBJECTPROPERTY(id, N'IsUserTable') = 1)
       CREATE TABLE Audit 
               (Type CHAR(1), 
               TableName VARCHAR(128), 
               PK VARCHAR(1000), 
               FieldName VARCHAR(128), 
               OldValue VARCHAR(1000), 
               NewValue VARCHAR(1000), 
               UpdateDate datetime, 
               UserName VARCHAR(128))

GO

然后对触发器进行如下操作,它可以跟踪、INSERT、UPDATE 和 DELETE 操作。

CREATE TRIGGER TR_GUESTS_AUDIT ON dbo.IBIS_UnitSchedule FOR UPDATE
AS

DECLARE @bit INT ,
       @field INT ,
       @maxfield INT ,
       @char INT ,
       @fieldname VARCHAR(128) ,
       @TableName VARCHAR(128) ,
       @PKCols VARCHAR(1000) ,
       @sql VARCHAR(2000), 
       @UpdateDate VARCHAR(21) ,
       @UserName VARCHAR(128) ,
       @Type CHAR(1) ,
       @PKSelect VARCHAR(1000)



SELECT @TableName = 'dbo.IBIS_UnitSchedule'

-- date and user
SELECT         @UserName = SYSTEM_USER ,
       @UpdateDate = CONVERT (NVARCHAR(30),GETDATE(),126)

-- Action
IF EXISTS (SELECT * FROM inserted)
       IF EXISTS (SELECT * FROM deleted)
               SELECT @Type = 'U'
       ELSE
               SELECT @Type = 'I'
ELSE
       SELECT @Type = 'D'

-- get list of columns
SELECT * INTO #ins FROM inserted
SELECT * INTO #del FROM deleted

-- Get primary key columns for full outer join
SELECT @PKCols = COALESCE(@PKCols + ' and', ' on') 
               + ' i.' + c.COLUMN_NAME + ' = d.' + c.COLUMN_NAME
       FROM    INFORMATION_SCHEMA.TABLE_CONSTRAINTS pk ,

              INFORMATION_SCHEMA.KEY_COLUMN_USAGE c
       WHERE   pk.TABLE_NAME = @TableName
       AND     CONSTRAINT_TYPE = 'PRIMARY KEY'
       AND     c.TABLE_NAME = pk.TABLE_NAME
       AND     c.CONSTRAINT_NAME = pk.CONSTRAINT_NAME

-- Get primary key select for insert
SELECT @PKSelect = COALESCE(@PKSelect+'+','') 
       + '''<' + COLUMN_NAME 
       + '=''+convert(varchar(100),
coalesce(i.' + COLUMN_NAME +',d.' + COLUMN_NAME + '))+''>''' 
       FROM    INFORMATION_SCHEMA.TABLE_CONSTRAINTS pk ,
               INFORMATION_SCHEMA.KEY_COLUMN_USAGE c
       WHERE   pk.TABLE_NAME = @TableName
       AND     CONSTRAINT_TYPE = 'PRIMARY KEY'
       AND     c.TABLE_NAME = pk.TABLE_NAME
       AND     c.CONSTRAINT_NAME = pk.CONSTRAINT_NAME

IF @PKCols IS NULL
BEGIN
       RAISERROR('no PK on table %s', 16, -1, @TableName)
       RETURN
END

SELECT         @field = 0, 
       @maxfield = MAX(ORDINAL_POSITION) 
       FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = @TableName
WHILE @field < @maxfield
BEGIN
       SELECT @field = MIN(ORDINAL_POSITION) 
               FROM INFORMATION_SCHEMA.COLUMNS 
               WHERE TABLE_NAME = @TableName 
               AND ORDINAL_POSITION > @field
       SELECT @bit = (@field - 1 )% 8 + 1
       SELECT @bit = POWER(2,@bit - 1)
       SELECT @char = ((@field - 1) / 8) + 1
       IF SUBSTRING(COLUMNS_UPDATED(),@char, 1) & @bit > 0
                                       OR @Type IN ('I','D')
       BEGIN
               SELECT @fieldname = COLUMN_NAME 
                       FROM INFORMATION_SCHEMA.COLUMNS 
                       WHERE TABLE_NAME = @TableName 
                       AND ORDINAL_POSITION = @field
               SELECT @sql = '
insert Audit (    Type, 
               TableName, 
               PK, 
               FieldName, 
               OldValue, 
               NewValue, 
               UpdateDate, 
               UserName)
select ''' + @Type + ''',''' 
       + @TableName + ''',' + @PKSelect
       + ',''' + @fieldname + ''''
       + ',convert(varchar(1000),d.' + @fieldname + ')'
       + ',convert(varchar(1000),i.' + @fieldname + ')'
       + ',''' + @UpdateDate + ''''
       + ',''' + @UserName + ''''
       + ' from #ins i full outer join #del d'
       + @PKCols
       + ' where i.' + @fieldname + ' <> d.' + @fieldname 
       + ' or (i.' + @fieldname + ' is null and  d.'
                                + @fieldname
                                + ' is not null)' 
       + ' or (i.' + @fieldname + ' is not null and  d.' 
                                + @fieldname
                                + ' is null)' 
               EXEC (@sql)
       END
END

GO

【讨论】:

  • 感谢您的宝贵时间,我会努力的!
猜你喜欢
  • 2020-11-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-12-05
  • 1970-01-01
  • 2011-04-12
  • 2023-03-12
相关资源
最近更新 更多