

USE AdventureWorks2008 -- edit for your database

SET ANSI_NULL_DFLT_ON ON
SET ANSI_NULLS ON
SET ANSI_PADDING ON
SET QUOTED_IDENTIFIER ON



/* 

AutoAudit script
Paul Nielsen
www.sqlserverbible.com


----------------------------------
version 1.00 - Jan 15, 2007

This script creates the audit table and builds 4 stored procedures. 

AutoAudit 'schema', 'table'
which: 
   1) creates dbo.audit  table if not already there
   2) adds created and modified columns to table
   3) builds insert, update, and modified audit trail trigger

AutoAditDrop 'schema', 'table'
which: 
1)	drops insert, update, and modified audit trail trigger

AutoAuditAll  
AuditDropAll
1)	runs the autoaudit or autoauditdrop for every table in the database other than the audit table. 

This is only a version 1, so test before you put it in production. 
Im open to suggestions and requests. 

This version is limited to tables with single column primary keys. 
But thats easy to change if enough readers ask for it. Also, in the next version Im going to add error handling. 

-----------------------------------
version 1.01 - Jan 15, 2007
   added row version column, incremented by the modified trigger
   cleaned up how the tablename is written to the audit.tablename column 
   added delete trigger, which just writes the table, pk, and operation ('d') to the audit table
   changed Audit.[Column] to Audit.ColumnName

		-- Query to determine lastvalues of deleted rows 
		Select TableName, PrimaryKey, ColumnName, NewValue as LastValue from dbo.audit 
		 Where Operation IN ('i', 'u') 
			AND AuditID IN (
				Select Max(AuditID) 
				  FROM dbo.audit a
					JOIN (SELECT TableName, PrimaryKey FROM dbo.audit where operation = 'd') d
					  on a.tablename = d.tablename and a.primarykey = d.primarykey
				  GROUP BY a.TableName, a.PrimaryKey, a.ColumnName )

-----------------------------------
version 1.02 - Jan 16, 2007
   fixed bug: Duplicate Columns. databases with user-defined types was causing the user-defined types to show up as system types. 
   added code gen to create [table]_deleted view that returns all deleted rows for the table

-----------------------------------
version 1.03 - Jan 16, 2007
   converted from cursor to Multiple Assignment Variable for building of for-each-column code
   added created, modifed, and delted columns to _deleted view 

-----------------------------------
version 1.04 - Jan 18, 2007
  minor clean-up on _deleted view. Removed extra Primary Key Column. 

-----------------------------------
version 1.05 - Jan 18, 2007
  changed from writing just the delete bit to writing the whole row. 
  modified _deleted view to return RowVersion

-----------------------------------
version 1.06 - Jan 30, 2007
  added host_name to audit trail
  improved modifed trigger run-away recursive trigger detection
  added basic error-trapping

-----------------------------------
version 1.07 - Feb 6, 2007
  idea from Gary Lail - don't log inserts, only updates
  added pRollbackAudit procedure
  changed all stored procedure names to pName


CREATE PROC usp AS SELECT OBJECT_NAME( @@PROCID )

-----------------------------------
version 1.08 - June 25, 2008
  case sensitive cleanup
  defaults named propoerly
  defaults and columns dropped in AutoAuditDrop proc

-----------------------------------
version 1.09 - Oct 15, 2008
  fixed @tablename bug in AutoAuditDrop
  changed audit time from GetDate() to inserted.created and inserted.modified to keep these times in synch
  changed from 'data type in()' to 'data type not in (xml, varbinary, image, text)'  
  added support for hierarchyID tracking (from Cast to Convert)
  added check: Table must have PK
  added check: PK must not be HierarchyID
  added RowVersion to dbo.Audit, and insert/update/delete procs
  added RowHistory Table Valued Function
  added SchemaAudit table and database trigger
  SchemaAuditDDLTrigger also fires pAutoAudit for Alter_Table events for tables with AutoAudit


-----------------------------------
version 1.09a - Oct 18, 2008
  fixed hard-coded path in _RowHistory dynamic SQL builder code
  changed _RowHistory values not updated from 0 to null
  
-----------------------------------
version 1.09b - Oct 23, 2008
  changed SchemaAudit.Schema and .Object to allow nulls for events that do not have schema.object
  
  
-----------------------------------
version 1.10 - Jan 24, 2010

  issue: NULL Updates that don't actually udpate anything 
    were still updating the modified column
    and incrementing the RowVersion
  fix: 
    elimiated the Modified trigger
    moved updating the Modified Column and incrementing the version number to the Update Trigger
    
  moved update of created col to insert trigger
  added modified and RowVersion col to Updated
  
  improved error reporting slightly
  
  added capture of user's SQL Statement/Batch
  
  added SET ARITHABORT ON : bug and fix reported by pjl on CodePlex on Jun 15 2009 at 9:35 AM
 
  added CreatedBy and ModifiedBy columns. If names passed to tables, then this value captured for Audit trail.
  
*/

SET QUOTED_IDENTIFIER ON

-- v1.09 adds schema audit 

IF Object_id('SchemaAudit') IS NULL
  CREATE TABLE dbo.SchemaAudit (
    AuditDate DATETIME NOT NULL,
    UserName SYSNAME NOT NULL,
    [Event] SYSNAME NOT NULL,
    [Schema] SYSNAME NULL,
    [Object] VARCHAR(50) NULL,
    [TSQL] VARCHAR(max) NOT NULL,
    [XMLEventData] XML NOT NULL
    );
go 

If Exists(select * from sys.triggers where name = 'SchemaAuditDDLTrigger')
   DROP TRIGGER SchemaAuditDDLTrigger ON Database
   
go -----------------------------------------------
 
   
CREATE TRIGGER [SchemaAuditDDLTrigger]
ON DATABASE
FOR DDL_DATABASE_LEVEL_EVENTS
AS 
BEGIN
  -- Added by AutoAudit 
  -- www.SQLServerBible.com 
  -- Paul Nielsen 
  SET NoCount ON
  SET ARITHABORT ON
 
  DECLARE 
    @EventData XML,
    @Schema SYSNAME,
    @Object SYSNAME,
    @EventType SYSNAME,
    @SQL VARCHAR(max)
    
  SET @EventData = EventData()
  
  SET @Schema = @EventData.value('data(/EVENT_INSTANCE/SchemaName)[1]', 'VARCHAR(50)')
  SET @Object = @EventData.value('data(/EVENT_INSTANCE/ObjectName)[1]', 'VARCHAR(50)')
  SET @EventType = @EventData.value('data(/EVENT_INSTANCE/EventType)[1]', 'VARCHAR(50)')
  
  
  INSERT SchemaAudit (AuditDate, UserName, [Event], [Schema], Object, TSQL, [XMLEventData])
  SELECT 
    GetDate(),
    @EventData.value('data(/EVENT_INSTANCE/UserName)[1]', 'SYSNAME'),
    @EventType, @Schema, @Object,
    @EventData.value('data(/EVENT_INSTANCE/TSQLCommand/CommandText)[1]', 'VARCHAR(max)'),
    @EventData
    
  IF @EventType = 'ALTER_TABLE'
     AND Exists(Select * from sys.objects where name = @Object + '_Audit_Insert')
    BEGIN 
      SET @SQL = 'EXEC pAutoAudit ''' + @Schema+ ''', ''' + @Object + ''''
      EXEC (@SQL)
    END 
   
END   

go -----------------------------------------------


IF Object_id('pAutoAudit') IS NOT NULL
  DROP PROC pAutoAudit
IF Object_id('pAutoAuditAll') IS NOT NULL
  DROP PROC pAutoAuditAll
IF Object_id('pAutoAuditDrop') IS NOT NULL
  DROP PROC pAutoAuditDrop
IF Object_id('pAutoAuditDropAll') IS NOT NULL
  DROP PROC pAutoAuditDropAll

go
IF Object_id('Audit') IS NULL
	CREATE TABLE dbo.Audit (
	  AuditID BIGINT NOT NULL IDENTITY PRIMARY KEY CLUSTERED,
	  AuditDate DATETIME NOT NULL,
	  HostName sysname NOT NULL,
	  SysUser NVARCHAR(128) NOT NULL,
	  Application VARCHAR(50) NOT NULL,
	  TableName sysname NOT NULL,
	  Operation CHAR(1) NOT NULL, -- i,u,d
	  SQLStatement VARCHAR(max) NULL, -- new column to capture SQL Statement
	  PrimaryKey VARCHAR(20) NOT NULL, -- edit to suite
	  RowDescription VARCHAR(50) NULL,-- Optional
	  SecondaryRow VARCHAR(50) NULL, -- Optional 
	  ColumnName sysname NULL, -- required for i,u, and now D (ver 1.07), should add check constraint
	  OldValue VARCHAR(50) NULL, -- edit to suite (Nvarchar() ?, varchar(MAX) ? ) 
	  NewValue VARCHAR(50) NULL, -- edit to suite (Nvarchar() ?, varchar(MAX) ? )
	  [RowVersion] INT NULL
	  )  -- optimzed for inserts, no non-clustered indexes

go --------------------------------------------------------------------


-- v 1.09 add RowVersion column to dbo.Audit 
IF not exists( select *
			  from sys.tables t
				join sys.schemas s
				  on s.schema_id = t.schema_id
				join sys.columns c
				  on t.object_id = c.object_id
			  where  t.name = 'Audit' AND s.name = 'dbo' and c.name = 'RowVersion')
 ALTER TABLE dbo.Audit ADD RowVersion INT NULL


go --------------------------------------------------------------------


CREATE PROC pAutoAudit (
   @SchemaName VARCHAR(50),
   @TableName VARCHAR(50)
) 
AS 
SET NoCount ON

-- script to create autoAudit triggers
DECLARE 
   @SQL NVARCHAR(max),
   @ColumnName  sysname,
   @PKColumnName sysname, 
   @PKDataType sysname

-- drop existing insert trigger
SET @SQL = 'If EXISTS (Select * from sys.objects where name = '
       + '''' + @TableName + '_Audit_Insert' + ''' )'
       + ' DROP TRIGGER ' + @SchemaName + '.' + @TableName + '_Audit_Insert'
EXEC (@SQL)

-- drop existing update trigger
SET @SQL = 'If EXISTS (Select * from sys.objects where name = '
       + '''' + @TableName + '_Audit_Update' + ''' )'
       + ' DROP TRIGGER ' + @SchemaName + '.' + @TableName + '_Audit_Update'
EXEC (@SQL)

-- drop existing modified trigger
SET @SQL = 'If EXISTS (Select * from sys.objects where name = '
       + '''' + @TableName + '_Modified' + ''' )'
       + ' DROP TRIGGER ' + @SchemaName + '.' + @TableName + '_Modified'
EXEC (@SQL)

-- drop existing delete trigger
SET @SQL = 'If EXISTS (Select * from sys.objects where name = '
       + '''' + @TableName + '_Audit_Delete' + ''' )'
       + ' DROP TRIGGER ' + @SchemaName + '.' + @TableName + '_Audit_Delete'
EXEC (@SQL)

-- drop existing delete view
SET @SQL = 'If EXISTS (Select * from sys.objects where name = '
       + '''v' + @TableName + '_Deleted' + ''' )'
       + ' DROP VIEW ' + @SchemaName + '.v' + @TableName + '_Deleted'
EXEC (@SQL)

-- drop existing row history TV UDF
SET @SQL = 'If EXISTS (Select * from sys.objects where name = '
       + '''' + @TableName + '_RowHistory' + ''' )'
       + ' DROP FUNCTION ' + @SchemaName + '.' + @TableName + '_RowHistory'
EXEC (@SQL)

-- add created column 
IF not exists (select *
			  from sys.tables t
				join sys.schemas s
				  on s.schema_id = t.schema_id
				join sys.columns c
				  on t.object_id = c.object_id
			  where  t.name = @TableName AND s.name = @SchemaName and c.name = 'Created')
  BEGIN                                                                                           
    SET @SQL = 'ALTER TABLE ' + @SchemaName + '.' + @TableName + ' ADD Created DateTime NOT NULL constraint ' + @TableName + '_Created_df Default GetDate()'
    EXEC (@SQL)
  END

-- add createdBy column 
IF not exists (select *
			  from sys.tables t
				join sys.schemas s
				  on s.schema_id = t.schema_id
				join sys.columns c
				  on t.object_id = c.object_id
			  where  t.name = @TableName AND s.name = @SchemaName and c.name = 'CreatedBy')
  BEGIN                                                                                           
    SET @SQL = 'ALTER TABLE ' + @SchemaName + '.' + @TableName + ' ADD CreatedBy NVARCHAR(128) NOT NULL constraint ' + @TableName + '_CreatedBy_df Default(Suser_SName())'
    EXEC (@SQL)
  END


-- add Modified column 
IF not exists( select *
			  from sys.tables t
				join sys.schemas s
				  on s.schema_id = t.schema_id
				join sys.columns c
				  on t.object_id = c.object_id
			  where  t.name = @TableName AND s.name = @SchemaName and c.name = 'Modified')
  BEGIN                                                                                               
    SET @SQL = 'ALTER TABLE ' + @SchemaName + '.' + @TableName + ' ADD Modified DateTime NOT NULL constraint ' + @TableName + '_Modified_df Default GetDate()'
    EXEC (@SQL)
  END
  
-- add createdBy column 
IF not exists (select *
			  from sys.tables t
				join sys.schemas s
				  on s.schema_id = t.schema_id
				join sys.columns c
				  on t.object_id = c.object_id
			  where  t.name = @TableName AND s.name = @SchemaName and c.name = 'ModifiedBy')
  BEGIN                                                                                           
    SET @SQL = 'ALTER TABLE ' + @SchemaName + '.' + @TableName + ' ADD ModifiedBy NVARCHAR(128) NOT NULL constraint ' + @TableName + '_ModifiedBy_df Default(Suser_SName())'
    EXEC (@SQL)
  END  
  

-- add RowVersion column 
IF not exists( select *
			  from sys.tables t
				join sys.schemas s
				  on s.schema_id = t.schema_id
				join sys.columns c
				  on t.object_id = c.object_id
			  where  t.name = @TableName AND s.name = @SchemaName and c.name = 'RowVersion')
  BEGIN   
    SET @SQL = 'ALTER TABLE ' + @SchemaName + '.' + @TableName + ' ADD RowVersion INT NOT NULL constraint ' + @TableName + '_RowVersion_df Default 1'
    EXEC (@SQL)
  END


-- get PK Column (1)  
select @PKColumnName = c.name, @PKDataType = ty.name
  from sys.tables t
    join sys.schemas s
      on s.schema_id = t.schema_id
    join sys.indexes i
      on t.object_id = i.object_id
    join sys.index_columns ic
  	  on i.object_id = ic.object_id
      and i.index_id = ic.index_id
    join sys.columns c
      on ic.object_id = c.object_id
      and ic.column_id = c.column_id
	join sys.types as ty
	  on ty.user_type_id = c.user_type_id
  where is_primary_key = 1 AND t.name = @TableName AND s.name = @SchemaName AND ic.index_column_id = 1
    

-- 1.09 Table PK Check  
  IF @PKColumnName IS NULL 
  BEGIN 
    PRINT '*** ' + @SchemaName + '.' + @TableName + ' invalid Table - no Primary Key. No triggers created.'
    RETURN
  END  
  
  
-- 1.09 Table PK HierarchyID Check  
  IF @PKDataType = 'HierarchyID'
  BEGIN 
    PRINT '*** ' + @SchemaName + '.' + @TableName + ' HierarchyID PK. No triggers created.'
    RETURN
  END  
  
   
     
-- build modified trigger 
--SET @SQL = 'CREATE TRIGGER ' + @SchemaName + '.' + @TableName + '_Modified' + ' ON '+ @SchemaName + '.' + @TableName + Char(13) + Char(10)
--       + ' AFTER Update' + Char(13) + Char(10) + ' NOT FOR REPLICATION AS' + Char(13) + Char(10)
--       + ' SET NoCount On ' + Char(13) + Char(10)
--       + ' -- generated by AutoAudit on ' + Convert(VARCHAR(30), GetDate(),100)  + Char(13) + Char(10)
--       + ' -- created by Paul Nielsen ' + Char(13) + Char(10)
--       + ' -- www.SQLServerBible.com ' + Char(13) + Char(10) + Char(13) + Char(10)

--       + ' Begin Try ' + Char(13) + Char(10)
--       + ' If Trigger_NestLevel(object_ID(N''[' + @SchemaName + '].[' + @TableName + '_Modified]'')) > 1 Return;' + Char(13) + Char(10)

--       + ' If (Update(Created) or Update(Modified)) AND Trigger_NestLevel() = 1' + Char(13) + Char(10)
--       + ' Begin; Raiserror(''Update failed.'', 16, 1); Rollback;  Return; End;' + Char(13) + Char(10)

--       + ' -- Update the Modified date' + Char(13) + Char(10)
--       + ' UPDATE [' + @SchemaName + '].[' + @TableName + ']'+ Char(13) + Char(10)
--       + ' SET Modified = getdate(), ' + Char(13) + Char(10)
--       + '        [RowVersion] = [' + @TableName + '].[RowVersion] + 1 ' + Char(13) + Char(10)
--       + '   FROM [' + @SchemaName + '].[' + @TableName + ']' + Char(13) + Char(10)
--       + '     JOIN Inserted'  + Char(13) + Char(10)
--       + '       ON [' + @TableName + '].[' + @PKColumnName + '] = Inserted.[' + @PKColumnName + ']'
--       + ' End Try ' + Char(13) + Char(10)
--       + ' Begin Catch ' + Char(13) + Char(10)
--       + '   Raiserror(''error in [' + + @SchemaName + '].[' + @TableName +'_modified] trigger'', 16, 1 ) with log' + Char(13) + Char(10)
--       + ' End Catch ' 

--    EXEC (@SQL)

--------------------------------------------------------------------------------------------
-- build insert trigger 
SET @SQL = 'CREATE TRIGGER ' + @SchemaName + '.' + @TableName + '_Audit_Insert' + ' ON '+ @SchemaName + '.' + @TableName + Char(13) + Char(10)
       + ' AFTER Insert' + Char(13) + Char(10) + ' NOT FOR REPLICATION AS' + Char(13) + Char(10)
       + ' SET NoCount On ' + Char(13) + Char(10)
       + ' SET ARITHABORT ON ' + Char(13) + Char(10)
      
       + ' -- generated by AutoAudit on ' + Convert(VARCHAR(30), GetDate(),100)  + Char(13) + Char(10)
       + ' -- created by Paul Nielsen ' + Char(13) + Char(10)
       + ' -- www.SQLServerBible.com ' + Char(13) + Char(10) + Char(13) + Char(10)
       + 'DECLARE @AuditTime DATETIME' + Char(13) + Char(10)
       + 'SET @AuditTime = GetDate()' + Char(13) + Char(10) + Char(13) + Char(10)
       
       + ' Begin Try ' + Char(13) + Char(10)
       
       + ' -- capture SQL Statement' + Char(13) + Char(10)
       + ' DECLARE @ExecStr varchar(50), @UserSQL nvarchar(max)' + Char(13) + Char(10)
       + ' DECLARE  @inputbuffer TABLE' + Char(13) + Char(10) 
       + ' (EventType nvarchar(30), Parameters int, EventInfo nvarchar(max))' + Char(13) + Char(10)
       + ' SET @ExecStr = ''DBCC INPUTBUFFER(@@SPID) with no_infomsgs''' + Char(13) + Char(10)
       + ' INSERT INTO @inputbuffer' + Char(13) + Char(10) 
       + '   EXEC (@ExecStr)' + Char(13) + Char(10)
       + ' SELECT @UserSQL = EventInfo FROM @inputbuffer' + Char(13) + Char(10)
       + Char(13) + Char(10)      
       + Char(13) + Char(10)
       

-- for PK column
	select @SQL = @SQL + 
		     '   INSERT dbo.Audit (AuditDate, SysUser, Application, HostName, TableName, Operation, SQLStatement, PrimaryKey, RowDescription, SecondaryRow, ColumnName, NewValue, RowVersion)' + Char(13) + Char(10)
		      + '   SELECT @AuditTime, COALESCE(Inserted.CreatedBy, Suser_SName()), APP_NAME(), Host_Name(), ' 
          + '''' + @SchemaName + '.' + @TableName + ''', ''i'','
          + ' @UserSQL, '  + Char(13) + Char(10)  
          + ' Inserted.[' + @PKColumnName + '],' + Char(13) + Char(10) 
          + '        NULL,     -- Row Description (e.g. Order Number)' + Char(13) + Char(10)   
          + '        NULL,     -- Secondary Row Value (e.g. Order Number for an Order Detail Line)' + Char(13) + Char(10)
          + '        ''[' + c.name + ']'','   
          + ' Cast(Inserted.[' + c.name + '] as VARCHAR(50)), 1' + Char(13) + Char(10)
          + '          FROM Inserted' + Char(13) + Char(10)
          + '          WHERE Inserted.['+ c.name + '] is not null' + Char(13) + Char(10)+ Char(13) + Char(10)
	  from sys.tables as t
		join sys.columns as c
		  on t.object_id = c.object_id
		join sys.schemas as s
		  on s.schema_id = t.schema_id
		join sys.types as ty
		  on ty.user_type_id = c.user_type_id
		-- v 1.09 changed to user type to accomodate SQL 2008 CLR data types
		--join sys.types st
		--  on ty.system_type_id = st.user_type_id
      where t.name = @TableName AND s.name = @SchemaName 
         AND c.name = @PKColumnName
         AND c.is_computed = 0
         -- version 1.09 modified list of data types
   	     -- v 1.09 changed to ty.name to accomodate SQL 2008 CLR data types
         AND ty.name NOT IN ('text', 'ntext', 'image',  'geography','xml', 'varbinary', 'timestamp')
	  order by c.column_id
	  
	select @SQL = @SQL 
	
       + '-----' + Char(13) + Char(10) + Char(13) + Char(10)
	     + ' -- Update the Created and Modified columns' + Char(13) + Char(10)
       + '   UPDATE [' + @SchemaName + '].[' + @TableName + ']'+ Char(13) + Char(10)
       + '     SET Created  = @AuditTime, ' + Char(13) + Char(10)
       + '         CreatedBy  = COALESCE(Inserted.CreatedBy, Suser_SName()), ' + Char(13) + Char(10)
       + '         Modified = @AuditTime, ' + Char(13) + Char(10)
       + '         ModifiedBy  = COALESCE(Inserted.CreatedBy, Suser_SName()), ' + Char(13) + Char(10)
       + '        [RowVersion] =  1' + Char(13) + Char(10)
       + '     FROM [' + @SchemaName + '].[' + @TableName + ']' + Char(13) + Char(10)
       + '       JOIN Inserted'  + Char(13) + Char(10)
       + '         ON [' + @TableName + '].[' + @PKColumnName + '] = Inserted.[' + @PKColumnName + ']'
       +  Char(13) + Char(10) + Char(13) + Char(10)
       + '-----' + Char(13) + Char(10) + Char(13) + Char(10)

	select @SQL = @SQL + 
       + ' End Try ' + Char(13) + Char(10)
       + ' Begin Catch ' + Char(13) + Char(10)
       + '   DECLARE @ErrorMessage NVARCHAR(4000), @ErrorSeverity INT, @ErrorState INT;' + Char(13) + Char(10) 

       + '   SET @ErrorMessage = ERROR_MESSAGE();  ' + Char(13) + Char(10)
       + '   SET @ErrorSeverity = ERROR_SEVERITY(); ' + Char(13) + Char(10) 
       + '   SET @ErrorState = ERROR_STATE();  ' + Char(13) + Char(10)
       + '   RAISERROR(@ErrorMessage,@ErrorSeverity,@ErrorState) with log;' + Char(13) + Char(10) 
--        + '   Raiserror(''error in [' + + @SchemaName + '].[' + @TableName +'_audit_insert] trigger'', 16, 1 ) with log' + Char(13) + Char(10)
       + ' End Catch '
EXEC (@SQL)

SET @SQL = @SchemaName + '.' + @TableName + '_Audit_Insert'

EXEC sp_settriggerorder 
  @triggername= @SQL, 
  @order='Last', 
  @stmttype = 'INSERT';


--------------------------------------------------------------------------------------------
-- build update trigger 

SET @SQL = 'CREATE TRIGGER ' + @SchemaName + '.' + @TableName + '_Audit_Update' + ' ON '+ @SchemaName + '.' + @TableName + Char(13) + Char(10)
       + ' AFTER Update' + Char(13) + Char(10) + ' NOT FOR REPLICATION AS' + Char(13) + Char(10)
       + ' SET NoCount On ' + Char(13) + Char(10)
       + ' -- generated by AutoAudit on ' + Convert(VARCHAR(30), GetDate(),100)  + Char(13) + Char(10)
       + ' -- created by Paul Nielsen ' + Char(13) + Char(10)
       + ' -- www.SQLServerBible.com ' + Char(13) + Char(10) + Char(13) + Char(10)
       + 'DECLARE @AuditTime DATETIME, @IsDirty BIT' + Char(13) + Char(10)
       + 'SET @AuditTime = GetDate()' + Char(13) + Char(10) + Char(13) + Char(10)
       + 'SET @IsDirty = 0' + Char(13) + Char(10) + Char(13) + Char(10)
       
       + ' Begin Try ' + Char(13) + Char(10)
              
       + ' -- capture SQL Statement' + Char(13) + Char(10)
       + ' DECLARE @ExecStr varchar(50), @UserSQL nvarchar(max)' + Char(13) + Char(10)
       + ' DECLARE  @inputbuffer TABLE' + Char(13) + Char(10) 
       + ' (EventType nvarchar(30), Parameters int, EventInfo nvarchar(max))' + Char(13) + Char(10)
       + ' SET @ExecStr = ''DBCC INPUTBUFFER(@@SPID) with no_infomsgs''' + Char(13) + Char(10)
       + ' INSERT INTO @inputbuffer' + Char(13) + Char(10) 
       + '   EXEC (@ExecStr)' + Char(13) + Char(10)
       + ' SELECT @UserSQL = EventInfo FROM @inputbuffer' + Char(13) + Char(10)
       + Char(13) + Char(10)      
       + Char(13) + Char(10)      
       
-- for each column
	select @SQL = @SQL + 

	--    1.10b Cuatious that the overuse of IF brnaching may cause recompiles or bad query execution plans
	--    +  ' IF UPDATE([' + c.name + '])' + Char(13) + Char(10)
        
       + '   INSERT dbo.Audit (AuditDate, SysUser, Application, HostName, TableName, Operation, SQLStatement, PrimaryKey, RowDescription, SecondaryRow, ColumnName, OldValue, NewValue, RowVersion)' + Char(13) + Char(10)
       + '   SELECT  @AuditTime, COALESCE(Inserted.ModifiedBy, Suser_SName()), APP_NAME(), Host_Name(), ' 
       + '''' + @SchemaName + '.' + @TableName + ''', ''u'','  
       + ' @UserSQL, '  + Char(13) + Char(10)  
       
       -- 1.09 handle HierarchyID PK  
       + ' Convert(VARCHAR(50), Inserted.[' + @PKColumnName + ']),' + Char(13) + Char(10) 
       ----------------
       + '        NULL,     -- Row Description (e.g. Order Number)' + Char(13) + Char(10)   
       + '        NULL,     -- Secondary Row Value (e.g. Order Number for an Order Detail Line)' + Char(13) + Char(10)
       + '        ''[' + c.name+ ']'',' 
         
  -- 1.09 changed from cast to convert to handle HierarchyID conversion 
  --     + ' Cast(Deleted.[' + c.name + '] as VARCHAR(50)), ' 
       + ' Convert(VARCHAR(50), Deleted.[' + c.name + ']), ' 
       
  --     + ' Cast(Inserted.[' + c.name + '] as VARCHAR(50))' + Char(13) + Char(10)
       + ' Convert(VARCHAR(50), Inserted.[' + c.name + ']),' + Char(13) + Char(10)
       
       + ' DELETED.Rowversion + 1' + Char(13) + Char(10)
       + '          FROM Inserted' + Char(13) + Char(10)
       + '             JOIN Deleted' + Char(13) + Char(10)
       + '               ON Inserted.[' + @PKColumnName + '] = Deleted.[' + @PKColumnName + ']' + Char(13) + Char(10)
     --  + '               AND isnull(Inserted.[' + c.name + '],'''') <> isnull(Deleted.[' + c.name + '],'''')' + Char(13) + Char(10) 
              --  AND ISNULL(Inserted.[CBIGINT],'') <> ISNULL(Deleted.[CBIGINT],'')

	+ CASE ty.name
	    WHEN 'HierarchyID'  
	      THEN '               AND ISNULL(Inserted.[' + c.name + '],0x999999) <> ISNULL(Deleted.[' + c.name + '],0x999999)'  + Char(13) + Char(10) 
	    WHEN 'uniqueidentifier'  
	      THEN '               AND ISNULL(Inserted.[' + c.name + '],''00000000-0000-0000-0000-000000000000'') <> ISNULL(Deleted.[' + c.name + '],''00000000-0000-0000-0000-000000000000'')'  + Char(13) + Char(10) 
	    WHEN 'DECIMAL'  
	      THEN '               AND ISNULL(Inserted.[' + c.name + '],0) <> ISNULL(Deleted.[' + c.name + '],0)'  + Char(13) + Char(10) 
	    WHEN 'NUMERIC'  
	      THEN '               AND ISNULL(Inserted.[' + c.name + '],0) <> ISNULL(Deleted.[' + c.name + '],0)'  + Char(13) + Char(10) 
	    ELSE '               AND ISNULL(Inserted.[' + c.name + '],'''') <> ISNULL(Deleted.[' + c.name + '],'''')'  + Char(13) + Char(10) 
	  END   
       
       
       
       
       + '   IF @@RowCount > 0 SET @IsDirty = 1' + Char(13) + Char(10)
       + '-----' + Char(13) + Char(10) + Char(13) + Char(10)
	  from sys.tables as t
		join sys.columns as c
		  on t.object_id = c.object_id
		join sys.schemas as s
		  on s.schema_id = t.schema_id
		join sys.types as ty
		  on ty.user_type_id = c.user_type_id
	    -- v 1.09 changed to user type to accomodate SQL 2008 CLR data types
		-- join sys.types st
		--   on ty.system_type_id = st.user_type_id
      where t.name = @TableName AND s.name = @SchemaName 
         AND c.name NOT IN ('created', 'modified','CreatedBy', 'ModifiedBy','RowVersion')
         AND c.name <> @PKColumnName  -- The PK should not be updatable
         AND c.is_computed = 0
         -- version 1.09 modified list of data types
   	     -- v 1.09 changed to ty.name to accomodate SQL 2008 CLR data types
         AND ty.name NOT IN ('text', 'ntext', 'image',  'geography','xml', 'binary', 'varbinary', 'timestamp')
	  order by c.column_id

-- uniqueidentifier

  -- update the modified col and increament the RowVersion
	select @SQL = @SQL 
	
       + '-----' + Char(13) + Char(10) + Char(13) + Char(10)
	     + ' -- Update the Modified date' + Char(13) + Char(10)
	     + ' IF @IsDirty = 1' + Char(13) + Char(10)
       + '   UPDATE [' + @SchemaName + '].[' + @TableName + ']'+ Char(13) + Char(10)
       + '     SET Modified = @AuditTime, ' + Char(13) + Char(10)
       + '         ModifiedBy  = COALESCE(Inserted.ModifiedBy, Suser_SName()), ' + Char(13) + Char(10)
       + '        [RowVersion] = [' + @TableName + '].[RowVersion] + 1 ' + Char(13) + Char(10)
       + '     FROM [' + @SchemaName + '].[' + @TableName + ']' + Char(13) + Char(10)
       + '       JOIN Inserted'  + Char(13) + Char(10)
       + '         ON [' + @TableName + '].[' + @PKColumnName + '] = Inserted.[' + @PKColumnName + ']'
       +  Char(13) + Char(10) + Char(13) + Char(10)
       + '-----' + Char(13) + Char(10) + Char(13) + Char(10)

	select @SQL = @SQL + 
       + ' End Try ' + Char(13) + Char(10)
       + ' Begin Catch ' + Char(13) + Char(10)
       + '   DECLARE @ErrorMessage NVARCHAR(4000), @ErrorSeverity INT, @ErrorState INT;' + Char(13) + Char(10) 

       + '   SET @ErrorMessage = ERROR_MESSAGE();  ' + Char(13) + Char(10)
       + '   SET @ErrorSeverity = ERROR_SEVERITY(); ' + Char(13) + Char(10) 
       + '   SET @ErrorState = ERROR_STATE();  ' + Char(13) + Char(10)
       + '   RAISERROR(@ErrorMessage,@ErrorSeverity,@ErrorState) with log;' + Char(13) + Char(10) 
--        + '   Raiserror(''error in [' + + @SchemaName + '].[' + @TableName +'_audit_insert] trigger'', 16, 1 ) with log' + Char(13) + Char(10)
       + ' End Catch ' 

EXEC (@SQL)

SET @SQL = @SchemaName + '.' + @TableName + '_Audit_Update'

EXEC sp_settriggerorder 
  @triggername= @SQL, 
  @order='Last', 
  @stmttype = 'UPDATE';



--------------------------
-- build delete trigger 
SET @SQL = 'CREATE TRIGGER ' + @SchemaName + '.' + @TableName + '_Audit_Delete' + ' ON '+ @SchemaName + '.' + @TableName + Char(13) + Char(10)
       + ' AFTER Delete' + Char(13) + Char(10) + ' NOT FOR REPLICATION AS' + Char(13) + Char(10)
       + ' SET NoCount On ' + Char(13) + Char(10)
       + ' -- generated by AutoAudit on ' + Convert(VARCHAR(30), GetDate(),100)  + Char(13) + Char(10)
       + ' -- created by Paul Nielsen ' + Char(13) + Char(10)
       + ' -- www.SQLServerBible.com ' + Char(13) + Char(10) + Char(13) + Char(10)
       + 'DECLARE @AuditTime DATETIME' + Char(13) + Char(10)
       + 'SET @AuditTime = GetDate()' + Char(13) + Char(10) + Char(13) + Char(10)
       
       + ' -- capture SQL Statement' + Char(13) + Char(10)
       + ' DECLARE @ExecStr varchar(50), @UserSQL nvarchar(max)' + Char(13) + Char(10)
       + ' DECLARE  @inputbuffer TABLE' + Char(13) + Char(10) 
       + ' (EventType nvarchar(30), Parameters int, EventInfo nvarchar(max))' + Char(13) + Char(10)
       + ' SET @ExecStr = ''DBCC INPUTBUFFER('' + STR(@@SPID) + '')''' + Char(13) + Char(10)
       + ' INSERT INTO @inputbuffer' + Char(13) + Char(10) 
       + '   EXEC (@ExecStr)' + Char(13) + Char(10)
       + ' SELECT @UserSQL = EventInfo FROM @inputbuffer' + Char(13) + Char(10)
       + Char(13) + Char(10)      
       + Char(13) + Char(10)      
       
       + ' Begin Try ' + Char(13) + Char(10)

-- for each column
	select @SQL = @SQL + 
		     '   INSERT dbo.Audit (AuditDate, SysUser, Application, HostName, TableName, Operation, SQLStatement, PrimaryKey, RowDescription, SecondaryRow, ColumnName, OldValue, Rowversion)' + Char(13) + Char(10)
          + '   SELECT  @AuditTime, COALESCE(deleted.ModifiedBy, Suser_SName()), APP_NAME(), Host_Name(),' 
          + '''' + @SchemaName + '.' + @TableName + ''', ''d'','
          + ' @UserSQL, '  + Char(13) + Char(10)  
             
          + ' deleted.[' + @PKColumnName + '],' + Char(13) + Char(10) 
          + '        NULL,     -- Row Description (e.g. Order Number)' + Char(13) + Char(10)   
          + '        NULL,     -- Secondary Row Value (e.g. Oder Number for an Order Detail Line)' + Char(13) + Char(10)
          + '        ''[' + c.name + ']'','   

  --     + ' Cast(Deleted.[' + c.name + '] as VARCHAR(50)), ' 
       + ' Convert(VARCHAR(50), Deleted.[' + c.name + ']), ' 
          + ' deleted.Rowversion '
          + '          FROM deleted' + Char(13) + Char(10)
          + '          WHERE deleted.['+ c.name + '] is not null' + Char(13) + Char(10)+ Char(13) + Char(10)
	  from sys.tables as t
		join sys.columns as c
		  on t.object_id = c.object_id
		join sys.schemas as s
		  on s.schema_id = t.schema_id
		join sys.types as ty
		  on ty.user_type_id = c.user_type_id
        -- v 1.09 changed to ty.name to accomodate SQL 2008 CLR data types
		--join sys.types st
		--  on ty.system_type_id = st.user_type_id
      where t.name = @TableName AND s.name = @SchemaName 
         -- AND c.name NOT IN ('created', 'modified','RowVersion')
         AND c.is_computed = 0
         -- version 1.09 modified list of data types
         -- v 1.09 changed to ty.name to accomodate SQL 2008 CLR data types
         AND ty.name NOT IN ('text', 'ntext', 'image',  'geography','xml', 'varbinary', 'timestamp')
	  order by c.column_id

	select @SQL = @SQL + 
       + ' End Try ' + Char(13) + Char(10)
       + ' Begin Catch ' + Char(13) + Char(10)
       + '   DECLARE @ErrorMessage NVARCHAR(4000), @ErrorSeverity INT, @ErrorState INT;' + Char(13) + Char(10) 

       + '   SET @ErrorMessage = ERROR_MESSAGE();  ' + Char(13) + Char(10)
       + '   SET @ErrorSeverity = ERROR_SEVERITY(); ' + Char(13) + Char(10) 
       + '   SET @ErrorState = ERROR_STATE();  ' + Char(13) + Char(10)
       + '   RAISERROR(@ErrorMessage,@ErrorSeverity,@ErrorState) with log;' + Char(13) + Char(10) 
--        + '   Raiserror(''error in [' + + @SchemaName + '].[' + @TableName +'_audit_insert] trigger'', 16, 1 ) with log' + Char(13) + Char(10)
       + ' End Catch ' 

EXEC (@SQL)

SET @SQL = @SchemaName + '.' + @TableName + '_Audit_Delete'

EXEC sp_settriggerorder 
  @triggername= @SQL, 
  @order='Last', 
  @stmttype = 'DELETE';



--------------------------------------------------------------------------------------------
-- build _Deleted view

/* Sample: 
CREATE VIEW Production.vCulture_Deleted
as
	SELECT 
		Max(Case ColumnName WHEN '[CultureID]' THEN OldValue ELSE '' END) AS [CultureID],
		Max(Case ColumnName WHEN '[Name]' THEN OldValue ELSE '' END) AS [Name],
		Max(Case ColumnName WHEN '[Created]' THEN OldValue ELSE '' END) AS [Created],
		Max(Case ColumnName WHEN '[Modified]' THEN OldValue ELSE '' END) AS Modified,
		Max(Case ColumnName WHEN '[Rowversion]' THEN OldValue ELSE '' END) AS [Rowversion],
        MAX(AuditDate) AS 'Deleted'
	FROM Audit 
	Where TableName = 'Production.Culture' AND Operation = 'd'
	GROUP BY PrimaryKey 
*/

SET @SQL = 'CREATE VIEW ' + @SchemaName + '.v' + @TableName + '_Deleted' + Char(13) + Char(10)
       + 'AS ' + Char(13) + Char(10) 
       + ' -- generated by AutoAudit on ' + Convert(VARCHAR(30), GetDate(),100)  + Char(13) + Char(10)
       + ' -- created by Paul Nielsen ' + Char(13) + Char(10)
       + ' -- www.SQLServerBible.com ' + Char(13) + Char(10) + Char(13) + Char(10)
       + 'SELECT ' + Char(13) + Char(10)

-- for each column
SELECT @SQL = @SQL +
		  '     Max(Case ColumnName WHEN ''[' + c.name + ']'' THEN OldValue ELSE '''' END) AS [' + c.name +'],'  + Char(13) + Char(10)
	  from sys.tables as t
		join sys.columns as c
		  on t.object_id = c.object_id
		join sys.schemas as s
		  on s.schema_id = t.schema_id
		join sys.types as ty
		  on ty.user_type_id = c.user_type_id
		--join sys.types st
		--  on ty.system_type_id = st.user_type_id
      where t.name = @TableName AND s.name = @SchemaName 
         AND c.is_computed = 0
         -- version 1.09 modified list of data types
         -- v 1.09 changed to ty.name to accomodate SQL 2008 CLR data types
         AND ty.name NOT IN ('text', 'ntext', 'image',  'geography','xml', 'varbinary', 'timestamp')
	  order by c.column_id

SET @SQL = @SQL
        + '      MAX(AuditDate) AS ''Deleted'''  + Char(13) + Char(10)
	    + '  FROM dbo.Audit'   + Char(13) + Char(10)
	    + '  Where TableName = ''' +@SchemaName + '.' + @TableName + ''' AND Operation = ''d'''  + Char(13) + Char(10)
	    + '  GROUP BY PrimaryKey' 

EXEC (@SQL)

--------------------------------------------------------------------------------------------
-- build _RowHistory Table-Valued UDF

/* sample:
CREATE FUNCTION HumanResources.Department_RowHistory (@PK INT) 
RETURNS TABLE
RETURN
(
-- Initial Row Values
Select Created as AuditDate, 'i' as Operation, 
    -- Name 
    (SELECT Coalesce( 
        (Select top 1 OldValue 
          FROM dbo.Audit 
          Where TableName = 'HumanResources.Department' 
            and PrimaryKey = @PK
            and Operation = 'u' 
            and ColumnName = '[Name]'
          order by AuditID),
      Name)
    FROM HumanResources.Department
      WHERE DepartmentID = @PK
  ) as Name,
  
    -- GroupName 
   ( SELECT Coalesce( 
        (Select top 1 OldValue 
          FROM dbo.Audit 
          Where TableName = 'HumanResources.Department' 
            and PrimaryKey = @PK 
            and Operation = 'u' 
            and ColumnName = '[GroupName]'
          order by AuditID),
      GroupName)
    FROM HumanResources.Department
      WHERE DepartmentID = @PK
  ) as GroupName
  FROM HumanResources.Department
  where DepartmentID = @PK

UNION ALL

SELECT AuditDate, 'u'
     ,Max(Case ColumnName WHEN '[Name]' THEN NewValue ELSE '' END) AS [Name]
     ,Max(Case ColumnName WHEN '[GroupName]' THEN NewValue ELSE '' END) AS [GroupName]
  FROM Audit
  Where TableName = 'HumanResources.Department' 
    AND PrimaryKey = @PK
    and Operation = 'u' 
  GROUP BY PrimaryKey, AuditDate
);
*/

SET @SQL = 'CREATE FUNCTION ' + @SchemaName + '.' + @TableName + '_RowHistory (@PK INT)' + Char(13) + Char(10)
       + 'RETURNS TABLE ' + Char(13) + Char(10) 
       + ' -- generated by AutoAudit on ' + Convert(VARCHAR(30), GetDate(),100)  + Char(13) + Char(10)
       + ' -- created by Paul Nielsen ' + Char(13) + Char(10)
       + ' -- www.SQLServerBible.com ' + Char(13) + Char(10) + Char(13) + Char(10)
       + 'RETURN ' + Char(13) + Char(10)
       + '( ' + Char(13) + Char(10)
       + '-- Initial Row Values ' + Char(13) + Char(10)
       + 'Select Created as AuditDate, ''i'' as Operation, 1 as RowVersion' 

-- for each column
SELECT @SQL = @SQL 
      + ', ' + Char(13) + Char(10)
      + '-- ' + c.name + Char(13) + Char(10)
      + '(SELECT Coalesce(' + Char(13) + Char(10)
      + '    (Select top 1 OldValue' + Char(13) + Char(10)
      + '       FROM dbo.Audit' + Char(13) + Char(10)
      + '       WHERE TableName = ''' + @SchemaName + '.' + @TableName + '''' + Char(13) + Char(10)
      + '         and PrimaryKey = @PK' + Char(13) + Char(10)
      + '         and Operation = ''u'''  + Char(13) + Char(10)
      + '         and ColumnName = ''[' + c.name + ']''' + Char(13) + Char(10)
      + '       ORDER BY AuditID),' + Char(13) + Char(10)
      + '     [' + c.name + '])' + Char(13) + Char(10)
      + '  FROM ' +  @SchemaName + '.' + @TableName  + Char(13) + Char(10)
      + '  WHERE [' + @PKColumnName + '] = @PK '  + Char(13) + Char(10)
      + ' ) as [' + c.name + ']' + Char(13) + Char(10)
	  from sys.tables as t
		join sys.columns as c
		  on t.object_id = c.object_id
		join sys.schemas as s
		  on s.schema_id = t.schema_id
		join sys.types as ty
		  on ty.user_type_id = c.user_type_id
		--join sys.types st
		--  on ty.system_type_id = st.user_type_id
      where t.name = @TableName AND s.name = @SchemaName 
         AND c.is_computed = 0
         -- version 1.09 modified list of data types
         -- v 1.09 changed to ty.name to accomodate SQL 2008 CLR data types
         AND ty.name NOT IN ('text', 'ntext', 'image',  'geography','xml', 'varbinary', 'timestamp')
         AND c.name NOT IN ('created', 'modified','RowVersion')
	  order by c.column_id

SELECT @SQL = @SQL 
      + ' FROM ' + @SchemaName + '.' + @TableName + Char(13) + Char(10)
      + '   WHERE ' + @PKColumnName +  ' = @PK )' + Char(13) + Char(10) + Char(13) + Char(10)
      
      + 'UNION ALL' + Char(13) + Char(10) + Char(13) + Char(10)
      
 -- Updated values from Audit Trail     
      + 'SELECT AuditDate, ''u'', RowVersion' + Char(13) + Char(10)

-- for each column
SELECT @SQL = @SQL 
      + '     ,Max(Case ColumnName WHEN ''[' + c.name + ']'' THEN NewValue ELSE null END) AS [' + c.name + ']' + Char(13) + Char(10)
	  from sys.tables as t
		join sys.columns as c
		  on t.object_id = c.object_id
		join sys.schemas as s
		  on s.schema_id = t.schema_id
		join sys.types as ty
		  on ty.user_type_id = c.user_type_id
		--join sys.types st
		--  on ty.system_type_id = st.user_type_id
      where t.name = @TableName AND s.name = @SchemaName 
         AND c.is_computed = 0
         -- version 1.09 modified list of data types
         -- v 1.09 changed to ty.name to accomodate SQL 2008 CLR data types
         AND ty.name NOT IN ('text', 'ntext', 'image',  'geography','xml', 'varbinary', 'timestamp')
         AND c.name NOT IN ('created', 'modified','RowVersion')
	  order by c.column_id

SELECT @SQL = @SQL 
      + '  FROM dbo.Audit'  + Char(13) + Char(10)
      + '    Where TableName = '''  +@SchemaName + '.' + @TableName + ''''   + Char(13) + Char(10)
      + '      AND PrimaryKey = @PK' + Char(13) + Char(10)
      + '      and Operation = ''u'''  + Char(13) + Char(10)
      + '    GROUP BY PrimaryKey, AuditDate, RowVersion'  + Char(13) + Char(10)
     -- + ' ORDER BY RowVersion'
     
EXEC (@SQL)

RETURN -- END OF SPROC

go --------------------------------------------------------------------
CREATE PROC pAutoAuditDrop (
   @SchemaName VARCHAR(50),
   @TableName VARCHAR(50)
) 
AS 
SET NoCount ON

DECLARE 
   @SQL NVARCHAR(max)

-- drop default constraints

-- version 1.09 - fixed bug, missing @table _
If Exists (select * from sys.objects where name = @TableName + '_Created_df')
  BEGIN 
    SET @SQL = 'ALTER TABLE ' + @SchemaName + '.' + @TableName + ' drop constraint ' + @TableName + '_Created_df'
    EXEC (@SQL)
  END

-- version 1.09 - fixed bug, missing @table _
If Exists (select * from sys.objects where name = @TableName + '_Modified_df')
  BEGIN 
    SET @SQL = 'ALTER TABLE ' + @SchemaName + '.' + @TableName + ' drop constraint ' + @TableName + '_Modified_df'
    EXEC (@SQL)
  END

-- version 1.09 - fixed bug, missing @table _
If Exists (select * from sys.objects where name = @TableName + '_RowVersion_df')
  BEGIN 
    SET @SQL = 'ALTER TABLE ' + @SchemaName + '.' + @TableName + ' drop constraint ' + @TableName + '_RowVersion_df'
    EXEC (@SQL)
  END

-- drop created column 
IF exists (select *
			  from sys.tables t
				join sys.schemas s
				  on s.schema_id = t.schema_id
				join sys.columns c
				  on t.object_id = c.object_id
			  where  t.name = @TableName AND s.name = @SchemaName and c.name = 'Created')
  BEGIN
    SET @SQL = 'ALTER TABLE ' + @SchemaName + '.' + @TableName + ' DROP COLUMN Created'
    EXEC (@SQL)
  END

-- add modified column 
IF exists( select *
			  from sys.tables t
				join sys.schemas s
				  on s.schema_id = t.schema_id
				join sys.columns c
				  on t.object_id = c.object_id
			  where  t.name = @TableName AND s.name = @SchemaName and c.name = 'Modified')
  BEGIN   
    SET @SQL = 'ALTER TABLE ' + @SchemaName + '.' + @TableName + ' DROP COLUMN Modified'
    EXEC (@SQL)
  END

-- add RowVersion column 
IF exists( select *
			  from sys.tables t
				join sys.schemas s
				  on s.schema_id = t.schema_id
				join sys.columns c
				  on t.object_id = c.object_id
			  where  t.name = @TableName AND s.name = @SchemaName and c.name = 'RowVersion')
  BEGIN   
    SET @SQL = 'ALTER TABLE ' + @SchemaName + '.' + @TableName + ' DROP COLUMN RowVersion'
    EXEC (@SQL)
  END

-- drop existing insert trigger
SET @SQL = 'If EXISTS (Select * from sys.objects where name = '
       + '''' + @TableName + '_Audit_Insert' + ''' )'
       + ' DROP TRIGGER ' + @SchemaName + '.' + @TableName + '_Audit_Insert'
EXEC (@SQL)

-- drop existing update trigger
SET @SQL = 'If EXISTS (Select * from sys.objects where name = '
       + '''' + @TableName + '_Audit_Update' + ''' )'
       + ' DROP TRIGGER ' + @SchemaName + '.' + @TableName + '_Audit_Update'
EXEC (@SQL)

-- drop existing modified trigger
SET @SQL = 'If EXISTS (Select * from sys.objects where name = '
       + '''' + @TableName + '_Modified' + ''' )'
       + ' DROP TRIGGER ' + @SchemaName + '.' + @TableName + '_Modified'
EXEC (@SQL)

-- drop existing delete trigger
SET @SQL = 'If EXISTS (Select * from sys.objects where name = '
       + '''' + @TableName + '_Audit_Delete' + ''' )'
       + ' DROP TRIGGER ' + @SchemaName + '.' + @TableName + '_Audit_Delete'
EXEC (@SQL)

go -----------------------------------------------------------------------
CREATE 
-- ALTER 
PROC pAutoAuditAll 
AS 
SET NoCount ON 
DECLARE 
   @TableName VARCHAR(50), 
   @SchemaName VARCHAR(50), 
   @SQL NVARCHAR(max)
-- for each table
-- 1
DECLARE cTables CURSOR FAST_FORWARD READ_ONLY
  FOR  SELECT s.name, t.name 
			  from sys.tables t
				join sys.schemas s
				  on t.schema_id = s.schema_id
			 where t.name <> 'audit'
--2 
OPEN cTables
--3 
FETCH cTables INTO @SchemaName, @TableName   -- prime the cursor
WHILE @@Fetch_Status = 0 
  BEGIN
		SET @SQL = 'EXEC pAutoAudit ''' + @SchemaName + ''', ''' + @TableName + ''''
		PRINT @SQL
		EXEC (@SQL)
      FETCH cTables INTO @SchemaName, @TableName   -- fetch next
  END
-- 4  
CLOSE cTables
-- 5
DEALLOCATE cTables

RETURN 

go -----------------------------------------------------------------------
CREATE PROC pAutoAuditDropAll 
AS 
SET NoCount ON 
DECLARE 
   @TableName VARCHAR(50), 
   @SchemaName VARCHAR(50), 
   @SQL NVARCHAR(max)
-- for each table
-- 1
DECLARE cTables CURSOR FAST_FORWARD READ_ONLY
  FOR  SELECT s.name, t.name 
			  from sys.tables t
				join sys.schemas s
				  on t.schema_id = s.schema_id
			 where t.name <> 'audit'
--2 
OPEN cTables
--3 
FETCH cTables INTO @SchemaName, @TableName   -- prime the cursor
WHILE @@Fetch_Status = 0 
  BEGIN
		SET @SQL = 'EXEC pAutoAuditDrop ''' + @SchemaName + ''', ''' + @TableName + ''''
		EXEC (@SQL)
      FETCH cTables INTO @SchemaName, @TableName   -- fetch next
  END
-- 4  
CLOSE cTables
-- 5
DEALLOCATE cTables

go --
use tempdb




