web-dev-qa-db-ja.com

T-SQLのバックアップデバイスにある該当するすべてのファイルを復元する

SQL Server(この場合は2008 R2)を使用していて、データベースをバックアップデバイス(データベースごとに1つ)にバックアップしています。毎週日曜日にデバイスが新しいフルバックアップで上書きされ、毎晩差分バックアップが追加され、毎時間トランザクションログバックアップがバックアップデバイスに追加されます。

SSMSを使用してこれを新しいサーバーに復元する場合、最初に新しいデータベースを作成し、次にバックアップデバイスを追加して、そのバックアップデバイスから復元します。次に、該当するファイルを手動で選択する必要があります。

  • 完全バックアップ(常にリストの最初のバックアップ)
  • 最新の差分バックアップ
  • 最後の差分バックアップの後に行われたすべてのトランザクションログバックアップ。

enter image description here

これは、時折の復元では問題なく機能しますが、T-SQLでこれを自動化する必要があります。

どのファイルを選択するかについていくつかのロジックがありますが、バックアップデバイスのコンテンツにアクセスする方法はまだわかりません。

データベースとバックアップデバイスの名前を取得し、最新バージョンを復元するストアドプロシージャが必要です。誰かがそのようなことを知っていますか?

または私はここで何かを逃し、これのための簡単なコマンドがありますか?

4
Peter Hahndorf

自分でソリューションを作成してしまい、テストを開始したばかりですが、見た目はよさそうです。私が必要なのはバックアップデバイスファイルだけです。

EXEC [dbo].[dba_RestoreFromBackupDevice] @DBName = 'test', @File = 'C:\test.bak', @WhatIf = 1

データベースを作成し、バックアップデバイスのコンテンツを読み取り、どのファイルを復元するかを判断します。 @WhatIfを使用すると、次のようにSQLが出力されます。

USE master; 
CREATE DATABASE [lab];
RESTORE DATABASE [lab] FROM  DISK = N'C:\test.bak' WITH  FILE = 1,  NORECOVERY,  NOUNLOAD,  REPLACE,  STATS = 10; 
RESTORE DATABASE [lab] FROM  DISK = N'C:\test.bak' WITH  FILE = 49, NORECOVERY,  NOUNLOAD,  REPLACE,  STATS = 10;
RESTORE LOG [lab] FROM  DISK = N'C:\test.bak' WITH  FILE = 50, NORECOVERY,  NOUNLOAD,  REPLACE,  STATS = 10; 
RESTORE LOG [lab] FROM  DISK = N'C:\test.bak' WITH  FILE = 51, NORECOVERY,  NOUNLOAD,  REPLACE,  STATS = 10; 
RESTORE database [lab] WITH RECOVERY; 

@WhatIf = 0または欠落していると、すぐに復元プロセスが実行されます。

完全なストアドプロシージャは次のとおりです。

/* ========================================================
Author:        Peter Hahndorf
Creation date: 25-Sep-2012

Restores a database from a backup device with multiple files.
Uses the first file, the lastest differential files and
all further transactional log files.
Creates the database if not already there.
Use @WhatIf = 1 to just print the SQL statement without
executing it.
========================================================= */
ALTER PROCEDURE [dbo].[dba_RestoreFromBackupDevice]
  @DBName varchar(100),
  @File varchar(250),
  @WhatIf bit = 0
AS
BEGIN

-- We need a table to store the backup devices file data
-- This table is for SQL Server 2008R2 earlier versions
-- have less fields, later may have more.
DECLARE @Headers TABLE (
BackupName nvarchar(128) ,
BackupDescription nvarchar(255),
BackupType smallint,
ExpirationDate datetime,
Compressed tinyint,
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),
DifferentialBackupLsn 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 Int, 
BeginsLogChain bit, 
HAsIncompleteMetaData bit, 
IsForceOFfline bit, 
IsCopyOnly bit, 
FirstRecoveryForkID uniqueidentifier,
ForkPointLSN numeric(25,0),
RecoveryModel nvarchar(60),
DifferentialBaseLSN numeric(25,0),
DifferentialBAseGUID uniqueidentifier,
BackupTypeDescription nvarchar(60),
BackupSetGUID uniqueidentifier,
CompressedBackupSize bigint
);

-- this gets the content of the backup device and puts it in
-- a table so we can work with it.
INSERT @Headers EXEC ('RESTORE HEADERONLY FROM DISK=''' + @File + '''')

/*
 The device may have one of the following combinations of files:
 Full only
 Full and at least one log
 Full, at least one diff and some further logs
 Full, at least one diff and no further logs
*/

-- We store all our commands in a string and then either print or
-- execute it at the end

DECLARE @SQL varchar(max)

SET @SQL = 'USE master; '

IF NOT EXISTS (SELECT name FROM sys.databases WHERE name = @DBName )
BEGIN
    SET @SQL = @SQL + 'CREATE DATABASE [' + @DBName + ']; '
END

-- Always restore the first file, it is the only full backup in the device
SET @SQL = @SQL + 'RESTORE DATABASE [' + @DBName + '] FROM  DISK = N''' + @File + ''' WITH  FILE = 1,  NORECOVERY,  NOUNLOAD,  REPLACE,  STATS = 10; '

-- Find the last differential backup, the highest position of any type 5 files
-- If there is none, we assign a one, which makes it the same as the full one
DECLARE @DiffFileNo INT
SELECT @DiffFileNo = (SELECT ISNULL(MAX(Position),1) FROM @Headers WHERE BackupType = 5)

-- If there is one, restore it
IF @DiffFileNo > 1
BEGIN       
    SET @SQL = @SQL + 'RESTORE DATABASE [' + @DBName + '] FROM  DISK = N''' + @File + ''' WITH  FILE = ' + CAST(@DiffFileNo AS VARCHAR) + ', NORECOVERY,  NOUNLOAD,  REPLACE,  STATS = 10;'
END

-- now we also need all log files that are newer/greater than the last differential
DECLARE @fileno INT
DECLARE restore_cursor CURSOR FOR 
SELECT Position FROM @Headers WHERE Position > @DiffFileNo ORDER BY Position

OPEN restore_cursor
FETCH NEXT FROM restore_cursor INTO @fileno
WHILE @@FETCH_STATUS = 0
BEGIN

SET @SQL = @SQL +  'RESTORE LOG [' + @DBName + '] FROM  DISK = N''' + @File + ''' WITH  FILE = ' + CAST(@fileno AS VARCHAR) + ', NORECOVERY,  NOUNLOAD,  REPLACE,  STATS = 10; '

FETCH NEXT FROM restore_cursor INTO @fileno
END
CLOSE restore_cursor
DEALLOCATE restore_cursor

-- finally we have to finish the restore
SET @SQL = @SQL + 'RESTORE database [' + @DBName + '] WITH RECOVERY; '

IF @WhatIf = 1
BEGIN
    PRINT @SQL
END 
ELSE
BEGIN
    EXEC(@SQL)
END

END
1
Peter Hahndorf

この手順はあなたが望むことをするはずです:


create procedure dbo.dbrestore
@dbname nvarchar(50)
as
declare @fileno integer
declare @dumpdevice nvarchar(50)
declare @mediasetid integer
declare @position integer

select top(1) @dumpdevice=a.logical_device_name, @mediasetid=b.media_set_id, @position=b.position from backupmediafamily a inner join backupset b on a.media_set_id=b.media_set_id
where b.database_name='backuptest' and b.type='D' order by a.media_set_id desc, b. position desc

declare restore_cursor cursor for
select position from msdb.dbo.backupset where database_name=@dbname and type='D' and media_set_id=@mediasetid and position=@position
union
select  max(position) as position from msdb.dbo.backupset where database_name=@dbname and type='I' and media_set_id=@mediasetid and position>@position
union
select position from msdb.dbo.backupset where database_name=@dbname and type='L' and media_set_id=@mediasetid and position>@position
and last_lsn>(select max(last_lsn)from msdb.dbo.backupset where database_name=@dbname and type='I' and media_set_id=@mediasetid and position>@position)
order by position asc;

open restore_cursor
fetch next from restore_cursor into @fileno
while @@FETCH_STATUS = 0
begin
--comment the print statement and uncomment the exec statement to run
--exec('restore database ['+@dbname+'] from '+@dumpdevice+' with norecovery, replace, FILE='+@fileno+';')
  print ('restore database ['+@dbname+'] from '+@dumpdevice+' with norecovery, replace, FILE='+convert(varchar(4),@fileno)+';')
fetch next from restore_cursor into @fileno
end
close restore_cursor
deallocate restore_cursor
--exec('restore database ['+@dbname+'] with recovery;')
go

実行は行く:

指定されたデータベース名に基づいて、ダンプデバイスの名前を取得します。私はそれらがすべて同じデバイス上にあると想定しています。先頭は、初期DBに「with format」を使用している場合、データベースごとに複数のメディアセットを持つことを回避し、デバイスで複数のフルバックアップの可能性に対処することです。

次に、以下で構成されるユニオンクエリを作成します。

デバイス内の最後の完全データベースバックアップ(タイプ= D)

最後の完全データベースバックアップの後に行われた最後の差分バックアップ(type = Iであり、LSN(ログシーケンス番号)が最大)。

上記の差分の後に取られたトランザクションログ(type = L)。

この場合はFILE値にマップする位置値のみを返します(http://msdn.Microsoft.com/en-us/library/ms186299.aspx)

結果をループして個別に復元し、最後に復元して最終的な復元を行います。

それを次のように呼びます:


exec dbrestore 'db2restore'

私はテストデータベースで試してみましたが、うまく機能しているように見え、SSMSが復元したい同じ操作で同じように見えるファイルと比較しています。

現状のままでは、おそらくタイプミスがあり、おそらく境界条件を見逃しており、エラーチェックもありません。

3
Stuart Moore