Jaster, Finally i got a solution for your query.
Its a dynamic script ,where you can mention the name of required tables. The script will manage the data change on the mentioned tables table across all columns (except columns with datatype text,ntext and image) by creating dynamic triggers which will get fired on INSERT/UPDATE/DELETE
actions on these tables. You can change the actions as per your requirement.I also included TableName
and ModifiedDate
in your HISTORY table for an easy look up.
So here are the steps to your solution.
Creating sample test environment using the provided Information
IF NOT EXISTS(SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME= 'History')
CREATE TABLE [dbo].[History](
[Id] [uniqueidentifier] NOT NULL CONSTRAINT [DF_History_Id] DEFAULT (newsequentialid()),
[Tablename] VARCHAR(150),
[ObjectId] VARCHAR(1500) NOT NULL,
[Timestamp] [datetime] NOT NULL CONSTRAINT [DF_History_Timestamp] DEFAULT (getdate()),
[ChangingUser] [varchar](max) NOT NULL CONSTRAINT [DF_History_ChangingUser] DEFAULT (suser_sname()),
[Column] [varchar](max) NOT NULL,
[OldValue] [nvarchar](max) NULL,
[NewValue] [nvarchar](max) NULL,
[ModifiedDate] Datetime NULL
CONSTRAINT [PK_History] PRIMARY KEY CLUSTERED
(
[Id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]
IF NOT EXISTS(SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME= 'T1')
BEGIN
CREATE TABLE [dbo].[T1](
[Id] [uniqueidentifier] NOT NULL CONSTRAINT [DF_T1_Id] DEFAULT (newsequentialid()),
[Title] text NULL,
[Amount] [int] NULL,
[Price] [decimal](18, 7) NULL,
[Version] [datetime] NOT NULL,
CONSTRAINT [PK_T1] PRIMARY KEY CLUSTERED
(
[Id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
INSERT INTO [T1] ([Title],[Amount],[Price],[Version]) VALUES ('Abc',3,372500000 ,'2016-09-01 11:49:33.473'),
(NULL,1,273000000 ,'2016-09-02 11:49:33.473'),
('Storm Catcher',10,NULL,'2016-09-01 11:49:33.473')
END
In the below temporary table ,you have to insert the table names for which wanted to track the data changes.
IF OBJECT_ID('tempdb..#FiltedTableList') IS NOT NULL
DROP TABLE #FiltedTableList
GO
CREATE TABLE #FiltedTableList
( ID INT IDENTITY(1,1),
TableName VARCHAR(150))
INSERT INTO #FiltedTableList (TableName)
SELECT 'T1' --here we have only one table to track the changes
Here comes the final step. execute the below dynamic query.
DECLARE @TableName sysname,@Counter INT,@Dynamsql varchar(MAX)
SELECT @Counter=MAX(ID) FROM #FiltedTableList
SET NOCOUNT ON
WHILE (@Counter !=0)
BEGIN
SELECT @TableName= TableName
FROM #FiltedTableList
WHERE ID=@Counter
DECLARE @ColumnFilter VARCHAR (MAX)
SELECT @ColumnFilter=Stuff(( Select ', ' + C.COLUMN_NAME
From INFORMATION_SCHEMA.COLUMNS As C Where C.TABLE_NAME = @TableName
AND c.DATA_TYPE NOT IN ('text','ntext','image')
Order By C.ORDINAL_POSITION For Xml Path('') ), 1, 2, '')
IF OBJECT_ID('tempdb..##MagInserted') IS NOT NULL
DROP TABLE ##MagInserted
IF OBJECT_ID('tempdb..##MagDeleted') IS NOT NULL
DROP TABLE ##MagDeleted
EXEC('IF OBJECT_ID (''' + @TableName+ '_LogTracker'', ''TR'') IS NOT NULL DROP TRIGGER ' + @TableName+ '_LogTracker')
SELECT @Dynamsql =
'CREATE TRIGGER ' + @TableName+ '_LogTracker ON ' + @TableName+ ' FOR INSERT, UPDATE, DELETE
AS
DECLARE @column int , @maxColumn int , @char int,@columnname varchar(128) , @TableName varchar(128) , @KeyColumn varchar(1000) , @Dynamsql varchar(2000) ,@dataquery VARCHAR(MAX),
@ModifiedDate varchar(21), @UserName varchar(128) , @key int , @Type char(1) , @PKFieldSelect varchar(1000),@PKValueSelect varchar(1000)
SELECT @TableName = ''' + @TableName+ ''';
SELECT @UserName = system_user , @ModifiedDate = convert(varchar(8), getdate(), 112) + '' '' + convert(varchar(12), getdate(), 114);
SELECT '+ @ColumnFilter+' INTO ##MagInserted FROM inserted;SELECT '+ @ColumnFilter+' INTO ##MagDeleted FROM deleted;
SELECT @KeyColumn = COALESCE(@KeyColumn + '' and'', '' on'') + '' i.'' + c.COLUMN_NAME + '' = d.'' + c.COLUMN_NAME
FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS kc JOIN INFORMATION_SCHEMA.KEY_COLUMN_USAGE c ON c.TABLE_NAME = kc.TABLE_NAME and c.CONSTRAINT_NAME = kc.CONSTRAINT_NAME
WHERE kc.TABLE_NAME = @TableName AND CONSTRAINT_TYPE = ''PRIMARY KEY''
SELECT @PKFieldSelect = COALESCE(@PKFieldSelect+''+'','''') + '''''''' + COLUMN_NAME + ''''''''
FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS kc JOIN INFORMATION_SCHEMA.KEY_COLUMN_USAGE c ON c.TABLE_NAME = kc.TABLE_NAME and c.CONSTRAINT_NAME = kc.CONSTRAINT_NAME
WHERE kc.TABLE_NAME = @TableName AND CONSTRAINT_TYPE = ''PRIMARY KEY''
SELECT @PKValueSelect = coalesce(@PKValueSelect+''+'','''') + ''convert(varchar(100), coalesce(i.'' + COLUMN_NAME + '',d.'' + COLUMN_NAME + ''))''
FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS kc JOIN INFORMATION_SCHEMA.KEY_COLUMN_USAGE c ON c.TABLE_NAME = kc.TABLE_NAME and c.CONSTRAINT_NAME = kc.CONSTRAINT_NAME
WHERE kc.TABLE_NAME = @TableName AND CONSTRAINT_TYPE = ''PRIMARY KEY''
SELECT @column = 0 , @maxColumn = max(ORDINAL_POSITION) FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = @TableName
WHILE @column < @maxColumn
BEGIN
SELECT @column = min(ORDINAL_POSITION) FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = @TableName AND ORDINAL_POSITION > @column
SELECT @key = (@column - 1 )% 8 + 1; SELECT @key = power(2,@key - 1);SELECT @char = ((@column - 1) / 8) + 1
IF SUBSTRING(COLUMNS_UPDATED(),@char, 1) & @key > 0 OR @Type IN (''I'',''D'')
BEGIN
SELECT @columnname = COLUMN_NAME from INFORMATION_SCHEMA.COLUMNS where TABLE_NAME = @TableName and ORDINAL_POSITION = @column
SELECT @Dynamsql = ''insert History (TableName,[ObjectId], [Column], OldValue, NewValue, ModifiedDate, ChangingUser)''
SELECT @Dynamsql = @Dynamsql + '' select '''''' + @TableName + ''''''''
SELECT @Dynamsql = @Dynamsql + '','' + @PKValueSelect
SELECT @Dynamsql = @Dynamsql + '','''''' + @columnname + ''''''''
SELECT @Dynamsql = @Dynamsql + '',convert(varchar(1000),d.'' + @columnname + '')''
SELECT @Dynamsql = @Dynamsql + '',convert(varchar(1000),i.'' + @columnname + '')''
SELECT @Dynamsql = @Dynamsql + '','''''' + @ModifiedDate + ''''''''
SELECT @Dynamsql = @Dynamsql + '','''''' + @UserName + ''''''''
SELECT @Dynamsql = @Dynamsql + '' from ##MagInserted i full outer join ##MagDeleted d''
SELECT @Dynamsql = @Dynamsql + @KeyColumn
SELECT @Dynamsql = @Dynamsql + '' where i.'' + @columnname + '' <> d.'' + @columnname
SELECT @Dynamsql = @Dynamsql + '' or (i.'' + @columnname + '' is null and d.'' + @columnname + '' is not null)''
SELECT @Dynamsql = @Dynamsql + '' or (i.'' + @columnname + '' is not null and d.'' + @columnname + '' is null)''
EXEC (@Dynamsql)
END END '
SELECT @Dynamsql
EXEC(@Dynamsql)
SET @Counter=@Counter-1
END
So here is the test case and output :
