Reputation: 33
I am trying to log data changes in MS SQL with trigger. I want to create a new History table in every month. After I found the answer how to change table name Dynamically I can't access the DELETED and INSERTED tables anymore. It says invalid object name.
ALTER TRIGGER [dbo].[teszttablatrigger] ON [teszt].[dbo].[teszt] FOR DELETE, INSERT, UPDATE AS
declare @hist nvarchar(40)
set @hist='teszthistory_' + CAST(YEAR(getdate()) as NCHAR(4))+ '_' + (case when Month(GETDATE())<10 then '0' + CAST (Month(GETDATE()) as NCHAR(1))
when Month(GETDATE())>=10 then CAST (Month(GETDATE()) as NCHAR(2)) end)
declare @DynamicSql1 nvarchar(2000)
declare @DynamicSql2 nvarchar(2000)
set @DynamicSql1 = N'IF NOT EXISTS (SELECT * FROM sysobjects WHERE id = object_id(N''[History][dbo].[@hist]'')
AND OBJECTPROPERTY(id, N''IsUserTable'') = 1)
CREATE TABLE [History].[dbo].[@hist] ( kulcs int, szoveg varchar(40), modtip varchar(40), datum datetime default getdate())'
Exec sp_executesql @DynamicSql1, N'@hist nvarchar(40)', @hist=@hist
set @DynamicSql2 = N'INSERT INTO [History].[dbo].[@hist] (kulcs, szoveg, modtip)
SELECT kulcs, szoveg, ''delete''
FROM DELETED
INSERT INTO [History].[dbo].[@hist] (kulcs, szoveg, modtip)
SELECT kulcs, szoveg, ''insert''
FROM INSERTED'
Exec sp_executesql @DynamicSql2, N'@hist nvarchar(40)', @hist=@hist
Thanks for the answers in advance.
Upvotes: 1
Views: 237
Reputation: 1
If you have SQL Server enterprise (check your version) Then better way will be to enable CDC.
https://msdn.microsoft.com/en-us/library/cc645937(v=sql.110).aspx
Upvotes: 0
Reputation: 33
Tanks for the answer @MtwStark. Now it works, I can check if the table exists, and create it if not. And have eaccess to the DELETED and INSERTED tables. I'm not sure, if in your solution I have to create the test_history_9999_99 table in advance. Because when I used your trigger I've got an error about column insertion (I didn't understand the error completly).
Now my code looks like this. I'm not sure if it can handle INSERT/UPDATE many records with a single operation. Probably I still need to insert this code for it? CAST(@guid AS varchar(64)) BATCH . I'm not sure what it really does, I have to look into it deeper.
CREATE TRIGGER [dbo].[teszttablatrigger] ON [teszt].[dbo].[teszt] FOR DELETE, INSERT, UPDATE AS
declare @hist nvarchar(40)
set @hist='teszthistory_' + CAST(YEAR(getdate()) as NCHAR(4))+ '_' + (case when Month(GETDATE())<10 then '0' + CAST (Month(GETDATE()) as NCHAR(1))
when Month(GETDATE())>=10 then CAST (Month(GETDATE()) as NCHAR(2)) end)
select * into #ins from inserted
select * into #del from deleted
declare @DynamicSql nvarchar(2000)
DECLARE @T2 BIT = 0
SELECT top 1 @T2 = 1 FROM sys.tables WHERE [TYPE] = 'U' AND name = @hist
if @T2=0 begin
set @DynamicSql = N'CREATE TABLE [' + @hist + '] ( kulcs int, szoveg varchar(40), modtip varchar(40), datum datetime default getdate())'
Exec sp_executesql @DynamicSql
end
set @DynamicSql = N'INSERT INTO ' + @hist + ' (kulcs, szoveg, modtip)
SELECT kulcs, szoveg, ''delete''
FROM #del
INSERT INTO ' + @hist + ' (kulcs, szoveg, modtip)
SELECT kulcs, szoveg, ''insert''
FROM #ins'
Exec sp_executesql @DynamicSql
Upvotes: 1
Reputation: 4058
Dynamic sql is executed in his own scope, so you can't acces inserted/deleted objects.
You could write a SQLCLR trigger in C# look this example SQLCLR Trigger but I think the easiest way is to use a temp table to write changes to, so the dynamic part is fixed.
Take a look:
DROP TRIGGER [test_history]
GO
CREATE TRIGGER [test_history] ON [test_table]
FOR DELETE, INSERT, UPDATE
AS
BEGIN
declare @date datetime = getdate()
declare @guid uniqueidentifier = newid()
declare @hist nvarchar(40)= 'test_history_' + CAST(YEAR(@date ) as VARCHAR(4))+ '_' + right('0' + CAST(Month(@date) as VARCHAR(2)), 2)
DECLARE @T1 BIT = 0
SELECT top 1 @T1 = 1 FROM sys.tables WHERE [TYPE] = 'U' AND name = 'test_history_9999_99'
IF @T1 = 1 TRUNCATE table test_history_9999_99
DECLARE @T2 BIT = 0
SELECT top 1 @T2 = 1 FROM sys.tables WHERE [TYPE] = 'U' AND name = @hist
IF @T1=0 BEGIN
SELECT ID, [desc], @date DATE_TIME, cast('delete' as varchar(20)) as operation, CAST(@guid AS varchar(64)) BATCH
INTO test_history_9999_99
FROM DELETED
END else begin
INSERT INTO test_history_9999_99
SELECT ID, [desc], @date, cast('delete' as varchar(20)) as operation, CAST(@guid AS varchar(64)) BATCH
FROM DELETED
end
INSERT INTO test_history_9999_99
SELECT ID, [desc], @date, cast('insert' as varchar(20)) as operation, CAST(@guid AS varchar(64)) BATCH
FROM inserted
IF @T2 = 0 BEGIN
EXEC sp_rename 'test_history_9999_99', @hist
END ELSE BEGIN
declare @DynamicSql nvarchar(2000)
SET @DynamicSql = 'INSERT INTO ' + @hist + ' SELECT * FROM test_history_9999_99;'
Exec sp_executesql @DynamicSql
END
END
My test_table contains only two columns ID and [Desc].
In the history tables I have added a DATETIME
column with change date and a UNIQUEIDENTIFIER
column so you can group all changes in a batch if you INSERT/UPDATE
many records with a single operation
Upvotes: 1
Reputation: 29
Try refreshing intellisense. Ctrl+Shift+R see if that might help. Or do a database table refresh.
Upvotes: 0