This is a difficult situation. The trigger you have created is trying to deal with a situation that I find to be very painful and not very useful in the long run. This Audit table will hold values for a given column along with the date it was modified. Here is the real issue with this type of design. Consider being asked to figure out all the values for a given primary key value on a given date. Think about how difficult this is going to be when you have a table with only 10 o 15 columns. You are going to have to write a big ugly nasty query just to determine the values for each column. Consider a much wider table and how difficult that is going to be.
Now lets examine the actual trigger. There are some major issues with this trigger. Performance is going to be a serious issue here because of the complexity of what you are doing. You have built a while loop inside this trigger to insert a new row into the audit table for each column in CER_SEDA_DOMAIN_A. Looping in t-sql is almost always very slow.
If this was a system I was working with I would consider rethinking the audit structure here and this can become a pretty simple process.
For anybody else stumbling in here I formatted the posted code so we can see it easier.
ALTER TRIGGER tr_trigtest ON dbo.CER_SEDA_DOMAIN_A
FOR INSERT
,UPDATE
,DELETE
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.CER_SEDA_DOMAIN_A'
-- date and user
SELECT @UserName = system_user
,@UpdateDate = convert(VARCHAR(8), getdate(), 112) + ' ' + convert(VARCHAR(12), getdate(), 114)
-- 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 CMMonitor.[dbo].Audit (Type, TableName, PK, FieldName, OldValue, NewValue, UpdateDate, UserName)'
SELECT @sql = @sql + ' select ''' + @Type + ''''
SELECT @sql = @sql + ',''' + @TableName + ''''
SELECT @sql = @sql + ',' + @PKSelect
SELECT @sql = @sql + ',''' + @fieldname + ''''
SELECT @sql = @sql + ',convert(varchar(1000),d.' + @fieldname + ')'
SELECT @sql = @sql + ',convert(varchar(1000),i.' + @fieldname + ')'
SELECT @sql = @sql + ',''' + @UpdateDate + ''''
SELECT @sql = @sql + ',''' + @UserName + ''''
SELECT @sql = @sql + ' from #ins i full outer join #del d'
SELECT @sql = @sql + @PKCols
SELECT @sql = @sql + ' where i.' + @fieldname + ' <> d.' + @fieldname
SELECT @sql = @sql + ' or (i.' + @fieldname + ' is null and d.' + @fieldname + ' is not null)'
SELECT @sql = @sql + ' or (i.' + @fieldname + ' is not null and d.' + @fieldname + ' is null)'
EXEC (@sql)
END
END
GO
_______________________________________________________________
Need help? Help us help you.
Read the article at http://www.sqlservercentral.com/articles/Best+Practices/61537/ for best practices on asking questions.
Need to split a string? Try Jeff Modens splitter http://www.sqlservercentral.com/articles/Tally+Table/72993/.
Cross Tabs and Pivots, Part 1 – Converting Rows to Columns - http://www.sqlservercentral.com/articles/T-SQL/63681/
Cross Tabs and Pivots, Part 2 - Dynamic Cross Tabs - http://www.sqlservercentral.com/articles/Crosstab/65048/
Understanding and Using APPLY (Part 1) - http://www.sqlservercentral.com/articles/APPLY/69953/
Understanding and Using APPLY (Part 2) - http://www.sqlservercentral.com/articles/APPLY/69954/