Sql Server. Triggers 2 – Creating Audit Trails – Author Nigel Rivett
— Triggers 2 – Creating Audit Trails
Author Nigel RivettCreating Audit Trails
Author: Nigel RivettObjective
The objective of an audit trail is to log changes to data in tables.This article deals with two ways of creating an audit trail:
1. Audit trail table for each table to be audit trailed
Create a copy of the table and write copies of the rows to it.
For this method it is usual to write the old data only as the new data is in the main table and to write an entry whether or not anything is actaully changed.
This can be implemented in triggers or in the stored procedures that update the data.
This method also does not require a primary key as it is just saving the before versions of rows updated.2. Single audit trail table
Create a table to log the table name, field name abd old and new versions of the data.
For this method it is usual to log both old and new versions of the data and only those fields that have changed.
To implement this in triggeres it is a requirement that either there is a primary key on the table or only single rows are updated.Note that text and image columns are not available in the insertd/deleted tables to after triggers so these cannot be saved by this method.
Test table to be audit trailed.
if exists (select * from sysobjects where id = object_id(N'[dbo].[trigtest]’) and OBJECTPROPERTY(id, N’IsUserTable’) = 1)
drop table [dbo].[trigtest]
go
create table trigtest (i_int_key int not null, j_int_key int not null, s_varchar varchar(10), t_char varchar(10), d_date datetime)
go
alter table trigtest add constraint pk primary key (i_int_key, j_int_key)
goTest updates
insert trigtest (i_int_key, j_int_key, s_varchar, t_char, d_date)
select 1, 1, ‘hello’, ‘goodbye’, ‘20000101’
insert trigtest (i_int_key, j_int_key, s_varchar, t_char, d_date)
select 2, 1, ‘hello’, ‘goodbye’, ‘20000101’
update trigtest set s_varchar = ‘helloupd’ where i_int_key = 1
update trigtest set t_char = ‘goodbyeupd’, d_date = ‘20000102’ where i_int_key = 1
update trigtest set t_char = null, d_date = null where i_int_key = 1
update trigtest set t_char = ‘good’, d_date = ‘20000103’ where i_int_key = 1
delete trigtest where i_int_key = 11. Audit trail table for each table to be audited
Audit trail table
if exists (select * from sysobjects where id = object_id(N'[dbo].[trigtest_au]’) and OBJECTPROPERTY(id, N’IsUserTable’) = 1)
drop table [dbo].[trigtest_au]
go
create table trigtest_au (i_int_key int not null, j_int_key int not null, s_varchar varchar(10), t_char varchar(10), d_date datetime, UpdateDate datetime, UserName varchar(128), type varchar(10))
goTrigger to create the audit trail for the table
To only save only the old copy of the data
if exists (select * from dbo.sysobjects where id = object_id(N'[dbo].[tr_au_trigtest]’) and OBJECTPROPERTY(id, N’IsTrigger’) = 1)
drop trigger [dbo].[tr_au_trigtest]
go
create trigger tr_au_trigtest on trigtest for update, delete
as
declare @type varchar(1) ,
@UpdateDate datetime ,
@UserName varchar(128)
if exists (select * from inserted)
select @type = ‘U’
else
select @type = ‘D’select @UpdateDate = getdate() ,
@UserName = system_userinsert trigtest_au (i_int_key, j_int_key, s_varchar, t_char, d_date, UpdateDate, UserName, type)
select i_int_key, j_int_key, s_varchar, t_char, d_date, @UpdateDate, @UserName, @type + ‘_old’
from deleted
goTo save the old and new copy of the data
if exists (select * from dbo.sysobjects where id = object_id(N'[dbo].[tr_au_trigtest]’) and OBJECTPROPERTY(id, N’IsTrigger’) = 1)
drop trigger [dbo].[tr_au_trigtest]
go
create trigger tr_au_trigtest on trigtest for insert, update, delete
as
declare @type varchar(1) ,
@UpdateDate datetime ,
@UserName varchar(128)
if exists (select * from inserted) and exists (select * from deleted)
select @type = ‘U’
else if exists (select * from inserted)
select @type = ‘I’
else
select @type = ‘D’select @UpdateDate = getdate() ,
@UserName = system_userinsert trigtest_au (i_int_key, j_int_key, s_varchar, t_char, d_date, UpdateDate, UserName, type)
select i_int_key, j_int_key, s_varchar, t_char, d_date, @UpdateDate, @UserName, @type + ‘_old’
from deleted
insert trigtest_au (i_int_key, j_int_key, s_varchar, t_char, d_date, UpdateDate, UserName, type)
select i_int_key, j_int_key, s_varchar, t_char, d_date, @UpdateDate, @UserName, @type + ‘_new’
from inserted
go2. Single Audit Trail table.
Audit trail table
if exists (select * from sysobjects where id = object_id(N'[dbo].[trigtest_au]’) and OBJECTPROPERTY(id, N’IsUserTable’) = 1)
drop table [dbo].[trigtest_au]
go
create table trigtest_au (TableName varchar(128), FieldName varchar(128), OldValue varchar(1000), NewValue varchar(1000), UpdateDate datetime, UserName varchar(128), type varchar(1))
goTrigger to save old and new values of fields that have changed
if exists (select * from dbo.sysobjects where id = object_id(N'[dbo].[tr_au_trigtest]’) and OBJECTPROPERTY(id, N’IsTrigger’) = 1)
drop trigger [dbo].[tr_au_trigtest]
go
create trigger tr_au_trigtest on trigtest for insert, update, delete
as
declare @type varchar(1) ,
@UpdateDate datetime ,
@UserName varchar(128)
if exists (select * from inserted) and exists (select * from deleted)
select @type = ‘U’
else if exists (select * from inserted)
select @type = ‘I’
else
select @type = ‘D’select @UpdateDate = getdate() ,
@UserName = system_userif update (i_int_key) or @type = ‘D’
insert trigtest_au (TableName, PK, FieldName, OldValue, NewValue, UpdateDate, UserName, type)
select ‘trigtest’, convert(varchar(20),coalesce(d.i_int_key,i.i_int_key)) + ‘|’ + convert(varchar(20),coalesce(d.j_int_key,i.j_int_key)), ‘i_int_key’, convert(varchar(1000),d.i_int_key), convert(varchar(1000),i.i_int_key), @UpdateDate, @UserName, @type
from inserted i
full outer join deleted d
on i.i_int_key = d.i_int_key
and i.j_int_key = d.j_int_key
where (i.i_int_key <> d.i_int_key or (i.i_int_key is null and d.i_int_key is not null) or (i.i_int_key is not null and d.i_int_key is null))
if update (j_int_key) or @type = ‘D’
insert trigtest_au (TableName, PK, FieldName, OldValue, NewValue, UpdateDate, UserName, type)
select ‘trigtest’, convert(varchar(20),coalesce(d.i_int_key,i.i_int_key)) + ‘|’ + convert(varchar(20),coalesce(d.j_int_key,i.j_int_key)), ‘j_int_key’, convert(varchar(1000),d.j_int_key), convert(varchar(1000),i.j_int_key), @UpdateDate, @UserName, @type
from inserted i
full outer join deleted d
on i.i_int_key = d.i_int_key
and i.j_int_key = d.j_int_key
where (i.j_int_key <> d.j_int_key or (i.j_int_key is null and d.j_int_key is not null) or (i.j_int_key is not null and d.j_int_key is null))
if update (s_varchar) or @type = ‘D’
insert trigtest_au (TableName, PK, FieldName, OldValue, NewValue, UpdateDate, UserName, type)
select ‘trigtest’, convert(varchar(20),coalesce(d.i_int_key,i.i_int_key)) + ‘|’ + convert(varchar(20),coalesce(d.j_int_key,i.j_int_key)), ‘s_varchar’, convert(varchar(1000),d.s_varchar), convert(varchar(1000),i.s_varchar), @UpdateDate, @UserName, @type
from inserted i
full outer join deleted d
on i.i_int_key = d.i_int_key
and i.j_int_key = d.j_int_key
where (i.s_varchar <> d.s_varchar or (i.s_varchar is null and d.s_varchar is not null) or (i.s_varchar is not null and d.s_varchar is null))
if update (t_char) or @type = ‘D’
insert trigtest_au (TableName, PK, FieldName, OldValue, NewValue, UpdateDate, UserName, type)
select ‘trigtest’, convert(varchar(20),coalesce(d.i_int_key,i.i_int_key)) + ‘|’ + convert(varchar(20),coalesce(d.j_int_key,i.j_int_key)), ‘t_char’, convert(varchar(1000),d.t_char), convert(varchar(1000),i.t_char), @UpdateDate, @UserName, @type
from inserted i
full outer join deleted d
on i.i_int_key = d.i_int_key
and i.j_int_key = d.j_int_key
where (i.t_char <> d.t_char or (i.t_char is null and d.t_char is not null) or (i.t_char is not null and d.t_char is null))
if update (d_date) or @type = ‘D’
insert trigtest_au (TableName, PK, FieldName, OldValue, NewValue, UpdateDate, UserName, type)
select ‘trigtest’, convert(varchar(20),coalesce(d.i_int_key,i.i_int_key)) + ‘|’ + convert(varchar(20),coalesce(d.j_int_key,i.j_int_key)), ‘d_date’, convert(char(9),d.d_date,112) + convert(char(8),d.d_date,8), convert(char(9),i.d_date,112) + convert(char(8),i.d_date,8), @UpdateDate, @UserName, @type
from inserted i
full outer join deleted d
on i.i_int_key = d.i_int_key
and i.j_int_key = d.j_int_key
where (i.d_date <> d.d_date or (i.d_date is null and d.d_date is not null) or (i.d_date is not null and d.d_date is null))
goThis gives the following result
select TableName = convert(char(12),TableName), PK = convert(char(6),PK), FieldName = convert(char(16),FieldName),
OldValue = convert(char(20),OldValue), NewValue = convert(char(20), NewValue),
UpdateDate = convert(char(9),UpdateDate,112)+convert(char(10),UpdateDate,8), UserName = convert(char(10),UserName), type
from trigtest_auTableName PK FieldName OldValue NewValue UpdateDate UserName type
———— —— —————- ——————– ——————– ——————- ———- —-
trigtest 1|1 i_int_key NULL 1 20030330 04:00:14 sa I
trigtest 1|1 j_int_key NULL 1 20030330 04:00:14 sa I
trigtest 1|1 s_varchar NULL hello 20030330 04:00:14 sa I
trigtest 1|1 t_char NULL goodbye 20030330 04:00:14 sa I
trigtest 1|1 d_date NULL 20000101 00:00:00 20030330 04:00:14 sa I
trigtest 2|1 i_int_key NULL 2 20030330 04:00:14 sa I
trigtest 2|1 j_int_key NULL 1 20030330 04:00:14 sa I
trigtest 2|1 s_varchar NULL hello 20030330 04:00:14 sa I
trigtest 2|1 t_char NULL goodbye 20030330 04:00:14 sa I
trigtest 2|1 d_date NULL 20000101 00:00:00 20030330 04:00:14 sa I
trigtest 1|1 s_varchar hello helloupd 20030330 04:00:14 sa U
trigtest 1|1 t_char goodbye goodbyeupd 20030330 04:00:14 sa U
trigtest 1|1 d_date 20000101 00:00:00 20000102 00:00:00 20030330 04:00:14 sa U
trigtest 1|1 t_char goodbyeupd NULL 20030330 04:00:14 sa U
trigtest 1|1 d_date 20000102 00:00:00 NULL 20030330 04:00:14 sa U
trigtest 1|1 t_char NULL good 20030330 04:00:14 sa U
trigtest 1|1 d_date NULL 20000103 00:00:00 20030330 04:00:14 sa U
trigtest 1|1 i_int_key 1 NULL 20030330 04:00:14 sa D
trigtest 1|1 j_int_key 1 NULL 20030330 04:00:14 sa D
trigtest 1|1 s_varchar helloupd NULL 20030330 04:00:14 sa D
trigtest 1|1 t_char good NULL 20030330 04:00:14 sa D
trigtest 1|1 d_date 20000103 00:00:00 NULL 20030330 04:00:14 sa D