SQL Server 更新触发器,仅获取修改的字段

问题描述 投票:0回答:9

我知道

COLUMNS_UPDATED
,好吧,我需要一些快速的捷径(如果有人做过,我已经在做了,但如果有人可以节省我的时间,我会感激的)

我基本上需要一个仅包含更新的列值的 XML,我需要它用于复制目的。

SELECT * FROM insert 为我提供了每一列,但我只需要更新的列。

类似以下内容...

CREATE TRIGGER DBCustomers_Insert
    ON DBCustomers
    AFTER UPDATE
AS
BEGIN
    DECLARE @sql as NVARCHAR(1024);
    SET @sql = 'SELECT ';


    I NEED HELP FOR FOLLOWING LINE ...., I can manually write every column, but I need 
    an automated routin which can work regardless of column specification
    for each column, if its modified append $sql = ',' + columnname...

    SET @sql = $sql + ' FROM inserted FOR XML RAW';

    DECLARE @x as XML;
    SET @x = CAST(EXEC(@sql) AS XML);


    .. use @x

END
sql sql-server triggers sql-update database-replication
9个回答
26
投票

我有另一个完全不同的解决方案,它根本不使用 COLUMNS_UPDATED,也不依赖在运行时构建动态 SQL。 (您可能想在设计时使用动态 SQL,但那是另一个故事了。)

基本上,您从“插入和删除的表”开始,对每个表进行逆透视,这样您就只剩下每个表的唯一键、字段值和字段名称列。然后将两者结合起来并过滤任何更改的内容。 这是一个完整的工作示例,包括一些测试调用以显示记录的内容。

-- -------------------- Setup tables and some initial data -------------------- CREATE TABLE dbo.Sample_Table (ContactID int, Forename varchar(100), Surname varchar(100), Extn varchar(16), Email varchar(100), Age int ); INSERT INTO Sample_Table VALUES (1,'Bob','Smith','2295','[email protected]',24); INSERT INTO Sample_Table VALUES (2,'Alice','Brown','2255','[email protected]',32); INSERT INTO Sample_Table VALUES (3,'Reg','Jones','2280','[email protected]',19); INSERT INTO Sample_Table VALUES (4,'Mary','Doe','2216','[email protected]',28); INSERT INTO Sample_Table VALUES (5,'Peter','Nash','2214','[email protected]',25); CREATE TABLE dbo.Sample_Table_Changes (ContactID int, FieldName sysname, FieldValueWas sql_variant, FieldValueIs sql_variant, modified datetime default (GETDATE())); GO -- -------------------- Create trigger -------------------- CREATE TRIGGER TriggerName ON dbo.Sample_Table FOR DELETE, INSERT, UPDATE AS BEGIN SET NOCOUNT ON; --Unpivot deleted WITH deleted_unpvt AS ( SELECT ContactID, FieldName, FieldValue FROM (SELECT ContactID , cast(Forename as sql_variant) Forename , cast(Surname as sql_variant) Surname , cast(Extn as sql_variant) Extn , cast(Email as sql_variant) Email , cast(Age as sql_variant) Age FROM deleted) p UNPIVOT (FieldValue FOR FieldName IN (Forename, Surname, Extn, Email, Age) ) AS deleted_unpvt ), --Unpivot inserted inserted_unpvt AS ( SELECT ContactID, FieldName, FieldValue FROM (SELECT ContactID , cast(Forename as sql_variant) Forename , cast(Surname as sql_variant) Surname , cast(Extn as sql_variant) Extn , cast(Email as sql_variant) Email , cast(Age as sql_variant) Age FROM inserted) p UNPIVOT (FieldValue FOR FieldName IN (Forename, Surname, Extn, Email, Age) ) AS inserted_unpvt ) --Join them together and show what's changed INSERT INTO Sample_Table_Changes (ContactID, FieldName, FieldValueWas, FieldValueIs) SELECT Coalesce (D.ContactID, I.ContactID) ContactID , Coalesce (D.FieldName, I.FieldName) FieldName , D.FieldValue as FieldValueWas , I.FieldValue AS FieldValueIs FROM deleted_unpvt d FULL OUTER JOIN inserted_unpvt i on D.ContactID = I.ContactID AND D.FieldName = I.FieldName WHERE D.FieldValue <> I.FieldValue --Changes OR (D.FieldValue IS NOT NULL AND I.FieldValue IS NULL) -- Deletions OR (D.FieldValue IS NULL AND I.FieldValue IS NOT NULL) -- Insertions END GO -- -------------------- Try some changes -------------------- UPDATE Sample_Table SET age = age+1; UPDATE Sample_Table SET Extn = '5'+Extn where Extn Like '221_'; DELETE FROM Sample_Table WHERE ContactID = 3; INSERT INTO Sample_Table VALUES (6,'Stephen','Turner','2299','[email protected]',25); UPDATE Sample_Table SET ContactID = 7 where ContactID = 4; --this will be shown as a delete and an insert -- -------------------- See the results -------------------- SELECT *, SQL_VARIANT_PROPERTY(FieldValueWas, 'BaseType') FieldBaseType, SQL_VARIANT_PROPERTY(FieldValueWas, 'MaxLength') FieldMaxLength from Sample_Table_Changes; -- -------------------- Cleanup -------------------- DROP TABLE dbo.Sample_Table; DROP TABLE dbo.Sample_Table_Changes;

所以不要搞乱bigint 位域和arth 溢出问题。如果您知道在设计时要比较的列,那么您不需要任何动态 SQL。 

缺点是输出采用不同的格式,并且所有字段值都转换为 sql_variant,第一个可以通过再次旋转输出来修复,第二个可以通过根据您的知识重新转换回所需的类型来修复表的设计,但是这两个都需要一些复杂的动态sql。这两者在 XML 输出中可能都不是问题。这个

问题

的作用类似于以相同的格式获取输出。 编辑:查看下面的评论,如果您有一个可以更改的自然主键,那么您仍然可以使用此方法。您只需使用 NEWID() 函数添加默认填充有 GUID 的列。然后,您可以使用该列代替主键。

您可能想为此字段添加索引,但由于触发器中删除和插入的表都在内存中,因此可能不会被使用,并且可能会对性能产生负面影响。


20
投票
COLUMNS_UPDATED()

来获取更新的值


-- Get the table id of the trigger -- DECLARE @idTable INT SELECT @idTable = T.id FROM sysobjects P JOIN sysobjects T ON P.parent_obj = T.id WHERE P.id = @@procid -- Get COLUMNS_UPDATED if update -- DECLARE @Columns_Updated VARCHAR(50) SELECT @Columns_Updated = ISNULL(@Columns_Updated + ', ', '') + name FROM syscolumns WHERE id = @idTable AND CONVERT(VARBINARY,REVERSE(COLUMNS_UPDATED())) & POWER(CONVERT(BIGINT, 2), colorder - 1) > 0

但是当您的表超过 62 列时,这段代码就会失败..Arth.Overflow...

这是最终版本,它处理超过 62 列,但只给出更新的列数。很容易与“syscolumns”链接来获取名称

DECLARE @Columns_Updated VARCHAR(100) SET @Columns_Updated = '' DECLARE @maxByteCU INT DECLARE @curByteCU INT SELECT @maxByteCU = DATALENGTH(COLUMNS_UPDATED()), @curByteCU = 1 WHILE @curByteCU <= @maxByteCU BEGIN DECLARE @cByte INT SET @cByte = SUBSTRING(COLUMNS_UPDATED(), @curByteCU, 1) DECLARE @curBit INT DECLARE @maxBit INT SELECT @curBit = 1, @maxBit = 8 WHILE @curBit <= @maxBit BEGIN IF CONVERT(BIT, @cByte & POWER(2,@curBit - 1)) <> 0 SET @Columns_Updated = @Columns_Updated + '[' + CONVERT(VARCHAR, 8 * (@curByteCU - 1) + @curBit) + ']' SET @curBit = @curBit + 1 END SET @curByteCU = @curByteCU + 1 END



5
投票

DECLARE @sql as NVARCHAR(1024); select @sql = coalesce(@sql + ',' + quotename(column_name), quotename(column_name)) from INFORMATION_SCHEMA.COLUMNS where substring(columns_updated(), columnproperty(object_id(table_schema + '.' + table_name, 'U'), column_name, 'columnId') / 8 + 1, 1) & power(2, -1 + columnproperty(object_id(table_schema + '.' + table_name, 'U'), column_name, 'columnId') % 8 ) > 0 and table_name = 'DBCustomers' -- and column_name in ('c1', 'c2') -- limit to specific columns -- and column_name not in ('c3', 'c4') -- or exclude specific columns SET @sql = 'SELECT ' + @sql + ' FROM inserted FOR XML RAW'; DECLARE @x as XML; SET @x = CAST(EXEC(@sql) AS XML);

它使用 
COLUMNS_UPDATED

,处理超过八列 - 它可以处理任意数量的列。 它会注意应使用

COLUMNPROPERTY

获得的正确列顺序。 它基于视图

COLUMNS

,因此它可能仅包含或排除特定列。


4
投票

/******************************************************************************************* * Add the below table to your database to track data changes using the trigger * * below. Remember to change the variables in the trigger to match the table that * * will be firing the trigger * *******************************************************************************************/ SET ANSI_NULLS ON; GO SET QUOTED_IDENTIFIER ON; GO CREATE TABLE [dbo].[AuditDataChanges] ( [RecordId] [INT] IDENTITY(1, 1) NOT NULL , [TableName] [VARCHAR](50) NOT NULL , [RecordPK] [VARCHAR](50) NOT NULL , [ColumnName] [VARCHAR](50) NOT NULL , [OldValue] [VARCHAR](50) NULL , [NewValue] [VARCHAR](50) NULL , [ChangeDate] [DATETIME2](7) NOT NULL , [UpdatedBy] [VARCHAR](50) NOT NULL , CONSTRAINT [PK_AuditDataChanges] PRIMARY KEY CLUSTERED ( [RecordId] ASC ) WITH ( PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON ) ON [PRIMARY] ) ON [PRIMARY]; GO ALTER TABLE [dbo].[AuditDataChanges] ADD CONSTRAINT [DF_AuditDataChanges_ChangeDate] DEFAULT (GETDATE()) FOR [ChangeDate]; GO /************************************************************************************************ * Add the below trigger to any table you want to audit data changes on. Changes will be saved * * in the AuditChangesTable. * ************************************************************************************************/ ALTER TRIGGER trg_Survey_Identify_Updated_Columns ON Survey --Change to match your table name FOR INSERT, UPDATE AS SET NOCOUNT ON; DECLARE @sql VARCHAR(5000) , @sqlInserted NVARCHAR(500) , @sqlDeleted NVARCHAR(500) , @NewValue NVARCHAR(100) , @OldValue NVARCHAR(100) , @UpdatedBy VARCHAR(50) , @ParmDefinitionD NVARCHAR(500) , @ParmDefinitionI NVARCHAR(500) , @TABLE_NAME VARCHAR(100) , @COLUMN_NAME VARCHAR(100) , @modifiedColumnsList NVARCHAR(4000) , @ColumnListItem NVARCHAR(500) , @Pos INT , @RecordPk VARCHAR(50) , @RecordPkName VARCHAR(50); SELECT * INTO #deleted FROM deleted; SELECT * INTO #Inserted FROM inserted; SET @TABLE_NAME = 'Survey'; ---Change to your table name SELECT @UpdatedBy = UpdatedBy --Change to your column name for the user update field FROM inserted; SELECT @RecordPk = SurveyId --Change to the table primary key field FROM inserted; SET @RecordPkName = 'SurveyId'; SET @modifiedColumnsList = STUFF(( SELECT ',' + name FROM sys.columns WHERE object_id = OBJECT_ID(@TABLE_NAME) AND SUBSTRING(COLUMNS_UPDATED(), ( ( column_id - 1 ) / 8 + 1 ), 1) & ( POWER(2, ( ( column_id - 1 ) % 8 + 1 ) - 1) ) = POWER(2, ( column_id - 1 ) % 8) FOR XML PATH('') ), 1, 1, ''); WHILE LEN(@modifiedColumnsList) > 0 BEGIN SET @Pos = CHARINDEX(',', @modifiedColumnsList); IF @Pos = 0 BEGIN SET @ColumnListItem = @modifiedColumnsList; END; ELSE BEGIN SET @ColumnListItem = SUBSTRING(@modifiedColumnsList, 1, @Pos - 1); END; SET @COLUMN_NAME = @ColumnListItem; SET @ParmDefinitionD = N'@OldValueOut NVARCHAR(100) OUTPUT'; SET @ParmDefinitionI = N'@NewValueOut NVARCHAR(100) OUTPUT'; SET @sqlDeleted = N'SELECT @OldValueOut=' + @COLUMN_NAME + ' FROM #deleted where ' + @RecordPkName + '=' + CONVERT(VARCHAR(50), @RecordPk); SET @sqlInserted = N'SELECT @NewValueOut=' + @COLUMN_NAME + ' FROM #Inserted where ' + @RecordPkName + '=' + CONVERT(VARCHAR(50), @RecordPk); EXECUTE sp_executesql @sqlDeleted, @ParmDefinitionD, @OldValueOut = @OldValue OUTPUT; EXECUTE sp_executesql @sqlInserted, @ParmDefinitionI, @NewValueOut = @NewValue OUTPUT; IF ( LTRIM(RTRIM(@NewValue)) != LTRIM(RTRIM(@OldValue)) ) BEGIN SET @sql = 'INSERT INTO [dbo].[AuditDataChanges] ([TableName] ,[RecordPK] ,[ColumnName] ,[OldValue] ,[NewValue] ,[UpdatedBy]) VALUES (' + QUOTENAME(@TABLE_NAME, '''') + ' ,' + QUOTENAME(@RecordPk, '''') + ' ,' + QUOTENAME(@COLUMN_NAME, '''') + ' ,' + QUOTENAME(@OldValue, '''') + ' ,' + QUOTENAME(@NewValue, '''') + ' ,' + QUOTENAME(@UpdatedBy, '''') + ')'; EXEC (@sql); END; SET @COLUMN_NAME = ''; SET @NewValue = ''; SET @OldValue = ''; IF @Pos = 0 BEGIN SET @modifiedColumnsList = ''; END; ELSE BEGIN -- start substring at the character after the first comma SET @modifiedColumnsList = SUBSTRING(@modifiedColumnsList, @Pos + 1, LEN(@modifiedColumnsList) - @Pos); END; END; DROP TABLE #Inserted; DROP TABLE #deleted; GO



2
投票

-- get names of updated columns DECLARE @idTable INT declare @ColumnName nvarchar(300) declare @ColId int SELECT @idTable = T.id FROM sysobjects P JOIN sysobjects T ON P.parent_obj = T.id WHERE P.id = @@procid DECLARE @changedProperties nvarchar(max) = '' DECLARE @Columns_Updated VARCHAR(2000) = '' DECLARE @maxByteCU INT DECLARE @curByteCU INT SELECT @maxByteCU = DATALENGTH(COLUMNS_UPDATED()), @curByteCU = 1 WHILE @curByteCU <= @maxByteCU BEGIN DECLARE @cByte INT SET @cByte = SUBSTRING(COLUMNS_UPDATED(), @curByteCU, 1) DECLARE @curBit INT DECLARE @maxBit INT SELECT @curBit = 1, @maxBit = 8 WHILE @curBit <= @maxBit BEGIN IF CONVERT(BIT, @cByte & POWER(2, @curBit - 1)) <> 0 BEGIN SET @ColId = cast( CONVERT(VARCHAR, 8 * (@curByteCU - 1) + @curBit) as int) select @ColumnName = [Name] FROM syscolumns WHERE id = @idTable and colid = @ColId SET @Columns_Updated = @Columns_Updated + ',' + @ColumnName END SET @curBit = @curBit + 1 END SET @curByteCU = @curByteCU + 1 END



1
投票

Declare @sql nvarchar(4000) DECLARE @ParmDefinition nvarchar(500) Declare @OutString varchar(8000) Declare @tbl sysname Set @OutString = '' Set @tbl = 'SomeTable' --The table we are interested in --Store the contents of deleted in temp table Select * into #tempDelete from deleted --Build sql string based on definition --of table --to retrieve the column name --or empty string --based on comparison between --target table and temp table set @sql = '' Select @sql = @sql + 'Case when IsNull(i.[' + Column_Name + '],0) = IsNull(d.[' + Column_name + '],0) then '''' else ' + quotename(Column_Name, char(39)) + ' + '',''' + ' end +' from information_schema.columns where table_name = @tbl --Define output parameter set @ParmDefinition = '@OutString varchar(8000) OUTPUT' --Format sql set @sql = 'Select @OutString = ' + Substring(@sql,1 , len(@sql) -1) + ' From SomeTable i ' --Will need to be updated for target schema + ' inner join #tempDelete d on i.PK = d.PK ' --Will need to be updated for target schema --Execute sql and retrieve desired column list in output parameter exec sp_executesql @sql, @ParmDefinition, @OutString OUT drop table #tempDelete --strip trailing column if a non-zero length string --was returned if Len(@Outstring) > 0 Set @OutString = Substring(@OutString, 1, Len(@Outstring) -1) --return comma delimited list of changed columns. Select @OutString End



0
投票

请让我增强 Rick 的版本如下:

USE [AFC] GO /****** Object: Trigger [dbo].[trg_Survey_Identify_Updated_Columns] Script Date: 27/7/2018 14:08:49 ******/ SET ANSI_NULLS ON GO SET QUOTED_IDENTIFIER ON GO ALTER TRIGGER [dbo].[trg_Survey_Identify_Updated_Columns] ON [dbo].[Sample_Table] --Change to match your table name FOR INSERT ,UPDATE AS SET NOCOUNT ON; DECLARE @sql VARCHAR(5000) ,@sqlInserted NVARCHAR(500) ,@sqlDeleted NVARCHAR(500) ,@NewValue NVARCHAR(100) ,@OldValue NVARCHAR(100) ,@UpdatedBy VARCHAR(50) ,@ParmDefinitionD NVARCHAR(500) ,@ParmDefinitionI NVARCHAR(500) ,@TABLE_NAME VARCHAR(100) ,@COLUMN_NAME VARCHAR(100) ,@modifiedColumnsList NVARCHAR(4000) ,@ColumnListItem NVARCHAR(500) ,@Pos INT ,@RecordPk VARCHAR(50) ,@RecordPkName VARCHAR(50); SELECT * INTO #deleted FROM deleted; SELECT * INTO #Inserted FROM inserted; SET @TABLE_NAME = 'Sample_Table';---Change to your table name DECLARE t_cursor CURSOR FOR SELECT ContactID FROM inserted OPEN t_cursor FETCH NEXT FROM t_cursor INTO @RecordPk WHILE @@FETCH_STATUS = 0 BEGIN --SELECT @UpdatedBy = Surname --Change to your column name for the user update field --FROM inserted; --SELECT @RecordPk = ContactID --Change to the table primary key field --FROM inserted; SET @RecordPkName = 'ContactID'; SET @modifiedColumnsList = STUFF(( SELECT ',' + name FROM sys.columns WHERE object_id = OBJECT_ID(@TABLE_NAME) AND SUBSTRING(COLUMNS_UPDATED(), ((column_id - 1) / 8 + 1), 1) & (POWER(2, ((column_id - 1) % 8 + 1) - 1)) = POWER(2, (column_id - 1) % 8) FOR XML PATH('') ), 1, 1, ''); WHILE LEN(@modifiedColumnsList) > 0 BEGIN SET @Pos = CHARINDEX(',', @modifiedColumnsList); IF @Pos = 0 BEGIN SET @ColumnListItem = @modifiedColumnsList; END; ELSE BEGIN SET @ColumnListItem = SUBSTRING(@modifiedColumnsList, 1, @Pos - 1); END; SET @COLUMN_NAME = @ColumnListItem; SET @ParmDefinitionD = N'@OldValueOut NVARCHAR(100) OUTPUT'; SET @ParmDefinitionI = N'@NewValueOut NVARCHAR(100) OUTPUT'; SET @sqlDeleted = N'SELECT @OldValueOut=' + @COLUMN_NAME + ' FROM #deleted where ' + @RecordPkName + '=' + CONVERT(VARCHAR(50), @RecordPk); SET @sqlInserted = N'SELECT @NewValueOut=' + @COLUMN_NAME + ' FROM #Inserted where ' + @RecordPkName + '=' + CONVERT(VARCHAR(50), @RecordPk); EXECUTE sp_executesql @sqlDeleted ,@ParmDefinitionD ,@OldValueOut = @OldValue OUTPUT; EXECUTE sp_executesql @sqlInserted ,@ParmDefinitionI ,@NewValueOut = @NewValue OUTPUT; --PRINT @newvalue --PRINT @oldvalue IF (LTRIM(RTRIM(@NewValue)) != LTRIM(RTRIM(@OldValue))) BEGIN SET @sql = 'INSERT INTO [dbo].[AuditDataChanges] ([TableName] ,[RecordPK] ,[ColumnName] ,[OldValue] ,[NewValue] ) VALUES (' + QUOTENAME(@TABLE_NAME, '''') + ' ,' + QUOTENAME(@RecordPk, '''') + ' ,' + QUOTENAME(@COLUMN_NAME, '''') + ' ,' + QUOTENAME(@OldValue, '''') + ' ,' + QUOTENAME(@NewValue, '''') + ' ' + ')'; EXEC (@sql); END; SET @COLUMN_NAME = ''; SET @NewValue = ''; SET @OldValue = ''; IF @Pos = 0 BEGIN SET @modifiedColumnsList = ''; END; ELSE BEGIN -- start substring at the character after the first comma SET @modifiedColumnsList = SUBSTRING(@modifiedColumnsList, @Pos + 1, LEN(@modifiedColumnsList) - @Pos); END; END; FETCH NEXT FROM t_cursor INTO @RecordPk END DROP TABLE #Inserted; DROP TABLE #deleted; CLOSE t_cursor; DEALLOCATE t_cursor;


0
投票
这是使用唯一记录和 UpdatedBy 用户跟踪更新的列值的日志的完美示例。

IF NOT EXISTS (SELECT * FROM sysobjects WHERE id = OBJECT_ID(N'[dbo].[ColumnAuditLogs]') AND OBJECTPROPERTY(id, N'IsUserTable') = 1) CREATE TABLE ColumnAuditLogs (Type CHAR(1), TableName VARCHAR(128), PK VARCHAR(1000), FieldName VARCHAR(128), OldValue VARCHAR(1000), NewValue VARCHAR(1000), UpdateDate datetime, UserName VARCHAR(128), UniqueId uniqueidentifier, UpdatedBy int ) GO create TRIGGER TR_ABCTable_AUDIT ON ABCTable 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), @UniqueId varchar(100), @UpdatedBy VARCHAR(50) --You will need to change @TableName to match the table to be audited. -- Here we made ABCTable for your example. SELECT @TableName = 'ABCTable' -- change table name accoring your table name -- use for table unique records for everytime updation. set @UniqueId = CONVERT(varchar(100),newID()) -- date and user SELECT @UserName = SYSTEM_USER , @UpdateDate = CONVERT (NVARCHAR(30),GETDATE(),126) SELECT @UpdatedBy = ModifiedBy --Change to your column name for the user update field FROM inserted; -- 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+'+','') + '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 ColumnAuditLogs ( Type, TableName, PK, FieldName, OldValue, NewValue, UpdateDate, UserName, UniqueId, [UpdatedBy]) select ''' + @Type + ''',''' + @TableName + ''',' + @PKSelect + ',''' + @fieldname + '''' + ',convert(varchar(1000),d.' + @fieldname + ')' + ',convert(varchar(1000),i.' + @fieldname + ')' + ',''' + @UpdateDate + '''' + ',''' + @UserName + '''' + ',''' + @UniqueId + '''' + ',' + QUOTENAME(@UpdatedBy, '''') + ' 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



0
投票

ALTER TRIGGER [dbo].[LogRecordHistory] ON [dbo].[MedicalRecords] AFTER UPDATE AS BEGIN -- SET NOCOUNT ON added to prevent extra result sets from -- interfering with SELECT statements. SET NOCOUNT ON; DECLARE @columnUpdated VARCHAR(50) DECLARE @query varchar(4000) -- Get columns updated DECLARE curColumnsUpdated CURSOR LOCAL FOR SELECT name FROM syscolumns WHERE id = OBJECT_ID('dbo.MedicalRecords') AND CONVERT(VARBINARY,REVERSE(COLUMNS_UPDATED())) & POWER(CONVERT(BIGINT, 2), colorder - 1) > 0; -- Transfer the inserted and deleted tables to temp tables in order to be used in a dynamic query. SELECT * INTO #tempInserted FROM INSERTED SELECT * INTO #tempDeleted FROM DELETED OPEN curColumnsUpdated; FETCH NEXT FROM curColumnsUpdated INTO @columnUpdated; WHILE @@FETCH_STATUS = 0 BEGIN -- set the select statement to insert into the HistoryLogs table SET @query = 'SELECT NEWID(), GETUTCDATE(), i.id, i.[UpdatedBy],'''+@columnUpdated +''', d.'+@columnUpdated+', i.'+@columnUpdated +' FROM #tempInserted i INNER JOIN #tempDeleted d ON i.ID = d.ID WHERE d.'+@columnUpdated +' is null or i.'+@columnUpdated+' is null or trim(d.'+@columnUpdated +') != trim(i.'+@columnUpdated+')'; INSERT INTO [dbo].[HistoryLogs] ([Id], [DateAddedUTC], [RecordId], [UserId], [FieldChanged], [OriginalData], [NewData]) EXEC (@query) FETCH NEXT FROM curColumnsUpdated INTO @columnUpdated; END; CLOSE curColumnsUpdated; DROP TABLE #tempInserted DROP TABLE #tempDeleted END

	
© www.soinside.com 2019 - 2024. All rights reserved.