Skip to content

Commit eccab41

Browse files
authored
Merge pull request #840 from digitalohm/sp_DatabaseRestore_814
Issue 814 - Added Differential Restore Feature to sp_DatabaseRestore
2 parents fadebfd + 8210ca3 commit eccab41

File tree

1 file changed

+102
-43
lines changed

1 file changed

+102
-43
lines changed

sp_DatabaseRestore.sql

Lines changed: 102 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -1,45 +1,67 @@
11
/*
2-
EXEC dbo.DatabaseRestore
2+
EXEC dbo.sp_DatabaseRestore
33
@Database = 'LogShipMe',
44
@BackupPathFull = 'D:\Backup\SQL2016PROD1A\LogShipMe\FULL\',
55
@BackupPathLog = 'D:\Backup\SQL2016PROD1A\LogShipMe\LOG\',
66
@ContinueLogs = 0,
77
@RunRecovery = 0;
88
9-
EXEC dbo.DatabaseRestore
9+
EXEC dbo.sp_DatabaseRestore
1010
@Database = 'LogShipMe',
1111
@BackupPathFull = 'D:\Backup\SQL2016PROD1A\LogShipMe\FULL\',
1212
@BackupPathLog = 'D:\Backup\SQL2016PROD1A\LogShipMe\LOG\',
1313
@ContinueLogs = 1,
1414
@RunRecovery = 0;
1515
16-
EXEC dbo.DatabaseRestore
16+
EXEC dbo.sp_DatabaseRestore
1717
@Database = 'LogShipMe',
1818
@BackupPathFull = 'D:\Backup\SQL2016PROD1A\LogShipMe\FULL\',
1919
@BackupPathLog = 'D:\Backup\SQL2016PROD1A\LogShipMe\LOG\',
2020
@ContinueLogs = 1,
2121
@RunRecovery = 1;
2222
23-
EXEC dbo.DatabaseRestore
23+
EXEC dbo.sp_DatabaseRestore
2424
@Database = 'LogShipMe',
2525
@BackupPathFull = 'D:\Backup\SQL2016PROD1A\LogShipMe\FULL\',
2626
@BackupPathLog = 'D:\Backup\SQL2016PROD1A\LogShipMe\LOG\',
2727
@ContinueLogs = 0,
2828
@RunRecovery = 1;
29+
30+
EXEC dbo.sp_DatabaseRestore
31+
@Database = 'LogShipMe',
32+
@BackupPathFull = 'D:\Backup\SQL2016PROD1A\LogShipMe\FULL\',
33+
@BackupPathDiff = 'D:\Backup\SQL2016PROD1A\LogShipMe\DIFF\',
34+
@BackupPathLog = 'D:\Backup\SQL2016PROD1A\LogShipMe\LOG\',
35+
@RestoreDiff = 1,
36+
@ContinueLogs = 0,
37+
@RunRecovery = 1;
38+
39+
--
40+
EXEC dbo.sp_DatabaseRestore
41+
@Database = 'LogShipMe',
42+
@BackupPathFull = '\\StorageServer\LogShipMe\FULL\',
43+
@BackupPathDiff = '\\StorageServer\LogShipMe\DIFF\',
44+
@BackupPathLog = '\\StorageServer\LogShipMe\LOG\',
45+
@RestoreDiff = 1,
46+
@ContinueLogs = 0,
47+
@RunRecovery = 1,
48+
@TestRestore = 1,
49+
@RunCheckDB = 1,
50+
@Debug = 0;
2951
*/
3052

3153
IF OBJECT_ID('dbo.sp_DatabaseRestore') IS NULL
3254
EXEC ('CREATE PROCEDURE dbo.sp_DatabaseRestore AS RETURN 0;')
3355
GO
3456

3557
ALTER PROCEDURE [dbo].[sp_DatabaseRestore]
36-
@Database NVARCHAR(128), @RestoreDatabaseName NVARCHAR(128) = NULL, @BackupPathFull NVARCHAR(MAX), @BackupPathLog NVARCHAR(MAX),
37-
@MoveFiles bit = 0, @MoveDataDrive NVARCHAR(260) = NULL, @MoveLogDrive NVARCHAR(260) = NULL, @TestRestore bit = 0, @RunCheckDB bit = 0,
38-
@ContinueLogs bit = 0, @RunRecovery bit = 0
58+
@Database NVARCHAR(128), @RestoreDatabaseName NVARCHAR(128) = NULL, @BackupPathFull NVARCHAR(MAX), @BackupPathDiff NVARCHAR(MAX), @BackupPathLog NVARCHAR(MAX),
59+
@MoveFiles bit = 0, @MoveDataDrive NVARCHAR(260) = NULL, @MoveLogDrive NVARCHAR(260) = NULL, @TestRestore bit = 0, @RunCheckDB bit = 0, @RestoreDiff bit = 0,
60+
@ContinueLogs bit = 0, @RunRecovery bit = 0, @Debug INT = 0
3961
AS
4062
SET NOCOUNT ON;
4163

42-
DECLARE @cmd NVARCHAR(4000), @sql NVARCHAR(MAX), @LastFullBackup NVARCHAR(500), @BackupFile NVARCHAR(500);
64+
DECLARE @cmd NVARCHAR(4000), @sql NVARCHAR(MAX), @LastFullBackup NVARCHAR(500), @LastDiffBackup NVARCHAR(500), @BackupFile NVARCHAR(500), @BackupDateTime AS CHAR(15), @FullLastLSN NUMERIC(25, 0), @DiffLastLSN NUMERIC(25, 0);
4365
DECLARE @FileList TABLE (BackupFile NVARCHAR(255));
4466

4567
IF @RestoreDatabaseName IS NULL
@@ -113,7 +135,9 @@ SET @FileListParamSQL += ')' + CHAR(13) + CHAR(10);
113135
SET @FileListParamSQL += 'EXEC (''RESTORE FILELISTONLY FROM DISK=''''{Path}'''''')';
114136

115137
SET @sql = REPLACE(@FileListParamSQL, '{Path}', @BackupPathFull + @LastFullBackup);
116-
PRINT @sql;
138+
IF @Debug = 2
139+
PRINT @sql;
140+
117141
EXEC (@sql);
118142

119143
-- Build SQL for RESTORE HEADERONLY - this will be used a bit further below
@@ -174,101 +198,136 @@ BEGIN
174198
SELECT @MoveOption = @MoveOption + logicalcmds
175199
FROM Files;
176200
END;
177-
178201
IF @ContinueLogs = 0
179202
BEGIN
180203
SET @sql = 'RESTORE DATABASE '+@RestoreDatabaseName+' FROM DISK = '''+@BackupPathFull + @LastFullBackup+ ''' WITH NORECOVERY, REPLACE' + @MoveOption+CHAR(13);
181204
PRINT @sql;
182-
EXECUTE @sql = [dbo].[CommandExecute] @Command = @sql, @CommandType = 'RESTORE DATABASE', @Mode = 1, @DatabaseName = @Database, @LogToTable = 'Y', @Execute = 'Y';
205+
IF @Debug = 0
206+
EXECUTE @sql = [dbo].[CommandExecute] @Command = @sql, @CommandType = 'RESTORE DATABASE', @Mode = 1, @DatabaseName = @Database, @LogToTable = 'Y', @Execute = 'Y';
183207

184-
--get the backup completed data so we can apply tlogs from that point forwards
185-
208+
--get the backup completed data so we can apply tlogs from that point forwards
186209
SET @sql = REPLACE(@HeadersSQL, '{Path}', @BackupPathFull + @LastFullBackup);
187-
PRINT @sql;
210+
IF @Debug = 2
211+
PRINT @sql;
212+
188213
EXECUTE (@sql);
189-
190-
DECLARE @BackupDateTime AS CHAR(15), @FullLastLSN NUMERIC(25, 0);
191-
214+
--DECLARE @BackupDateTime AS CHAR(15), @FullLastLSN NUMERIC(25, 0); Commented out for testing
192215
SELECT @BackupDateTime = RIGHT(@LastFullBackup, 19)
193-
194-
SELECT @FullLastLSN = CAST(LastLSN AS NUMERIC(25, 0)) FROM #Headers WHERE BackupType = 1;
195-
216+
SELECT @FullLastLSN = CAST(LastLSN AS NUMERIC(25, 0)) FROM #Headers WHERE BackupType = 1;
217+
IF @Debug = 2
218+
PRINT @BackupDateTime
196219
END;
197220
ELSE
198221
BEGIN
199222
DECLARE @DatabaseLastLSN NUMERIC(25, 0);
200-
201223
SELECT @DatabaseLastLSN = CAST(f.redo_start_lsn AS NUMERIC(25, 0))
202224
FROM master.sys.databases d
203225
JOIN master.sys.master_files f ON d.database_id = f.database_id
204226
WHERE d.name = @RestoreDatabaseName AND f.file_id = 1
205227
END;
206228

229+
--Clear out table variables for differential
230+
DELETE FROM @FileList;
231+
232+
-- get list of files
233+
SET @cmd = 'DIR /b "'+ @BackupPathDiff + '"';
234+
INSERT INTO @FileList (BackupFile)
235+
EXEC master.sys.xp_cmdshell @cmd;
236+
237+
-- select * from @fileList
238+
-- Find latest diff backup
239+
SELECT @LastDiffBackup = MAX(BackupFile)
240+
FROM @FileList
241+
WHERE BackupFile LIKE '%.bak'
242+
AND
243+
BackupFile LIKE '%'+@Database+'%';
244+
245+
IF @RestoreDiff = 1 AND @BackupDateTime < RIGHT(@LastDiffBackup, 19)
246+
BEGIN
247+
SET @sql = 'RESTORE DATABASE '+@RestoreDatabaseName+' FROM DISK = '''+@BackupPathDiff + @LastDiffBackup+ ''' WITH NORECOVERY'+CHAR(13);
248+
PRINT @sql;
249+
IF @Debug = 0
250+
EXECUTE @sql = [dbo].[CommandExecute] @Command = @sql, @CommandType = 'RESTORE DATABASE', @Mode = 1, @DatabaseName = @Database, @LogToTable = 'Y', @Execute = 'Y';
251+
252+
--get the backup completed data so we can apply tlogs from that point forwards
253+
SET @sql = REPLACE(@HeadersSQL, '{Path}', @BackupPathDiff + @LastDiffBackup);
254+
IF @Debug = 2
255+
PRINT @sql;
256+
257+
EXECUTE (@sql);
258+
--DECLARE @BackupDateTime AS CHAR(15), @FullLastLSN NUMERIC(25, 0);
259+
SELECT @BackupDateTime = RIGHT(@LastDiffBackup, 19)
260+
SELECT @DiffLastLSN = CAST(LastLSN AS NUMERIC(25, 0)) FROM #Headers WHERE BackupType = 5;
261+
END;
262+
207263
--Clear out table variables for translogs
208264
DELETE FROM @FileList;
209265

210266
SET @cmd = 'DIR /b "'+ @BackupPathLog + '"';
211267
INSERT INTO @FileList (BackupFile)
212268
EXEC master.sys.xp_cmdshell @cmd;
213-
214269
--check for log backups
215270
DECLARE BackupFiles CURSOR FOR
216271
SELECT BackupFile
217272
FROM @FileList
218273
WHERE BackupFile LIKE '%.trn'
219274
AND BackupFile LIKE '%'+@Database+'%'
220-
AND (@ContinueLogs = 1 OR (@ContinueLogs = 0 AND LEFT(RIGHT(BackupFile, 19), 15) >= @BackupDateTime));
221-
275+
AND (@ContinueLogs = 1 OR (@ContinueLogs = 0 AND LEFT(RIGHT(BackupFile, 19), 15) >= @BackupDateTime))
276+
ORDER BY BackupFile;
222277
OPEN BackupFiles;
223-
224278
DECLARE @i tinyint = 1, @LogFirstLSN NUMERIC(25, 0), @LogLastLSN NUMERIC(25, 0);
225-
226279
-- Loop through all the files for the database
227280
FETCH NEXT FROM BackupFiles INTO @BackupFile;
228281
WHILE @@FETCH_STATUS = 0
229282
BEGIN
230283
IF @i = 1
231284
BEGIN
232285
SET @sql = REPLACE(@HeadersSQL, '{Path}', @BackupPathLog + @BackupFile);
233-
PRINT @sql;
234-
EXECUTE (@sql);
286+
IF @Debug = 2
287+
PRINT @sql;
288+
EXECUTE (@sql);
235289

236290
SELECT @LogFirstLSN = CAST(FirstLSN AS NUMERIC(25, 0)), @LogLastLSN = CAST(LastLSN AS NUMERIC(25, 0)) FROM #Headers WHERE BackupType = 2;
237291

238-
IF (@ContinueLogs = 0 AND @LogFirstLSN <= @FullLastLSN AND @FullLastLSN <= @LogLastLSN) OR (@ContinueLogs = 1 AND @LogFirstLSN <= @DatabaseLastLSN AND @DatabaseLastLSN < @LogLastLSN)
292+
IF (@ContinueLogs = 0 AND @LogFirstLSN <= @FullLastLSN AND @FullLastLSN <= @LogLastLSN AND @RestoreDiff = 0) OR (@ContinueLogs = 1 AND @LogFirstLSN <= @DatabaseLastLSN AND @DatabaseLastLSN < @LogLastLSN AND @RestoreDiff = 0)
293+
SET @i = 2;
294+
IF (@ContinueLogs = 0 AND @LogFirstLSN <= @DiffLastLSN AND @DiffLastLSN <= @LogLastLSN AND @RestoreDiff = 1) OR (@ContinueLogs = 1 AND @LogFirstLSN <= @DatabaseLastLSN AND @DatabaseLastLSN < @LogLastLSN AND @RestoreDiff = 1)
239295
SET @i = 2;
240-
241296
DELETE FROM #Headers WHERE BackupType = 2;
242297
END;
243-
244298
IF @i = 2
245299
BEGIN
246300
SET @sql = 'RESTORE LOG '+@RestoreDatabaseName+' FROM DISK = '''+@BackupPathLog + @BackupFile+''' WITH NORECOVERY'+CHAR(13);
247301
PRINT @sql
248-
EXECUTE @sql = [dbo].[CommandExecute] @Command = @sql, @CommandType = 'RESTORE LOG', @Mode = 1, @DatabaseName = @Database, @LogToTable = 'Y', @Execute = 'Y';
302+
IF @Debug = 0
303+
EXECUTE @sql = [dbo].[CommandExecute] @Command = @sql, @CommandType = 'RESTORE LOG', @Mode = 1, @DatabaseName = @Database, @LogToTable = 'Y', @Execute = 'Y';
249304
END;
250-
251305
FETCH NEXT FROM BackupFiles INTO @BackupFile;
252306
END;
253-
254307
CLOSE BackupFiles;
255308
DEALLOCATE BackupFiles;
256-
257309
-- put database in a useable state
258310
IF @RunRecovery = 1
259311
BEGIN
260-
SET @sql = 'RESTORE DATABASE '+@RestoreDatabaseName+' WITH RECOVERY';
261-
EXECUTE sp_executesql @sql;
312+
SET @sql = 'RESTORE DATABASE '+@RestoreDatabaseName+' WITH RECOVERY'+CHAR(13);
313+
PRINT @sql
314+
IF @Debug = 0
315+
EXECUTE sp_executesql @sql;
262316
END;
263317

264318
--Run checkdb against this database
265319
IF @RunCheckDB = 1
266-
EXECUTE [dbo].[DatabaseIntegrityCheck] @Databases = @RestoreDatabaseName, @LogToTable = 'Y';
267-
320+
BEGIN
321+
SET @sql = 'EXECUTE [dbo].[DatabaseIntegrityCheck] @Databases = ' + @RestoreDatabaseName + ', @LogToTable = ''Y'''+CHAR(13);
322+
PRINT @sql
323+
IF @Debug = 0
324+
EXECUTE sys.sp_executesql @sql
325+
END;
268326
--If test restore then blow the database away (be careful)
269327
IF @TestRestore = 1
270328
BEGIN
271-
SET @sql = 'DROP DATABASE '+@RestoreDatabaseName;
272-
EXECUTE sp_executesql @sql;
273-
END;
274-
329+
SET @sql = 'DROP DATABASE '+@RestoreDatabaseName+CHAR(13);
330+
PRINT @sql
331+
IF @Debug = 0
332+
EXECUTE sp_executesql @sql;
333+
END;

0 commit comments

Comments
 (0)