Create a full Point-In-Time restore script automatically!


A new version has been created! Check it out here: Updated: create point-in-time restore script automatically

Ok, I was sort of bored today so I figured I’d do something I’ve been wanting to do for a long time now.

It’s not very often you need to do a point-in-time restore but when you do you’re quite often in a big hurry. So I created a nice little script that will generate the entire point-in-time restore code for you automatically by only providing the name of the database and the point in time. All the scripts are generated from data in the backup-tables in the MSDB but for the actual restore scripts to work you’ll of course need the backup files in the same location as they were when you ran the backup.

Please note that the script only works on SQL Server 2008 (because of the format of the header/fillist tables).
Create a point-in-time restore automatically – SQL 2005 version

/*
Generate Point-in-time restore script, given only DBName and Point-in-time timestamp
Works only on SQL2008 databases!!

Script for generating restore script for full backup originally created by Michael
Valentine Jones, SQLTeam.com

The rest is created by Henning Frettem, www.thefirstsql.com
*/
SET NOCOUNT ON
GO

DECLARE
  @DBName varchar(200) = 'MyDB',
  @PointInTime datetime = '2010-08-09 09:10:00',
  @Filename varchar(200),
  @tab varchar(1) = char(9),
  @cr varchar(2) = char(13)+char(10),
  @Full_BackupStartDate datetime,
  @Diff_BackupStartDate datetime,
  @Log_BackupStartDate datetime,
  @SQL nvarchar(max) = ''

BEGIN TRY
  --> Performing some checks
  IF NOT EXISTS (SELECT 1 FROM msdb.dbo.backupset WHERE database_name = @DBName AND type = 'D' AND backup_start_date  @PointInTime)
    RAISERROR(N'No backup of the log exist after the specified PointInTime', 16, 1)

  --> Getting the filename and the date of the last full backup prior to the PointInTime
  SELECT TOP 1
    @Filename = b.physical_device_name,
    @Full_BackupStartDate = backup_start_date
  FROM msdb.dbo.backupset a
    INNER JOIN msdb.dbo.backupmediafamily b
      ON a.media_set_id = b.media_set_id
  WHERE a.database_name = @DBName
    AND a.type = 'D'
    AND a.backup_start_date  Create temp-tables for file header information
  DECLARE @header table (
    BackupName nvarchar(128),
    BackupDescription nvarchar(255),
    BackupType smallint,
    ExpirationDate datetime,
    Compressed bit,
    Position smallint,
    DeviceType tinyint,
    UserName nvarchar(128),
    ServerName nvarchar(128),
    DatabaseName nvarchar(128),
    DatabaseVersion int,
    DatabaseCreationDate datetime,
    BackupSize numeric(20,0),
    FirstLSN numeric(25,0),
    LastLSN numeric(25,0),
    CheckpointLSN numeric(25,0),
    DatabaseBackupLSN numeric(25,0),
    BackupStartDate datetime,
    BackupFinishDate datetime,
    SortOrder smallint,
    CodePage smallint,
    UnicodeLocaleId int,
    UnicodeComparisonStyle int,
    CompatibilityLevel tinyint,
    SoftwareVendorId int,
    SoftwareVersionMajor int,
    SoftwareVersionMinor int,
    SoftwareVersionBuild int,
    MachineName nvarchar(128),
    Flags  int,
    BindingID uniqueidentifier,
    RecoveryForkID uniqueidentifier,
    Collation nvarchar(128),
    FamilyGUID uniqueidentifier,
    HasBulkLoggedData bit,
    IsSnapshot bit,
    IsReadOnly bit,
    IsSingleUser bit,
    HasBackupChecksums bit,
    IsDamaged bit,
    BeginsLogChain bit,
    HasIncompleteMetaData bit,
    IsForceOffline bit,
    IsCopyOnly bit,
    FirstRecoveryForkID uniqueidentifier,
    ForkPointLSN numeric(25,0) NULL,
    RecoveryModel nvarchar(60),
    DifferentialBaseLSN numeric(25,0) NULL,
    DifferentialBaseGUID uniqueidentifier,
    BackupTypeDescription nvarchar(60),
    BackupSetGUID uniqueidentifier NULL,
    CompressedBackupSize bigint,
    Seq int NOT NULL identity(1,1)
    )

  --> Create temp-table for db file information
  DECLARE @filelist TABLE  (
    LogicalName nvarchar(128),
    PhysicalName nvarchar(260),
    Type char(1),
    FileGroupName nvarchar(128),
    Size numeric(20,0),
    MaxSize numeric(20,0),
    FileID bigint,
    CreateLSN numeric(25,0),
    DropLSN numeric(25,0) NULL,
    UniqueID uniqueidentifier,
    ReadOnlyLSN numeric(25,0) NULL,
    ReadWriteLSN numeric(25,0) NULL,
    BackupSizeInBytes bigint,
    SourceBlockSize int,
    FileGroupID int,
    LogGroupGUID uniqueidentifier NULL,
    DifferentialBaseLSN numeric(25,0) NULL,
    DifferentialBaseGUID uniqueidentifier,
    IsReadOnly bit,
    IsPresent bit,
    TDEThumbprint varbinary(32),
    Seq int NOT NULL identity(1,1)
    )

  --> Get header and filelist information from the backup file
  INSERT INTO @header
  EXEC ('RESTORE HeaderOnly FROM DISK = ''' + @Filename + '''')

  INSERT INTO @filelist
  EXEC ('RESTORE FilelistOnly FROM DISK = ''' + @Filename + '''')

  --> Generate the full backup restore script
  SELECT
    @SQL = @SQL +
      CASE
        WHEN a.Seq = 1 THEN
          @cr + 'RESTORE DATABASE [' + c.DatabaseName + ']' +
          @cr + 'FROM DISK =' + @cr + @tab + '''' +
          @Filename + '''' + @cr + 'WITH'
        ELSE ''
      END
      + @cr + @tab + 'MOVE ''' + a.LogicalName + ''' TO ''' + a.PhysicalName + ''','
      +
      CASE
        WHEN a.Seq = b.Seq THEN
          @cr + @tab + 'REPLACE, STATS = 5, NORECOVERY'
        ELSE ''
      END
  FROM
    @filelist a
    CROSS JOIN
      (SELECT Seq = MAX(b1.Seq) FROM @filelist b1 ) b
    CROSS JOIN
      (SELECT DatabaseName = MAX(c1.DatabaseName) FROM @header c1) c
  ORDER BY
    a.Seq

  SELECT @SQL = @SQL + @cr + 'GO' + @cr + @cr

  --> Restore the last differential backup if it exists
  SELECT TOP 1
    @SQL = @SQL + 'RESTORE DATABASE [' + @DBName + '] FROM DISK = ''' + b.physical_device_name + ''' WITH NORECOVERY' + @cr + 'GO',
    @Diff_BackupStartDate = a.backup_start_date
  FROM msdb.dbo.backupset a
    INNER JOIN msdb.dbo.backupmediafamily b
      ON a.media_set_id = b.media_set_id
  WHERE a.database_name = @DBName
    AND a.type = 'I'
    AND a.backup_start_date > @Full_BackupStartDate
    AND a.backup_start_date < @PointInTime   ORDER BY a.backup_start_date DESC      IF @Diff_BackupStartDate IS NULL     SET @Diff_BackupStartDate = @Full_BackupStartDate        --> Generate all log restores except the last one
  SELECT
    @SQL = @SQL + 'RESTORE LOG [' + @DBName + '] FROM DISK = ''' + b.physical_device_name + ''' WITH NORECOVERY' + @cr + 'GO' + @cr,
    @Log_BackupStartDate = a.backup_start_date
  FROM msdb.dbo.backupset a
    INNER JOIN msdb.dbo.backupmediafamily b
      ON a.media_set_id = b.media_set_id
  WHERE a.database_name = @DBName
    AND a.type = 'L'
    AND a.backup_start_date > @Diff_BackupStartDate
    AND a.backup_start_date < @PointInTime   ORDER BY a.backup_start_date      --> Generate last log restore script with the stopat command and recovery
  SELECT TOP 1
    @SQL = @SQL + 'RESTORE LOG [' + @DBName + '] FROM DISK = ''' + b.physical_device_name + '''
      WITH RECOVERY, STOPAT = ''' + CONVERT(varchar(20), @PointInTime, 120) + '''' + @cr + 'GO' + @cr
  FROM msdb.dbo.backupset a
    INNER JOIN msdb.dbo.backupmediafamily b
      ON a.media_set_id = b.media_set_id
  WHERE a.database_name = @DBName
    AND a.type = 'L'
    AND a.backup_start_date > @PointInTime
  ORDER BY a.backup_start_date ASC

  PRINT @SQL

END TRY
BEGIN CATCH
  PRINT ERROR_MESSAGE()
END CATCH
Advertisements

Finding the most active 15 minute time period


A guy over at sqlteam.com wanted to find the most active 15-minute period for a series of different stores his company runs and I thought this would be a nice topic for a blog post. I modified the problem a little to fit a sample table I had lying around and the result wasn’t too hard to come by.

Basically I have a table with sample call records for a telephone company and I want to find the most active 15-minute period where the most calls are started. First we have to generate a table with some sample data:

create table calls (
	call_id int identity(1, 1) primary key clustered,
	a_number bigint,
	b_number bigint,
	start_time datetime,
	duration int)
 go

--> Function to generate random numbers, used to create sample data
create function randomnumber (@min bigint, @max bigint, @rand float)
returns bigint
as
begin
	return CAST(((@Max + 1) - @Min) * @Rand + @Min AS bigint)
end
go

--&gt; Generate 500  rows of sample data
declare 
	@end int = 500,
	@current int = 1
	
while (@current <= @end)
	begin
		insert into calls (a_number, b_number, start_time, duration)
		select 
			dbo.randomnumber (22000000, 22999999, RAND()),
			dbo.randomnumber (22000000, 22999999, RAND()),
			DATEADD(MINUTE, dbo.randomnumber (1, 300, RAND()),GETDATE()),
			dbo.randomnumber (3, 3600, RAND())
			
		select @current = @current + 1
	end
go

Now once the test table with sample data has been established, we can run the actual query:

DECLARE @dummydate datetime = '2010-06-23 00:00:00'

;WITH cte AS (
	SELECT 
		[Mod] = DATEDIFF(MILLISECOND, @dummydate, start_time) % (15 * 60 * 1000), --&gt; 15 mins to milliseconds
		*
	FROM calls
	)
SELECT TOP 10 
	Period = DATEADD(MILLISECOND, -[Mod], start_time), 
	CountOfTransID = COUNT(call_id)
FROM cte
GROUP BY DATEADD(MILLISECOND, -[Mod], start_time)
ORDER BY COUNT(call_id) DESC

Last backups in a glance


As most other DBA’s I’m lazy and I try to make things as easy as possible for myself without missing out on anything. And the most important task of my day is to check the latest backups on all servers and I don’t think it can be done any easier than this. Unfortunately I don’t have the luxury of having all my servers registered in Management Studio because of security regulations but if you do, running this as a multi-server query should make things *reeeally* easy for you:

--> Latest backups
USE master
GO

SET NOCOUNT ON
GO

SELECT
  database_name,
  backup_finish_date = CASE
    WHEN db_backup_finish_date &gt; log_backup_finish_date THEN db_backup_finish_date
    ELSE log_backup_finish_date
    END
FROM (
  SELECT
    database_name = a.[name],
    db_backup_finish_date = 
      COALESCE(MAX(db_backup.backup_finish_date), '1900-01-01 00:00:00'),
    log_backup_finish_date = 
      COALESCE(MAX(log_backup.backup_finish_date), '1900-01-01 00:00:00')
  FROM sys.databases a
    LEFT OUTER JOIN msdb..backupset AS db_backup
      ON a.[name] = db_backup.database_name
        AND db_backup.[Type] = 'D'
    LEFT OUTER JOIN msdb..backupset AS log_backup
      ON db_backup.database_name = log_backup.database_name
        AND log_backup.[Type] = 'L'
  GROUP BY a.[name]
  ) AS dt

Poor mans partitioning


There is absolutely nothing new about what I’m writing here, this is a really old trick back from the SQL 2000 days but there are quite a few people who are not aware of the powers of partitioning your data and this is why I’m posting. Unfortunately, native partitioning is for large corporations and companies with thick wallets where Enterprise version is the only way there is, while the rest of us will have to tweak that standard version until our eyes pop out. Well…for those of you that are not aware: there is a way to partition even with standard version and I’m gonna show you how. It will take a bit more effort than the native way and it doesn’t do everything you would hope for but it’s still pretty darn powerful.

And this little script here illustrates the whole concept; you create a few tables with some check constraints, you create a union all view and VOILA! There is your partitioning! Before you run the script make sure to turn on “Show actual execution plan” in your Management studio.

CREATE TABLE part_table1 (
  ID INT NOT NULL PRIMARY KEY CLUSTERED,
  SomeValue VARCHAR(20)
  )
GO
CREATE TABLE part_table2 (
  ID INT NOT NULL PRIMARY KEY CLUSTERED,
  SomeValue VARCHAR(20)
  )
GO
CREATE TABLE part_table3 (
  ID INT NOT NULL PRIMARY KEY CLUSTERED,
  SomeValue VARCHAR(20)
  )
GO

ALTER TABLE part_table1 WITH CHECK ADD CONSTRAINT CK_part_table1 CHECK (ID >= 0 AND ID <= 10) 
ALTER TABLE part_table1 CHECK CONSTRAINT CK_part_table1 

ALTER TABLE part_table2 WITH CHECK ADD CONSTRAINT CK_part_table2 CHECK (ID &gt;= 11 AND ID <= 100) 
ALTER TABLE part_table2 CHECK CONSTRAINT CK_part_table2 

ALTER TABLE part_table3 WITH CHECK ADD CONSTRAINT CK_part_table3 CHECK (ID &gt;= 101)
ALTER TABLE part_table3 CHECK CONSTRAINT CK_part_table3
GO

CREATE VIEW part_table
AS
SELECT * FROM part_table1
UNION ALL
SELECT * FROM part_table2
UNION ALL
SELECT * FROM part_table3
GO

INSERT INTO part_table SELECT 9, 'sqlteam'
INSERT INTO part_table SELECT 24, 'sqlteam'
INSERT INTO part_table2 SELECT 25, 'sqlteam'

SELECT * FROM part_table WHERE ID = 25

Notice in the execution plan when you do the inserts and the select. The insert is a clustered insert but as you can see it scans all the tables to find the right one which is somewhat annoying. But when you look at the final select you see where the “magic” is happening: when you do a select based on the “partitioning column” the query optimizer actually knows which data is where so it only has to search one of the tables! Pretty cool stuff…at least somewhat cool hehe. Using this method you can utilize quite a few of the partitioning techniques that are built in the native partitioning. You can for example have each partition on a different disk and you can add and remove partitions from the view as you like, just remember to do changes in the view first before you delete or rename any tables permanently…

Using sqlcmd from Powershell


I’ve been doing some Sharepoint 2010 stuff lately and one of the tasks I had to accomplish was to automate creating some databases using Powershell. Never having used Powershell before I was somewhat intrigued by the task, but it proved to be a little more hassle than I expected, and included voting up a bug in the sqlcmds “-Variable”-setting!! Check the details here: connect.microsoft.com/sqlserver/…/

What I basically needed to do was to create a database using Powershell, sqlcmd and a .sql script-file that accepted parameters. Sounds easy enough? Well…I might not me the smartest guy in the world but I didn’t expect to spend as much time as I did on it (the bug in invoke-sqlcmd taking most of the time to find out). Here’s what I ended up with:

The powershell script create_database.ps1

#Parameters that must be set:
$DBServer = "192.168.1.10"
$DBDataPath = "D:\MSSQL\Data\"
$DBLogPath = "E:\MSSQL\Log\"
$DBScriptFile = "c:\create databases.sql"

#Add snap-ins and create parameters in the correct format
add-pssnapin sqlserverprovidersnapin100
add-pssnapin sqlservercmdletsnapin100
$DBParam1 = "DBDataPath=" + $DBDataPath
$DBParam2 = "DBLogPath=" + $DBLogPath
$DBParams = $DBParam1, $DBParam2
Invoke-Sqlcmd -InputFile $DBScriptFile -Variable $DBParams -Serverinstance $DBServer -E

And here is the file create_databases.sql

CREATE
DATABASE [SharePoint_Config] ON PRIMARY
(NAME = N'SharePoint_Config', FILENAME = N'$(DBDataPath)SharePoint_Config.mdf' , SIZE = 100MB , MAXSIZE = UNLIMITED, FILEGROWTH = 10% )
LOG ON
(NAME = N'SharePoint_Config_log', FILENAME = N'$(DBLogPath)SharePoint_Config.ldf' , SIZE = 100MB , MAXSIZE = UNLIMITED , FILEGROWTH = 10%)
COLLATE Latin1_General_CI_AS_KS_WS
GO

Create as many parameters as you want, just keep in mind not to have spaces around the powershell parameters when defining them!!

This works:
$DBParam1 = “DBDataPath=” + $DBDataPath

This will not work:
$DBParam1 = “DBDataPath = ” + $DBDataPath

Create an audit table on the fly


The following is a script that generates an audit table for a provided table name.

It will:
– Add 5 audit columns to an existing table (DataVersion, CreatedDate, CreatedBy, ModifiedDate, ModifiedBy)
– Create 2 triggers on the production table, one for updates and one for deletes (inserts do not need audit info)
– Create an audit table with similar design as the production table
– Create a primary key index on the audit table matching that of the production table but with the DataVersion also added as a part of it

Prerequisites:
– The stored procedure “sp_ScriptTableForAudit” must be present in the database (provided here)
– The script must be run in the database that the production table resides.
– The script does not handle table names with other schemas than dbo
– The table must have a primary key

Please add feedback/bug reports etc as comments and I will try to fix them right away

/*
Script created by Henning Frettem, aka Lumbago @ SQLTeam, 2010-03-23
BE CAREFUL, THIS SCRIPT WILL MAKE CHANGES TO YOUR TABLES!!
*/

SET NOCOUNT ON

BEGIN TRY

	DECLARE @TableName varchar(100); SET @TableName = 'myTable'
	DECLARE @AuditTableName varchar(200)

	--> Checking prerequisites
	IF NOT EXISTS (
			SELECT 1
			FROM sys.index_columns sc
			  JOIN sys.indexes i
				ON sc.object_id = i.object_id
				  AND sc.index_id = i.index_id
			WHERE OBJECT_NAME(sc.object_id) = @TableName 
			  AND i.is_primary_key = 1)
		RAISERROR('There is no primary key on the table', 16, 1)

	IF NOT EXISTS (SELECT 1 FROM INFORMATION_SCHEMA.ROUTINES WHERE ROUTINE_NAME = 'sp_ScriptTableForAudit')
		RAISERROR('The stored procedure sp_ScriptTableForAudit is not present in the database', 16, 1)

	SET @AuditTableName = @TableName + '_audit'

	DECLARE @SQL nvarchar(max); SET @SQL = ''

	--> Add audit columns to the production table if they don't exist already
	IF NOT EXISTS(SELECT 1 FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = @Tablename AND COLUMN_NAME = 'DataVersion')
	  SET @SQL = @SQL + 'ALTER TABLE ' + @TableName + ' ADD DataVersion int NOT NULL DEFAULT 1' + CHAR(13)

	IF NOT EXISTS(SELECT 1 FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = @Tablename AND COLUMN_NAME = 'CreatedDate')
	  SET @SQL = @SQL + 'ALTER TABLE ' + @TableName + ' ADD CreatedDate datetime NOT NULL DEFAULT GETDATE()' + CHAR(13)

	IF NOT EXISTS(SELECT 1 FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = @Tablename AND COLUMN_NAME = 'CreatedBy')
	  SET @SQL = @SQL + 'ALTER TABLE ' + @TableName + ' ADD CreatedBy nvarchar(200) NOT NULL DEFAULT SUSER_NAME()' + CHAR(13)

	IF NOT EXISTS(SELECT 1 FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = @Tablename AND COLUMN_NAME = 'ModifiedDate')
	  SET @SQL = @SQL + 'ALTER TABLE ' + @TableName + ' ADD ModifiedDate datetime NOT NULL DEFAULT GETDATE()' + CHAR(13)

	IF NOT EXISTS(SELECT 1 FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = @Tablename AND COLUMN_NAME = 'ModifiedBy')
	  SET @SQL = @SQL + 'ALTER TABLE ' + @TableName + ' ADD ModifiedBy nvarchar(200) NOT NULL DEFAULT SUSER_NAME()' + CHAR(13)

	IF LEN(@SQL) > 0
	  BEGIN
		PRINT @SQL
		EXEC sp_executesql @SQL
		SET @SQL = ''
	  END

	--> Create the audit table with the same definition as the production table, but add the IsDeleted column
	IF NOT EXISTS (SELECT 1 FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = @AuditTableName)
	  BEGIN
		DECLARE @AuditTableDefinition table (
		  RowNumber int IDENTITY(1, 1),
		  DefinitionRow varchar(max)
		  )

		INSERT INTO @AuditTableDefinition
		EXEC sp_ScriptTableForAudit @Tablename = @TableName, @NewTablename = @AuditTableName

		--> Need IsDeleted column in audit table
		UPDATE @AuditTableDefinition
		  SET DefinitionRow = ', IsDeleted bit NOT NULL DEFAULT(0) )'
		WHERE RowNumber = (SELECT MAX(RowNumber) FROM @AuditTableDefinition)

		SELECT @SQL = @SQL + DefinitionRow FROM @AuditTableDefinition

		--> Run the create table script in the database
		PRINT @SQL
		EXEC sp_executesql @SQL
		SET @SQL = ''
	  END

	--> Create Update trigger
	IF NOT EXISTS (SELECT 1 FROM sys.triggers WHERE name = 'trg' + REPLACE(@TableName, '.', '') + 'Update')
	  BEGIN
		SET @SQL = @SQL + '
		  CREATE TRIGGER [trg' + REPLACE(@TableName, '.', '') + 'Update] ON ' + @TableName + '
		  FOR UPDATE
		  AS

		  SET NOCOUNT ON

		  UPDATE a SET
			a.DataVersion = b.DataVersion + 1,
			a.ModifiedDate = GETDATE(),
			a.ModifiedBy = SYSTEM_USER
		  FROM ' + @TableName + ' a
			INNER JOIN inserted b
			  ON '

		SELECT @SQL = @SQL + 'a.' + c.name + ' = b.' + c.name + ' AND '
		FROM sys.index_columns sc
		  JOIN sys.columns c
			ON sc.object_id = c.object_id
			  AND sc.column_id = c.column_id
		  JOIN sys.indexes i
			ON sc.object_id = i.object_id
			  AND sc.index_id = i.index_id
		WHERE OBJECT_NAME(sc.object_id) = @TableName
		  AND i.is_primary_key = 1
		ORDER BY index_column_id ASC

		SET @SQL = LEFT(@SQL, LEN(@SQL) - 4) + CHAR(13) + CHAR(13)

		SET @SQL = @SQL + '  INSERT INTO ' + @AuditTableName + '
			('

		SELECT @SQL = @SQL + '[' + COLUMN_NAME + '], '
		FROM INFORMATION_SCHEMA.COLUMNS
		WHERE TABLE_NAME = @AuditTableName

		SET @SQL = LEFT(@SQL, LEN(@SQL) - 1)
		SET @SQL = @SQL + ')
		  SELECT '

		SELECT @SQL = @SQL + '[' + COLUMN_NAME + '], '
		FROM INFORMATION_SCHEMA.COLUMNS
		WHERE TABLE_NAME = @AuditTableName

		SET @SQL = LEFT(@SQL, LEN(@SQL) - 1)
		SET @SQL = @SQL + ' = 0
		  FROM deleted

		  GO
		  '

		PRINT @SQL
		EXEC sp_executesql @SQL
		SET @SQL = ''
	  END

	--> Create delete trigger
	IF NOT EXISTS (SELECT 1 FROM sys.triggers WHERE name = 'trg' + REPLACE(@TableName, '.', '') + 'Delete')
	  BEGIN
		SET @SQL = @SQL + '
		  CREATE TRIGGER [trg' + REPLACE(@TableName, '.', '') + 'Delete] ON ' + @TableName + '
		  FOR DELETE
		  AS

		  SET NOCOUNT ON

		  INSERT INTO ' + @AuditTableName + '
			('

		SELECT @SQL = @SQL + '[' + COLUMN_NAME + '], '
		FROM INFORMATION_SCHEMA.COLUMNS
		WHERE TABLE_NAME = @AuditTableName

		SET @SQL = LEFT(@SQL, LEN(@SQL) - 1)
		SET @SQL = @SQL + ')
		  SELECT '

		SELECT @SQL = @SQL + '[' + COLUMN_NAME + '], '
		FROM INFORMATION_SCHEMA.COLUMNS
		WHERE TABLE_NAME = @AuditTableName

		SET @SQL = LEFT(@SQL, LEN(@SQL) - 1)
		SET @SQL = @SQL + ' = 1
		  FROM deleted

		  GO'

		PRINT @SQL
		EXEC sp_executesql @SQL
		SET @SQL = ''
	  END
END TRY
BEGIN CATCH
    PRINT Error_Message()
END CATCH