IF OBJECT_ID('dbo.sp_DatabaseRestore') IS NULL EXEC ('CREATE PROCEDURE dbo.sp_DatabaseRestore AS RETURN 0;'); GO ALTER PROCEDURE [dbo].[sp_DatabaseRestore] @Database NVARCHAR(128) = NULL, @RestoreDatabaseName NVARCHAR(128) = NULL, @BackupPathFull NVARCHAR(MAX) = NULL, @BackupPathDiff NVARCHAR(MAX) = NULL, @BackupPathLog NVARCHAR(MAX) = NULL, @MoveFiles BIT = 0, @MoveDataDrive NVARCHAR(260) = NULL, @MoveLogDrive NVARCHAR(260) = NULL, @TestRestore BIT = 0, @RunCheckDB BIT = 0, @RestoreDiff BIT = 0, @ContinueLogs BIT = 0, @RunRecovery BIT = 0, @ForceSimpleRecovery BIT = 0, @StopAt NVARCHAR(14) = NULL, @OnlyLogsAfter NVARCHAR(14) = NULL, @Debug INT = 0, @Help BIT = 0, @VersionDate DATETIME = NULL OUTPUT AS SET NOCOUNT ON; /*Versioning details*/ DECLARE @Version NVARCHAR(30); SET @Version = '5.9.5'; SET @VersionDate = '20171115'; IF @Help = 1 BEGIN PRINT ' /* sp_DatabaseRestore from http://FirstResponderKit.org This script will restore a database from a given file path. To learn more, visit http://FirstResponderKit.org where you can download new versions for free, watch training videos on how it works, get more info on the findings, contribute your own code, and more. Known limitations of this version: - Only Microsoft-supported versions of SQL Server. Sorry, 2005 and 2000. - Tastes awful with marmite. Unknown limitations of this version: - None. (If we knew them, they would be known. Duh.) Changes - for the full list of improvements and fixes in this version, see: https://github.com/BrentOzarULTD/SQL-Server-First-Responder-Kit/ MIT License Copyright for portions of sp_Blitz are held by Microsoft as part of project tigertoolbox and are provided under the MIT license: https://github.com/Microsoft/tigertoolbox All other copyright for sp_Blitz are held by Brent Ozar Unlimited, 2017. Copyright (c) 2017 Brent Ozar Unlimited Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ '; PRINT ' /* EXEC dbo.sp_DatabaseRestore @Database = ''LogShipMe'', @BackupPathFull = ''D:\Backup\SQL2016PROD1A\LogShipMe\FULL\'', @BackupPathLog = ''D:\Backup\SQL2016PROD1A\LogShipMe\LOG\'', @ContinueLogs = 0, @RunRecovery = 0; EXEC dbo.sp_DatabaseRestore @Database = ''LogShipMe'', @BackupPathFull = ''D:\Backup\SQL2016PROD1A\LogShipMe\FULL\'', @BackupPathLog = ''D:\Backup\SQL2016PROD1A\LogShipMe\LOG\'', @ContinueLogs = 1, @RunRecovery = 0; EXEC dbo.sp_DatabaseRestore @Database = ''LogShipMe'', @BackupPathFull = ''D:\Backup\SQL2016PROD1A\LogShipMe\FULL\'', @BackupPathLog = ''D:\Backup\SQL2016PROD1A\LogShipMe\LOG\'', @ContinueLogs = 1, @RunRecovery = 1; EXEC dbo.sp_DatabaseRestore @Database = ''LogShipMe'', @BackupPathFull = ''D:\Backup\SQL2016PROD1A\LogShipMe\FULL\'', @BackupPathLog = ''D:\Backup\SQL2016PROD1A\LogShipMe\LOG\'', @ContinueLogs = 0, @RunRecovery = 1; EXEC dbo.sp_DatabaseRestore @Database = ''LogShipMe'', @BackupPathFull = ''D:\Backup\SQL2016PROD1A\LogShipMe\FULL\'', @BackupPathDiff = ''D:\Backup\SQL2016PROD1A\LogShipMe\DIFF\'', @BackupPathLog = ''D:\Backup\SQL2016PROD1A\LogShipMe\LOG\'', @RestoreDiff = 1, @ContinueLogs = 0, @RunRecovery = 1; EXEC dbo.sp_DatabaseRestore @Database = ''LogShipMe'', @BackupPathFull = ''\\StorageServer\LogShipMe\FULL\'', @BackupPathDiff = ''\\StorageServer\LogShipMe\DIFF\'', @BackupPathLog = ''\\StorageServer\LogShipMe\LOG\'', @RestoreDiff = 1, @ContinueLogs = 0, @RunRecovery = 1, @TestRestore = 1, @RunCheckDB = 1, @Debug = 0; --This example will restore the latest differential backup, and stop transaction logs at the specified date time. It will also only print the commands. EXEC dbo.sp_DatabaseRestore @Database = ''DBA'', @BackupPathFull = ''\\StorageServer\LogShipMe\FULL\'', @BackupPathDiff = ''\\StorageServer\LogShipMe\DIFF\'', @BackupPathLog = ''\\StorageServer\LogShipMe\LOG\'', @RestoreDiff = 1, @ContinueLogs = 0, @RunRecovery = 1, @StopAt = ''20170508201501'', @Debug = 1; Variables: @RestoreDiff - This variable is a flag for whether or not the script is expecting to restore differentials @StopAt - This variable is used to restore transaction logs to a specific date and time. The format must be in YYYYMMDDHHMMSS. The time is in military format. About Debug Modes: There are 3 Debug Modes. Mode 0 is the default and will execute the script. Debug 1 will print just the commands. Debug 2 will print other useful information that has mostly been useful for troubleshooting. Debug 2 needs to be expanded to make it more useful. */ '; RETURN; END; -- Get the SQL Server version number because the columns returned by RESTORE commands vary by version -- Based on: https://www.brentozar.com/archive/2015/05/sql-server-version-detection/ -- Need to capture BuildVersion because RESTORE HEADERONLY changed with 2014 CU1, not RTM DECLARE @ProductVersion AS NVARCHAR(20) = CAST(SERVERPROPERTY ('productversion') AS NVARCHAR(20)); DECLARE @MajorVersion AS SMALLINT = CAST(PARSENAME(@ProductVersion, 4) AS SMALLINT); DECLARE @MinorVersion AS SMALLINT = CAST(PARSENAME(@ProductVersion, 3) AS SMALLINT); DECLARE @BuildVersion AS SMALLINT = CAST(PARSENAME(@ProductVersion, 2) AS SMALLINT); IF @MajorVersion < 10 BEGIN RAISERROR('Sorry, DatabaseRestore doesn''t work on versions of SQL prior to 2008.', 15, 1); RETURN; END; DECLARE @cmd NVARCHAR(4000) = N'', --Holds xp_cmdshell command @sql NVARCHAR(MAX) = N'', --Holds executable SQL commands @LastFullBackup NVARCHAR(500) = N'', --Last full backup name @LastDiffBackup NVARCHAR(500) = N'', --Last diff backup name @LastDiffBackupDateTime NVARCHAR(500) = N'', --Last diff backup date @BackupFile NVARCHAR(500) = N'', --Name of backup file @BackupDateTime AS CHAR(15) = N'', --Used for comparisons to generate ordered backup files/create a stopat point @FullLastLSN NUMERIC(25, 0), --LSN for full @DiffLastLSN NUMERIC(25, 0), --LSN for diff @HeadersSQL AS NVARCHAR(4000) = N'', --Dynamic insert into #Headers table (deals with varying results from RESTORE FILELISTONLY across different versions) @MoveOption AS NVARCHAR(MAX)= N'', --If you need to move restored files to a different directory @DatabaseLastLSN NUMERIC(25, 0), --redo_start_lsn of the current database @i TINYINT = 1, --Maintains loop to continue logs @LogFirstLSN NUMERIC(25, 0), --Holds first LSN in log backup headers @LogLastLSN NUMERIC(25, 0), --Holds last LSN in log backup headers @FileListParamSQL NVARCHAR(4000) = N''; --Holds INSERT list for #FileListParameters DECLARE @FileList TABLE ( BackupFile NVARCHAR(255) ); IF OBJECT_ID(N'tempdb..#FileListParameters') IS NOT NULL DROP TABLE #FileListParameters; CREATE TABLE #FileListParameters ( LogicalName NVARCHAR(128) NOT NULL, PhysicalName NVARCHAR(260) NOT NULL, Type CHAR(1) NOT NULL, FileGroupName NVARCHAR(120) NULL, Size NUMERIC(20, 0) NOT NULL, MaxSize NUMERIC(20, 0) NOT NULL, FileID BIGINT NULL, CreateLSN NUMERIC(25, 0) NULL, DropLSN NUMERIC(25, 0) NULL, UniqueID UNIQUEIDENTIFIER NULL, ReadOnlyLSN NUMERIC(25, 0) NULL, ReadWriteLSN NUMERIC(25, 0) NULL, BackupSizeInBytes BIGINT NULL, SourceBlockSize INT NULL, FileGroupID INT NULL, LogGroupGUID UNIQUEIDENTIFIER NULL, DifferentialBaseLSN NUMERIC(25, 0) NULL, DifferentialBaseGUID UNIQUEIDENTIFIER NULL, IsReadOnly BIT NULL, IsPresent BIT NULL, TDEThumbprint VARBINARY(32) NULL, SnapshotUrl NVARCHAR(360) NULL ); IF OBJECT_ID(N'tempdb..#Headers') IS NOT NULL DROP TABLE #Headers; CREATE TABLE #Headers ( BackupName NVARCHAR(256), BackupDescription NVARCHAR(256), BackupType NVARCHAR(256), ExpirationDate NVARCHAR(256), Compressed NVARCHAR(256), Position NVARCHAR(256), DeviceType NVARCHAR(256), UserName NVARCHAR(256), ServerName NVARCHAR(256), DatabaseName NVARCHAR(256), DatabaseVersion NVARCHAR(256), DatabaseCreationDate NVARCHAR(256), BackupSize NVARCHAR(256), FirstLSN NVARCHAR(256), LastLSN NVARCHAR(256), CheckpointLSN NVARCHAR(256), DatabaseBackupLSN NVARCHAR(256), BackupStartDate NVARCHAR(256), BackupFinishDate NVARCHAR(256), SortOrder NVARCHAR(256), CodePage NVARCHAR(256), UnicodeLocaleId NVARCHAR(256), UnicodeComparisonStyle NVARCHAR(256), CompatibilityLevel NVARCHAR(256), SoftwareVendorId NVARCHAR(256), SoftwareVersionMajor NVARCHAR(256), SoftwareVersionMinor NVARCHAR(256), SoftwareVersionBuild NVARCHAR(256), MachineName NVARCHAR(256), Flags NVARCHAR(256), BindingID NVARCHAR(256), RecoveryForkID NVARCHAR(256), Collation NVARCHAR(256), FamilyGUID NVARCHAR(256), HasBulkLoggedData NVARCHAR(256), IsSnapshot NVARCHAR(256), IsReadOnly NVARCHAR(256), IsSingleUser NVARCHAR(256), HasBackupChecksums NVARCHAR(256), IsDamaged NVARCHAR(256), BeginsLogChain NVARCHAR(256), HasIncompleteMetaData NVARCHAR(256), IsForceOffline NVARCHAR(256), IsCopyOnly NVARCHAR(256), FirstRecoveryForkID NVARCHAR(256), ForkPointLSN NVARCHAR(256), RecoveryModel NVARCHAR(256), DifferentialBaseLSN NVARCHAR(256), DifferentialBaseGUID NVARCHAR(256), BackupTypeDescription NVARCHAR(256), BackupSetGUID NVARCHAR(256), CompressedBackupSize NVARCHAR(256), Containment NVARCHAR(256), KeyAlgorithm NVARCHAR(32), EncryptorThumbprint VARBINARY(20), EncryptorType NVARCHAR(32), -- -- Seq added to retain order by -- Seq INT NOT NULL IDENTITY(1, 1) ); /* Correct paths in case people forget a final "\" */ /*Full*/ IF (SELECT RIGHT(@BackupPathFull, 1)) <> '\' --Has to end in a '\' BEGIN RAISERROR('Fixing @BackupPathFull to add a "\"', 0, 1) WITH NOWAIT; SET @BackupPathFull += N'\'; END; /*Diff*/ IF (SELECT RIGHT(@BackupPathDiff, 1)) <> '\' --Has to end in a '\' BEGIN RAISERROR('Fixing @BackupPathDiff to add a "\"', 0, 1) WITH NOWAIT; SET @BackupPathDiff += N'\'; END; /*Log*/ IF (SELECT RIGHT(@BackupPathLog, 1)) <> '\' --Has to end in a '\' BEGIN RAISERROR('Fixing @BackupPathLog to add a "\"', 0, 1) WITH NOWAIT; SET @BackupPathLog += N'\'; END; /*Move Data File*/ IF NULLIF(@MoveDataDrive, '') IS NULL BEGIN RAISERROR('Getting default data drive for @MoveDataDrive', 0, 1) WITH NOWAIT; SET @MoveDataDrive = CAST(SERVERPROPERTY('InstanceDefaultDataPath') AS nvarchar(260)); END; IF (SELECT RIGHT(@MoveDataDrive, 1)) <> '\' --Has to end in a '\' BEGIN RAISERROR('Fixing @MoveDataDrive to add a "\"', 0, 1) WITH NOWAIT; SET @MoveDataDrive += N'\'; END; /*Move Log File*/ IF NULLIF(@MoveLogDrive, '') IS NULL BEGIN RAISERROR('Getting default log drive for @@MoveLogDrive', 0, 1) WITH NOWAIT; SET @MoveLogDrive = CAST(SERVERPROPERTY('InstanceDefaultLogPath') AS nvarchar(260)); END; IF (SELECT RIGHT(@MoveLogDrive, 1)) <> '\' --Has to end in a '\' BEGIN RAISERROR('Fixing @MoveDataDrive to add a "\"', 0, 1) WITH NOWAIT; SET @MoveLogDrive += N'\'; END; IF @RestoreDatabaseName IS NULL BEGIN SET @RestoreDatabaseName = QUOTENAME(@Database); END ELSE BEGIN SET @RestoreDatabaseName = QUOTENAME(@RestoreDatabaseName) END IF @BackupPathFull IS NOT NULL BEGIN -- get list of files SET @cmd = N'DIR /b "' + @BackupPathFull + N'"'; IF @Debug = 1 BEGIN IF @cmd IS NULL PRINT '@cmd is NULL for @BackupPathFull'; PRINT @cmd; END; INSERT INTO @FileList (BackupFile) EXEC master.sys.xp_cmdshell @cmd; /*Sanity check folders*/ IF ( SELECT COUNT(*) FROM @FileList AS fl WHERE fl.BackupFile = 'The system cannot find the path specified.' OR fl.BackupFile = 'File Not Found' ) = 1 BEGIN RAISERROR('No rows were returned for that database\path', 0, 1) WITH NOWAIT; END; IF ( SELECT COUNT(*) FROM @FileList AS fl WHERE fl.BackupFile = 'Access is denied.' ) = 1 BEGIN RAISERROR('Access is denied to %s', 16, 1, @BackupPathFull) WITH NOWAIT; END; IF ( SELECT COUNT(*) FROM @FileList AS fl ) = 1 AND ( SELECT COUNT(*) FROM @FileList AS fl WHERE fl.BackupFile IS NULL ) = 1 BEGIN RAISERROR('That directory appears to be empty', 0, 1) WITH NOWAIT; RETURN; END IF ( SELECT COUNT(*) FROM @FileList AS fl WHERE fl.BackupFile = 'The user name or password is incorrect.' ) = 1 BEGIN RAISERROR('Incorrect user name or password for %s', 16, 1, @BackupPathFull) WITH NOWAIT; END; /*End folder sanity check*/ -- Find latest full backup SELECT @LastFullBackup = MAX(BackupFile) FROM @FileList WHERE BackupFile LIKE N'%.bak' AND BackupFile LIKE N'%' + @Database + N'%' AND (@StopAt IS NULL OR REPLACE(LEFT(RIGHT(BackupFile, 19), 15),'_','') <= @StopAt); IF @Debug = 1 BEGIN SELECT * FROM @FileList; END; SET @FileListParamSQL = N'INSERT INTO #FileListParameters WITH (TABLOCK) (LogicalName, PhysicalName, Type, FileGroupName, Size, MaxSize, FileID, CreateLSN, DropLSN ,UniqueID, ReadOnlyLSN, ReadWriteLSN, BackupSizeInBytes, SourceBlockSize, FileGroupID, LogGroupGUID ,DifferentialBaseLSN, DifferentialBaseGUID, IsReadOnly, IsPresent, TDEThumbprint'; IF @MajorVersion >= 13 BEGIN SET @FileListParamSQL += N', SnapshotUrl'; END; SET @FileListParamSQL += N')' + NCHAR(13) + NCHAR(10); SET @FileListParamSQL += N'EXEC (''RESTORE FILELISTONLY FROM DISK=''''{Path}'''''')'; SET @sql = REPLACE(@FileListParamSQL, N'{Path}', @BackupPathFull + @LastFullBackup); IF @Debug = 1 BEGIN IF @sql IS NULL PRINT '@sql is NULL for INSERT to #FileListParameters: @BackupPathFull + @LastFullBackup'; PRINT @sql; END; EXEC (@sql); IF @Debug = 1 BEGIN SELECT '#FileListParameters' AS table_name, * FROM #FileListParameters END SET @HeadersSQL = N'INSERT INTO #Headers WITH (TABLOCK) (BackupName, BackupDescription, BackupType, ExpirationDate, Compressed, Position, DeviceType, UserName, ServerName ,DatabaseName, DatabaseVersion, DatabaseCreationDate, BackupSize, FirstLSN, LastLSN, CheckpointLSN, DatabaseBackupLSN ,BackupStartDate, BackupFinishDate, SortOrder, CodePage, UnicodeLocaleId, UnicodeComparisonStyle, CompatibilityLevel ,SoftwareVendorId, SoftwareVersionMajor, SoftwareVersionMinor, SoftwareVersionBuild, MachineName, Flags, BindingID ,RecoveryForkID, Collation, FamilyGUID, HasBulkLoggedData, IsSnapshot, IsReadOnly, IsSingleUser, HasBackupChecksums ,IsDamaged, BeginsLogChain, HasIncompleteMetaData, IsForceOffline, IsCopyOnly, FirstRecoveryForkID, ForkPointLSN ,RecoveryModel, DifferentialBaseLSN, DifferentialBaseGUID, BackupTypeDescription, BackupSetGUID, CompressedBackupSize'; IF @MajorVersion >= 11 SET @HeadersSQL += NCHAR(13) + NCHAR(10) + N', Containment'; IF @MajorVersion >= 13 OR (@MajorVersion = 12 AND @BuildVersion >= 2342) SET @HeadersSQL += N', KeyAlgorithm, EncryptorThumbprint, EncryptorType'; SET @HeadersSQL += N')' + NCHAR(13) + NCHAR(10); SET @HeadersSQL += N'EXEC (''RESTORE HEADERONLY FROM DISK=''''{Path}'''''')'; IF @MoveFiles = 1 BEGIN RAISERROR('@MoveFiles = 1, adjusting paths', 0, 1) WITH NOWAIT; WITH Files AS ( SELECT N', MOVE ''' + LogicalName + N''' TO ''' + CASE WHEN Type = 'D' THEN @MoveDataDrive WHEN Type = 'L' THEN @MoveLogDrive END + CASE WHEN @Database = @RestoreDatabaseName THEN REVERSE(LEFT(REVERSE(PhysicalName), CHARINDEX('\', REVERSE(PhysicalName), 1) -1)) + '''' ELSE REPLACE(REVERSE(LEFT(REVERSE(PhysicalName), CHARINDEX('\', REVERSE(PhysicalName), 1) -1)), @Database, SUBSTRING(@RestoreDatabaseName, 2, LEN(@RestoreDatabaseName) -2)) + '''' END AS logicalcmds FROM #FileListParameters) SELECT @MoveOption = @MoveOption + Files.logicalcmds FROM Files; IF @Debug = 1 PRINT @MoveOption END; IF @ContinueLogs = 0 BEGIN RAISERROR('@ContinueLogs set to 0', 0, 1) WITH NOWAIT; SET @sql = N'RESTORE DATABASE ' + @RestoreDatabaseName + N' FROM DISK = ''' + @BackupPathFull + @LastFullBackup + N''' WITH NORECOVERY, REPLACE' + @MoveOption + NCHAR(13); IF @Debug = 1 BEGIN IF @sql IS NULL PRINT '@sql is NULL for RESTORE DATABASE: @BackupPathFull, @LastFullBackup, @MoveOption'; PRINT @sql; END; IF @Debug IN (0, 1) EXECUTE @sql = [dbo].[CommandExecute] @Command = @sql, @CommandType = 'RESTORE DATABASE', @Mode = 1, @DatabaseName = @Database, @LogToTable = 'Y', @Execute = 'Y'; --get the backup completed data so we can apply tlogs from that point forwards SET @sql = REPLACE(@HeadersSQL, N'{Path}', @BackupPathFull + @LastFullBackup); IF @Debug = 1 BEGIN IF @sql IS NULL PRINT '@sql is NULL for get backup completed data: @BackupPathFull, @LastFullBackup'; PRINT @sql; END; EXECUTE (@sql); --setting the @BackupDateTime to a numeric string so that it can be used in comparisons SET @BackupDateTime = REPLACE(LEFT(RIGHT(@LastFullBackup, 19),15), '_', ''); SELECT @FullLastLSN = CAST(LastLSN AS NUMERIC(25, 0)) FROM #Headers WHERE BackupType = 1; IF @Debug = 1 BEGIN IF @BackupDateTime IS NULL PRINT '@BackupDateTime is NULL for REPLACE: @LastFullBackup'; PRINT @BackupDateTime; END; END; ELSE BEGIN SELECT @DatabaseLastLSN = CAST(f.redo_start_lsn AS NUMERIC(25, 0)) FROM master.sys.databases d JOIN master.sys.master_files f ON d.database_id = f.database_id WHERE d.name = SUBSTRING(@RestoreDatabaseName, 2, LEN(@RestoreDatabaseName) - 2) AND f.file_id = 1; END; --Clear out table variables for differential DELETE FROM @FileList; END IF @BackupPathDiff IS NOT NULL BEGIN -- get list of files SET @cmd = N'DIR /b "'+ @BackupPathDiff + N'"'; IF @Debug = 1 BEGIN IF @cmd IS NULL PRINT '@cmd is NULL for @BackupPathDiff check'; PRINT @cmd; END; IF @Debug = 1 BEGIN SELECT * FROM @FileList; END; INSERT INTO @FileList (BackupFile) EXEC master.sys.xp_cmdshell @cmd; /*Sanity check folders*/ IF ( SELECT COUNT(*) FROM @FileList AS fl WHERE fl.BackupFile = 'The system cannot find the path specified.' OR fl.BackupFile = 'File Not Found' ) = 1 BEGIN RAISERROR('No rows were returned for that database\path', 0, 1) WITH NOWAIT; END; IF ( SELECT COUNT(*) FROM @FileList AS fl WHERE fl.BackupFile = 'Access is denied.' ) = 1 BEGIN RAISERROR('Access is denied to %s', 16, 1, @BackupPathDiff) WITH NOWAIT; END; IF ( SELECT COUNT(*) FROM @FileList AS fl ) = 1 AND ( SELECT COUNT(*) FROM @FileList AS fl WHERE fl.BackupFile IS NULL ) = 1 BEGIN RAISERROR('That directory appears to be empty', 0, 1) WITH NOWAIT; RETURN; END /*End folder sanity check*/ -- Find latest diff backup SELECT @LastDiffBackup = MAX(BackupFile) FROM @FileList WHERE BackupFile LIKE N'%.bak' AND BackupFile LIKE N'%' + @Database + '%' AND (@StopAt IS NULL OR REPLACE(LEFT(RIGHT(BackupFile, 19), 15),'_','') <= @StopAt); --set the @BackupDateTime so that it can be used for comparisons SET @BackupDateTime = REPLACE(@BackupDateTime, '_', ''); SET @LastDiffBackupDateTime = REPLACE(LEFT(RIGHT(@LastDiffBackup, 19),15), '_', ''); IF @RestoreDiff = 1 AND @BackupDateTime < @LastDiffBackupDateTime BEGIN SET @sql = N'RESTORE DATABASE ' + @RestoreDatabaseName + N' FROM DISK = ''' + @BackupPathDiff + @LastDiffBackup + N''' WITH NORECOVERY' + NCHAR(13); IF @Debug = 1 BEGIN IF @sql IS NULL PRINT '@sql is NULL for RESTORE DATABASE: @BackupPathDiff, @LastDiffBackup'; PRINT @sql; END; IF @Debug IN (0, 1) EXECUTE @sql = [dbo].[CommandExecute] @Command = @sql, @CommandType = 'RESTORE DATABASE', @Mode = 1, @DatabaseName = @Database, @LogToTable = 'Y', @Execute = 'Y'; --get the backup completed data so we can apply tlogs from that point forwards SET @sql = REPLACE(@HeadersSQL, N'{Path}', @BackupPathDiff + @LastDiffBackup); IF @Debug = 1 BEGIN IF @sql IS NULL PRINT '@sql is NULL for REPLACE: @BackupPathDiff, @LastDiffBackup'; PRINT @sql; END; EXECUTE (@sql); IF @Debug = 1 BEGIN SELECT '#Headers' AS table_name, * FROM #Headers AS h END --set the @BackupDateTime to the date time on the most recent differential SET @BackupDateTime = @LastDiffBackupDateTime; SELECT @DiffLastLSN = CAST(LastLSN AS NUMERIC(25, 0)) FROM #Headers WHERE BackupType = 5; END; --Clear out table variables for translogs DELETE FROM @FileList; END IF @BackupPathLog IS NOT NULL BEGIN SET @cmd = N'DIR /b "' + @BackupPathLog + N'"'; IF @Debug = 1 BEGIN IF @cmd IS NULL PRINT '@cmd is NULL for @BackupPathLog check'; PRINT @cmd; END; IF @Debug = 1 BEGIN SELECT * FROM @FileList; END; INSERT INTO @FileList (BackupFile) EXEC master.sys.xp_cmdshell @cmd; /*Sanity check folders*/ IF ( SELECT COUNT(*) FROM @FileList AS fl WHERE fl.BackupFile = 'The system cannot find the path specified.' OR fl.BackupFile = 'File Not Found' ) = 1 BEGIN RAISERROR('No rows were returned for that database\path', 0, 1) WITH NOWAIT; END; IF ( SELECT COUNT(*) FROM @FileList AS fl WHERE fl.BackupFile = 'Access is denied.' ) = 1 BEGIN RAISERROR('Access is denied to %s', 16, 1, @BackupPathLog) WITH NOWAIT; END; IF ( SELECT COUNT(*) FROM @FileList AS fl ) = 1 AND ( SELECT COUNT(*) FROM @FileList AS fl WHERE fl.BackupFile IS NULL ) = 1 BEGIN RAISERROR('That directory appears to be empty', 0, 1) WITH NOWAIT; RETURN; END /*End folder sanity check*/ IF (@OnlyLogsAfter IS NOT NULL) BEGIN RAISERROR('@OnlyLogsAfter is NOT NULL, deleting from @FileList', 0, 1) WITH NOWAIT; DELETE fl FROM @FileList AS fl WHERE BackupFile LIKE N'%.trn' AND BackupFile LIKE N'%' + @Database + N'%' AND REPLACE(LEFT(RIGHT(fl.BackupFile, 19), 15),'_','') < @OnlyLogsAfter END --check for log backups IF(@StopAt IS NULL AND @OnlyLogsAfter IS NULL) BEGIN DECLARE BackupFiles CURSOR FOR SELECT BackupFile FROM @FileList WHERE BackupFile LIKE N'%.trn' AND BackupFile LIKE N'%' + @Database + N'%' AND (@ContinueLogs = 1 OR (@ContinueLogs = 0 AND REPLACE(LEFT(RIGHT(BackupFile, 19), 15),'_','') >= @BackupDateTime)) ORDER BY BackupFile; OPEN BackupFiles; END; IF (@StopAt IS NULL AND @OnlyLogsAfter IS NOT NULL) BEGIN DECLARE BackupFiles CURSOR FOR SELECT BackupFile FROM @FileList WHERE BackupFile LIKE N'%.trn' AND BackupFile LIKE N'%' + @Database + N'%' AND (@ContinueLogs = 1 OR (@ContinueLogs = 0 AND REPLACE(LEFT(RIGHT(BackupFile, 19), 15),'_','') >= @OnlyLogsAfter)) ORDER BY BackupFile; OPEN BackupFiles; END IF (@StopAt IS NOT NULL AND @OnlyLogsAfter IS NULL) BEGIN DECLARE BackupFiles CURSOR FOR SELECT BackupFile FROM @FileList WHERE BackupFile LIKE N'%.trn' AND BackupFile LIKE N'%' + @Database + N'%' AND (@ContinueLogs = 1 OR (@ContinueLogs = 0 AND REPLACE(LEFT(RIGHT(BackupFile, 19), 15),'_','') >= @BackupDateTime) AND REPLACE(LEFT(RIGHT(BackupFile, 19), 15),'_','') <= @StopAt) ORDER BY BackupFile; OPEN BackupFiles; END; -- Loop through all the files for the database FETCH NEXT FROM BackupFiles INTO @BackupFile; WHILE @@FETCH_STATUS = 0 BEGIN IF @i = 1 BEGIN SET @sql = REPLACE(@HeadersSQL, N'{Path}', @BackupPathLog + @BackupFile); IF @Debug = 1 BEGIN IF @sql IS NULL PRINT '@sql is NULL for REPLACE: @HeadersSQL, @BackupPathLog, @BackupFile'; PRINT @sql; END; EXECUTE (@sql); SELECT @LogFirstLSN = CAST(FirstLSN AS NUMERIC(25, 0)), @LogLastLSN = CAST(LastLSN AS NUMERIC(25, 0)) FROM #Headers WHERE BackupType = 2; IF (@ContinueLogs = 0 AND @LogFirstLSN <= @FullLastLSN AND @FullLastLSN <= @LogLastLSN AND @RestoreDiff = 0) OR (@ContinueLogs = 1 AND @LogFirstLSN <= @DatabaseLastLSN AND @DatabaseLastLSN < @LogLastLSN AND @RestoreDiff = 0) SET @i = 2; IF (@ContinueLogs = 0 AND @LogFirstLSN <= @DiffLastLSN AND @DiffLastLSN <= @LogLastLSN AND @RestoreDiff = 1) OR (@ContinueLogs = 1 AND @LogFirstLSN <= @DatabaseLastLSN AND @DatabaseLastLSN < @LogLastLSN AND @RestoreDiff = 1) SET @i = 2; DELETE FROM #Headers WHERE BackupType = 2; END; IF @i = 1 BEGIN IF @Debug = 1 RAISERROR('No Log to Restore', 0, 1) WITH NOWAIT; END IF @i = 2 BEGIN RAISERROR('@i set to 2, restoring logs', 0, 1) WITH NOWAIT; SET @sql = N'RESTORE LOG ' + @RestoreDatabaseName + N' FROM DISK = ''' + @BackupPathLog + @BackupFile + N''' WITH NORECOVERY' + NCHAR(13); IF @Debug = 1 BEGIN IF @sql IS NULL PRINT '@sql is NULL for RESTORE LOG: @RestoreDatabaseName, @BackupPathLog, @BackupFile'; PRINT @sql; END; IF @Debug IN (0, 1) EXECUTE @sql = [dbo].[CommandExecute] @Command = @sql, @CommandType = 'RESTORE LOG', @Mode = 1, @DatabaseName = @Database, @LogToTable = 'Y', @Execute = 'Y'; END; FETCH NEXT FROM BackupFiles INTO @BackupFile; END; CLOSE BackupFiles; DEALLOCATE BackupFiles; IF @Debug = 1 BEGIN SELECT '#Headers' AS table_name, * FROM #Headers AS h END END -- put database in a useable state IF @RunRecovery = 1 BEGIN SET @sql = N'RESTORE DATABASE ' + @RestoreDatabaseName + N' WITH RECOVERY' + NCHAR(13); IF @Debug = 1 BEGIN IF @sql IS NULL PRINT '@sql is NULL for RESTORE DATABASE: @RestoreDatabaseName'; PRINT @sql; END; IF @Debug IN (0, 1) EXECUTE sp_executesql @sql; END; -- ensure simple recovery model IF @ForceSimpleRecovery = 1 BEGIN SET @sql = N'ALTER DATABASE ' + @RestoreDatabaseName + N' SET RECOVERY SIMPLE' + NCHAR(13); IF @Debug = 1 BEGIN IF @sql IS NULL PRINT '@sql is NULL for SET RECOVERY SIMPLE: @RestoreDatabaseName'; PRINT @sql; END; IF @Debug IN (0, 1) EXECUTE sp_executesql @sql; END; --Run checkdb against this database IF @RunCheckDB = 1 BEGIN SET @sql = N'EXECUTE [dbo].[DatabaseIntegrityCheck] @Databases = ' + @RestoreDatabaseName + N', @LogToTable = ''Y''' + NCHAR(13); IF @Debug = 1 BEGIN IF @sql IS NULL PRINT '@sql is NULL for Run Integrity Check: @RestoreDatabaseName'; PRINT @sql; END; IF @Debug IN (0, 1) EXECUTE sys.sp_executesql @sql; END; --If test restore then blow the database away (be careful) IF @TestRestore = 1 BEGIN SET @sql = N'DROP DATABASE ' + @RestoreDatabaseName + NCHAR(13); IF @Debug = 1 BEGIN IF @sql IS NULL PRINT '@sql is NULL for DROP DATABASE: @RestoreDatabaseName'; PRINT @sql; END; IF @Debug IN (0, 1) EXECUTE sp_executesql @sql; END; GO