Database
 sql >> Database >  >> RDS >> Database

Implementazione di backup e ripristino automatizzati del database con mezzi predefiniti

Introduzione

Puoi trovare molte guide su come eseguire il backup e il ripristino dei database. In questo, mostreremo come questo può essere fatto usando il mezzo predefinito di MS SQL Server.

Questo esempio tratterà una serie di approcci, dal controllo dell'integrità del database prima di eseguirne il backup al ripristino del database da una copia di backup creata in precedenza.

La soluzione

In primo luogo, diamo un'occhiata all'algoritmo generale che utilizzeremo per il backup di un database:

1) Definire quali database devono essere sottoposti a backup
2) Verifica dell'integrità dei database scelti
3) Creazione di un backup (completo, differenziale o copia del log delle transazioni) per ciascuno dei database scelti
4) Verifica delle copie di backup create
5) Compressione dei log delle transazioni (se necessario)

Di seguito, puoi trovare un esempio di implementazione di questo algoritmo.

Per definire di quali database è necessario eseguire il backup, creeremo la seguente tabella:

USE [DB_NAME]
GO

SET ANSI_NULLS ON
GO

SET QUOTED_IDENTIFIER ON
GO

CREATE TABLE [srv].[BackupSettings](
	[DBID] [int] NOT NULL,
	[FullPathBackup] [nvarchar](255) NOT NULL,
	[DiffPathBackup] [nvarchar](255) NULL,
	[LogPathBackup] [nvarchar](255) NULL,
	[InsertUTCDate] [datetime] NOT NULL,
 CONSTRAINT [PK_BackupSettings] PRIMARY KEY CLUSTERED 
(
	[DBID] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY];
GO

ALTER TABLE [srv].[BackupSettings] ADD  CONSTRAINT [DF_BackupSettings_InsertUTCDate]  DEFAULT (getutcdate()) FOR [InsertUTCDate];
GO

L'identificatore del database si trova nella prima colonna, "FullPathBackup" contiene il percorso per la creazione di una copia di backup completa (ad esempio, "disco:\…\") e DiffPathBackup e LogPathBackup contengono percorsi completi per la creazione di copie differenziali e del registro delle transazioni rispettivamente. Se le colonne DiffPathBackup o LogPathBackup sono vuote, la copia differenziale e/o del registro delle transazioni per questo database non verrà creata.

Possiamo anche creare una rappresentazione basata su questa tabella:

USE [DB_NAME]
GO

SET ANSI_NULLS ON
GO

SET QUOTED_IDENTIFIER ON
GO

CREATE view [srv].[vBackupSettings]
as
SELECT [DBID]
      ,DB_Name([DBID]) as [DBName]
	  ,[FullPathBackup]
      ,[DiffPathBackup]
      ,[LogPathBackup]
      ,[InsertUTCDate]
  FROM [srv].[BackupSettings];
GO

Questa rappresentazione consente di verificare in modo efficace quali database partecipano al processo di backup.

Ora creiamo una rappresentazione che mostri le informazioni sul file di database dalla rappresentazione di sistema sys.master_files:

USE [DB_NAME]
GO

SET ANSI_NULLS ON
GO

SET QUOTED_IDENTIFIER ON
GO

CREATE view [inf].[ServerDBFileInfo] as
SELECT  @@Servername AS Server ,
        File_id ,--DB file identifier. The base value for file_id is 1
        Type_desc ,--Type file description
        Name as [FileName] ,--DB logical file name 
        LEFT(Physical_Name, 1) AS Drive ,--Drive flag of the DB file location
        Physical_Name ,--Full file name in the OS
        RIGHT(physical_name, 3) AS Ext ,--File extension
        Size as CountPage, --Current file size in 8Kb pages
		round((cast(Size*8 as float))/1024,3) as SizeMb, --File size in Mb
		round((cast(Size*8 as float))/1024/1024,3) as SizeGb, --File size in Gb
        case when is_percent_growth=0 then Growth*8 else 0 end as Growth, --File growth in 8Kb pages
		case when is_percent_growth=0 then round((cast(Growth*8 as float))/1024,3) end as GrowthMb, --File growth in Mb
		case when is_percent_growth=0 then round((cast(Growth*8 as float))/1024/1024,3) end as GrowthGb, --File growth in Gb
		case when is_percent_growth=1 then Growth else 0 end as GrowthPercent, --File growth in percent
		is_percent_growth, --Percent growth attribute
		database_id,
		DB_Name(database_id) as [DB_Name],
		State,--File state
		state_desc as StateDesc,--File state description
		is_media_read_only as IsMediaReadOnly,--File is located on the drive as read-only (0 - and for writing)
		is_read_only as IsReadOnly,--file is flagged as read-only (0 - and for writing)
		is_sparse as IsSpace,--Sparse file
		is_name_reserved as IsNameReserved,--1 - Remote file name, accessible for use.
		--It is necessary to get a log backup before using the same name (name or physical_name arguments) again for a new file
		--0 - Filename, inaccessible for use
		create_lsn as CreateLsn,--Transaction registration number in the log (LSN) which was used to create the file
		drop_lsn as DropLsn,--LSN which was used to delete the file
		read_only_lsn as ReadOnlyLsn,--LSN which was used by the file group containing the file to change the "read and write" type to "read-only" (the latest change)
		read_write_lsn as ReadWriteLsn,--LSN which was used by the file group containing the file to change the "read-only" type to "read and write" (the latest change)
		differential_base_lsn as DifferentialBaseLsn,--A base for differential backup copies. Data extents which were changed after the LSN is included into the differential backup.
		differential_base_guid as DifferentialBaseGuid,--Unique identifier of the base backup copy which will be used to create a differential copy.
		differential_base_time as DifferentialBaseTime,--The time corresponding to differential_base_lsn
		redo_start_lsn as RedoStartLsn,--LSN used to determine the start of the next redo
		--Is NULL, except for the cases in which state = RESTORING or state = RECOVERY_PENDING
		redo_start_fork_guid as RedoStartForkGuid,--Unique identifier for the restoration fork point
		--first_fork_guid argument value of the next restored backup copy should be equal to this value
		redo_target_lsn as RedoTargetLsn,--LSN which serves as a stop point for an "online" mode redo in this file
		--Is NULL, except for the cases in which state = RESTORING or state = RECOVERY_PENDING
		redo_target_fork_guid as RedoTargetForkGuid,--Restoration fork on which the container can be restored. Used along with redo_target_lsn
		backup_lsn as BackupLsn--LSN of the most recent data or the file's differential backup copy
FROM    sys.master_files--database_files;
GO

Per creare copie di backup complete, implementiamo la seguente procedura memorizzata:

[expand title =”Codice “]

USE [DB_NAME]
GO

SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO

ALTER PROCEDURE [srv].[RunFullBackupDB]
	@ClearLog bit=1 --specifies whether the transaction log size should be reduced
AS
BEGIN
	/*
		Creating a full DB backup copy and checking the DB for integrity beforehand
	*/
	SET NOCOUNT ON;

    declare @dt datetime=getdate();
	declare @year int=YEAR(@dt);
	declare @month int=MONTH(@dt);
	declare @day int=DAY(@dt);
	declare @hour int=DatePart(hour, @dt);
	declare @minute int=DatePart(minute, @dt);
	declare @second int=DatePart(second, @dt);
	declare @pathBackup nvarchar(255);
	declare @pathstr nvarchar(255);
	declare @DBName nvarchar(255);
	declare @backupName nvarchar(255);
	declare @sql nvarchar(max);
	declare @backupSetId as int;
	declare @FileNameLog nvarchar(255);

	declare @tbllog table(
		[DBName] [nvarchar](255) NOT NULL,
		[FileNameLog] [nvarchar](255) NOT NULL
	);
	
	declare @tbl table (
		[DBName] [nvarchar](255) NOT NULL,
		[FullPathBackup] [nvarchar](255) NOT NULL
	);
	
	--Retrieving DB name and full paths for full backup copy creation
	insert into @tbl (
	           [DBName]
	           ,[FullPathBackup]
	)
	select		DB_NAME([DBID])
	           ,[FullPathBackup]
	from [srv].[BackupSettings];

	--Retrieving the DB name and names of the according transaction logs (as one DB can have multiple logs)
	insert into @tbllog([DBName], [FileNameLog])
	select t.[DBName], tt.[FileName] as [FileNameLog]
	from @tbl as t
	inner join [inf].[ServerDBFileInfo] as tt on t.[DBName]=DB_NAME(tt.[database_id])
	where tt.[Type_desc]='LOG';
	
	--sequentially processing each of the DBs we got earlier
	while(exists(select top(1) 1 from @tbl))
	begin
		set @backupSetId=NULL;

		select top(1)
		@DBName=[DBName],
		@pathBackup=[FullPathBackup]
		from @tbl;
	
		set @[email protected]+N'_Full_backup_'+cast(@year as nvarchar(255))+N'_'+cast(@month as nvarchar(255))+N'_'+cast(@day as nvarchar(255))--+N'_'
						--+cast(@hour as nvarchar(255))+N'_'+cast(@minute as nvarchar(255))+N'_'+cast(@second as nvarchar(255));
		set @[email protected]@sqldat.com+N'.bak';

		--checking the DB for integrity
		set @sql=N'DBCC CHECKDB(N'+N''''[email protected]+N''''+N')  WITH NO_INFOMSGS';

		exec(@sql);
		
		--executing the backup copy creation procedure
		set @sql=N'BACKUP DATABASE ['[email protected]+N'] TO DISK = N'+N''''[email protected]+N''''+
				 N' WITH NOFORMAT, NOINIT, NAME = N'+N''''[email protected]+N''''+
				 N', CHECKSUM, STOP_ON_ERROR, SKIP, REWIND, COMPRESSION, STATS = 10;';
	
		exec(@sql);

		--checking the backup copy we created
		select @backupSetId = position
		from msdb..backupset where [email protected]
		and backup_set_id=(select max(backup_set_id) from msdb..backupset where [email protected]);

		set @sql=N'Verification error. Backup copy information for "'[email protected]+'" database not found.';

		if @backupSetId is null begin raiserror(@sql, 16, 1) end
		else
		begin
			set @sql=N'RESTORE VERIFYONLY FROM DISK = N'+''''[email protected]+N''''+N' WITH FILE = '+cast(@backupSetId as nvarchar(255));

			exec(@sql);
		end

		--compressing the DB transaction logs
		if(@ClearLog=1)
		begin
			while(exists(select top(1) 1 from @tbllog where [DBName][email protected]))
			begin
				select top(1)
				@FileNameLog=FileNameLog
				from @tbllog
				where [email protected];
			
				set @sql=N'USE ['[email protected]+N'];'+N' DBCC SHRINKFILE (N'+N''''[email protected]+N''''+N' , 0, TRUNCATEONLY)';

				exec(@sql);

				delete from @tbllog
				where [email protected]
				and [email protected];
			end
		end
		
		delete from @tbl
		where [DBName][email protected];
	end
END
GO

[/espandi]

Secondo il codice, possiamo vedere che questa procedura fornisce una soluzione per i restanti passaggi dell'algoritmo di creazione della copia di backup.

Le procedure che creano copie differenziali e del registro delle transazioni sono implementate in modo simile:

[expand title =”Codice “]

USE [DB_NAME]
GO

SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO

ALTER PROCEDURE [srv].[RunDiffBackupDB]
	@ClearLog bit=1 --specifies if the transaction log size should be reduced
AS
BEGIN
	/*
		Creating a differential DB backup copy
	*/
	SET NOCOUNT ON;

    declare @dt datetime=getdate();
	declare @year int=YEAR(@dt);
	declare @month int=MONTH(@dt);
	declare @day int=DAY(@dt);
	declare @hour int=DatePart(hour, @dt);
	declare @minute int=DatePart(minute, @dt);
	declare @second int=DatePart(second, @dt);
	declare @pathBackup nvarchar(255);
	declare @pathstr nvarchar(255);
	declare @DBName nvarchar(255);
	declare @backupName nvarchar(255);
	declare @sql nvarchar(max);
	declare @backupSetId as int;
	declare @FileNameLog nvarchar(255);
	
	declare @tbl table (
		[DBName] [nvarchar](255) NOT NULL,
		[DiffPathBackup] [nvarchar](255) NOT NULL
	);

	declare @tbllog table(
		[DBName] [nvarchar](255) NOT NULL,
		[FileNameLog] [nvarchar](255) NOT NULL
	);
	
	--Retrieving the DB name and full paths for creating differential backup copies
	insert into @tbl (
	           [DBName]
	           ,[DiffPathBackup]
	)
	select		DB_NAME([DBID])
	           ,[DiffPathBackup]
	from [srv].[BackupSettings]
	where [DiffPathBackup] is not null;

	--Retrieving DB name and the full names of the according transaction log files (as one DB can have multiple logs)
	insert into @tbllog([DBName], [FileNameLog])
	select t.[DBName], tt.[FileName] as [FileNameLog]
	from @tbl as t
	inner join [inf].[ServerDBFileInfo] as tt on t.[DBName]=DB_NAME(tt.[database_id])
	where tt.[Type_desc]='LOG';
	
	--sequentially processing each of the DBs we got earlier
	while(exists(select top(1) 1 from @tbl))
	begin
		set @backupSetId=NULL;

		select top(1)
		@DBName=[DBName],
		@pathBackup=[DiffPathBackup]
		from @tbl;
	
		set @[email protected]+N'_Diff_backup_'+cast(@year as nvarchar(255))+N'_'+cast(@month as nvarchar(255))+N'_'+cast(@day as nvarchar(255))+N'_'
						+cast(@hour as nvarchar(255))+N'_'+cast(@minute as nvarchar(255))+N'_'+cast(@second as nvarchar(255));
		set @[email protected]@sqldat.com+N'.bak';
		
		--checking the DB for integrity
		set @sql=N'DBCC CHECKDB(N'+N''''[email protected]+N''''+N')  WITH NO_INFOMSGS';

		exec(@sql);
		
		--executing the backup procedure
		set @sql=N'BACKUP DATABASE ['[email protected]+N'] TO DISK = N'+N''''[email protected]+N''''+
				 N' WITH DIFFERENTIAL, NOFORMAT, NOINIT, NAME = N'+N''''[email protected]+N''''+
				 N', CHECKSUM, STOP_ON_ERROR, SKIP, REWIND, COMPRESSION, STATS = 10;';
	
		exec(@sql);

		--checking the backup copy we just created
		select @backupSetId = position
		from msdb..backupset where [email protected]
		and backup_set_id=(select max(backup_set_id) from msdb..backupset where [email protected]);

		set @sql=N'Verification error. Backup copy information for "'[email protected]+'" database not found.';

		if @backupSetId is null begin raiserror(@sql, 16, 1) end
		else
		begin
			set @sql=N'RESTORE VERIFYONLY FROM DISK = N'+''''[email protected]+N''''+N' WITH FILE = '+cast(@backupSetId as nvarchar(255));

			exec(@sql);
		end

		--compressing the DB transaction logs
		if(@ClearLog=1)
		begin
			while(exists(select top(1) 1 from @tbllog where [DBName][email protected]))
			begin
				select top(1)
				@FileNameLog=FileNameLog
				from @tbllog
				where [email protected];
			
				set @sql=N'USE ['[email protected]+N'];'+N' DBCC SHRINKFILE (N'+N''''[email protected]+N''''+N' , 0, TRUNCATEONLY)';

				exec(@sql);

				delete from @tbllog
				where [email protected]
				and [email protected];
			end
		end
		
		delete from @tbl
		where [DBName][email protected];
	end
END
GO

[/espandi]

Poiché il controllo dell'integrità dei database richiede molte risorse, possiamo ometterlo durante la creazione di una copia di backup differenziale.

[expand title =”Codice “]

USE [DB_NAME]
GO

SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO

ALTER PROCEDURE [srv].[RunLogBackupDB]
	@ClearLog bit=1 --specifies if the transaction log size should be reduced
AS
BEGIN
	/*
		Backing up the DB transaction log
	*/
	SET NOCOUNT ON;

    declare @dt datetime=getdate();
	declare @year int=YEAR(@dt);
	declare @month int=MONTH(@dt);
	declare @day int=DAY(@dt);
	declare @hour int=DatePart(hour, @dt);
	declare @minute int=DatePart(minute, @dt);
	declare @second int=DatePart(second, @dt);
	declare @pathBackup nvarchar(255);
	declare @pathstr nvarchar(255);
	declare @DBName nvarchar(255);
	declare @backupName nvarchar(255);
	declare @sql nvarchar(max);
	declare @backupSetId as int;
	declare @FileNameLog nvarchar(255);
	
	declare @tbl table (
		[DBName] [nvarchar](255) NOT NULL,
		[LogPathBackup] [nvarchar](255) NOT NULL
	);

	declare @tbllog table(
		[DBName] [nvarchar](255) NOT NULL,
		[FileNameLog] [nvarchar](255) NOT NULL
	);
	
	--Retrieving DB names and full paths for creating backup copies of transaction logs with a non-simple recovery model (full or bulk-logged). System DBs are also excluded
	insert into @tbl (
	           [DBName]
	           ,[LogPathBackup]
	)
	select		DB_NAME(b.[DBID])
	           ,b.[LogPathBackup]
	from [srv].[BackupSettings] as b
	inner join sys.databases as d on b.[DBID]=d.[database_id]
	where d.recovery_model<3
	and DB_NAME([DBID]) not in (
		N'master',
		N'tempdb',
		N'model',
		N'msdb',
		N'ReportServer',
		N'ReportServerTempDB'
	)
	and [LogPathBackup] is not null;

	--Retrieving DB name and the full names of the according transaction log files (as one DB can have multiple logs)
	insert into @tbllog([DBName], [FileNameLog])
	select t.[DBName], tt.[FileName] as [FileNameLog]
	from @tbl as t
	inner join [inf].[ServerDBFileInfo] as tt on t.[DBName]=DB_NAME(tt.[database_id])
	where tt.[Type_desc]='LOG';
	
	--sequentially processing each of the DBs we got earlier
	while(exists(select top(1) 1 from @tbl))
	begin
		set @backupSetId=NULL;

		select top(1)
		@DBName=[DBName],
		@pathBackup=[LogPathBackup]
		from @tbl;
	
		set @[email protected]+N'_Log_backup_'+cast(@year as nvarchar(255))+N'_'+cast(@month as nvarchar(255))+N'_'+cast(@day as nvarchar(255))+N'_'
						+cast(@hour as nvarchar(255))+N'_'+cast(@minute as nvarchar(255))+N'_'+cast(@second as nvarchar(255));
		set @[email protected]@sqldat.com+N'.trn';
		
		--executing the backup procedure
		set @sql=N'BACKUP LOG ['[email protected]+N'] TO DISK = N'+N''''[email protected]+N''''+
				 N' WITH NOFORMAT, NOINIT, NAME = N'+N''''[email protected]+N''''+
				 N', CHECKSUM, STOP_ON_ERROR, SKIP, REWIND, COMPRESSION, STATS = 10;';
	
		exec(@sql);

		--Checking the transaction log backup copy we just created
		select @backupSetId = position
		from msdb..backupset where [email protected]
		and backup_set_id=(select max(backup_set_id) from msdb..backupset where [email protected]);

		set @sql=N'Verification error. Backup copy information for "'[email protected]+'" database not found.';

		if @backupSetId is null begin raiserror(@sql, 16, 1) end
		else
		begin
			set @sql=N'RESTORE VERIFYONLY FROM DISK = N'+''''[email protected]+N''''+N' WITH FILE = '+cast(@backupSetId as nvarchar(255));

			exec(@sql);
		end

		--compressing the DB transaction logs
		if(@ClearLog=1)
		begin
			while(exists(select top(1) 1 from @tbllog where [DBName][email protected]))
			begin
				select top(1)
				@FileNameLog=FileNameLog
				from @tbllog
				where [email protected];
			
				set @sql=N'USE ['[email protected]+N'];'+N' DBCC SHRINKFILE (N'+N''''[email protected]+N''''+N' , 0, TRUNCATEONLY)';

				exec(@sql);

				delete from @tbllog
				where [email protected]
				and [email protected];
			end
		end
		
		delete from @tbl
		where [DBName][email protected];
	end
END
GO

[/espandi]

Come detto sopra, il controllo dell'integrità dei database è un'attività che richiede molte risorse. In combinazione con il fatto che le copie di backup del registro delle transazioni di solito devono essere create abbastanza spesso, questo ci dà un motivo per omettere il controllo dell'integrità durante la creazione di una copia del registro delle transazioni.

Tieni inoltre presente che le copie di backup complete dei database "master", "msdb" e "model" devono essere eseguite periodicamente.

Per automatizzare il processo di creazione della copia di backup, devi semplicemente effettuare una chiamata delle procedure precedentemente implementate nell'Utilità di pianificazione di Windows, nei lavori degli agenti o in qualsiasi altro servizio disponibile simile.

Dovrai impostare la frequenza delle chiamate per ciascuna di queste procedure individualmente in base ai picchi di carico, ai livelli di attività ecc.

L'approccio di base è il seguente:

1) Creazione di una copia di backup completa una volta al giorno
2) Creazione di copie di backup differenziali ogni 2-4 ore
3) Creazione di copie di backup del registro delle transazioni ogni 5-60 minuti

Tieni presente che di solito i database partecipano al sistema di accesso rapido e fail-safe. E, se il successivo utilizza copie di backup del registro delle transazioni, è di vitale importanza non interferire con la procedura. Più specificamente, ciò significa che le copie del registro delle transazioni non devono essere create da diversi processi:in tal caso, la sequenza di backup di queste copie andrà persa.

Qui, abbiamo visto esempi di ciascun database elaborato in sequenza, uno alla volta. Tuttavia, possiamo ottenere un'elaborazione parallela nell'ambiente di produzione, consentendo di eseguire più copie di backup contemporaneamente. Questo può essere affrontato in diversi modi. Ad esempio, chiamando la seguente stored procedure:

USE [DB_NAME]
GO

SET ANSI_NULLS ON
GO

SET QUOTED_IDENTIFIER ON
GO

CREATE PROCEDURE [inf].[RunAsyncExecute]
(
	@sql nvarchar(max),
	@jobname nvarchar(57) = null,   
	@database nvarchar(128)= null,
	@owner nvarchar(128) = null
)
AS BEGIN
/*
	Asynchronous package execution via the Agent's jobs
	RunAsyncExecute - asynchronous execution of T-SQL command or stored prodecure  
	2012 Antonin Foller, Motobit Software, www.motobit.com
	http://www.motobit.com/tips/detpg_async-execute-sql/  
*/  
    SET NOCOUNT ON;  
  
    declare @id uniqueidentifier;

    --Create unique job name if the name is not specified  
    if (@jobname is null) set @jobname= '';

    set @jobname = @jobname + '_async_' + convert(varchar(64),NEWID());
  
    if (@owner is null) set @owner = 'sa';
  
    --Create a new job, get job ID  
    execute msdb..sp_add_job @jobname, @[email protected], @[email protected] OUTPUT;
  
    --Specify a job server for the job  
    execute msdb..sp_add_jobserver @[email protected];
  
    --Specify a first step of the job - the SQL command  
    --(@on_success_action = 3 ... Go to next step)  
    execute msdb..sp_add_jobstep @[email protected], @step_name='Step1', @command = @sql,   
        @database_name = @database, @on_success_action = 3;
  
    --Specify next step of the job - delete the job  
    declare @deletecommand varchar(200);

    set @deletecommand = 'execute msdb..sp_delete_job @job_name='''[email protected]+'''';

    execute msdb..sp_add_jobstep @[email protected], @step_name='Step2', @command = @deletecommand;
  
    --Start the job  
    execute msdb..sp_start_job @[email protected];
  
END  
GO

Qui, l'asincronia si ottiene creando dinamicamente i lavori dell'agente, eseguendoli ed eliminandoli in seguito.

Ora, diamo un'occhiata all'algoritmo generale per il ripristino dei database da copie di backup precedentemente create in un ambiente diverso/di test:

1) Definire quali database devono essere ripristinati e la posizione delle loro copie di backup
2) Ripristino dei database
3) Verifica dell'integrità dei database ripristinati

Ora esamineremo un'implementazione di un algoritmo che ripristina un database da una copia di backup completa. Per una copia differenziale, la procedura è simile, con l'unica differenza che è necessario ripristinare in prima persona una copia di backup completa, seguita dalla copia differenziale.

Per definire quali database devono essere ripristinati, nonché la posizione delle loro copie di backup, creiamo due tabelle come mostrato di seguito:

USE [DB_NAME]
GO

SET ANSI_NULLS ON
GO

SET QUOTED_IDENTIFIER ON
GO

CREATE TABLE [srv].[RestoreSettings](
	[DBName] [nvarchar](255) NOT NULL,
	[FullPathRestore] [nvarchar](255) NOT NULL,
	[DiffPathRestore] [nvarchar](255) NOT NULL,
	[LogPathRestore] [nvarchar](255) NOT NULL,
	[InsertUTCDate] [datetime] NOT NULL,
 CONSTRAINT [PK_RestoreSettings] PRIMARY KEY CLUSTERED 
(
	[DBName] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY];
GO

ALTER TABLE [srv].[RestoreSettings] ADD  CONSTRAINT [DF_RestoreSettings_InsertUTCDate]  DEFAULT (getutcdate()) FOR [InsertUTCDate];
GO

Qui, lo scopo delle colonne è analogo a quelli della tabella [srv].[BackupSettings]. L'unica differenza è che verrà utilizzato il percorso completo per individuare le copie di backup per il ripristino e non per crearne di nuove.

USE [DB_NAME]
GO

SET ANSI_NULLS ON
GO

SET QUOTED_IDENTIFIER ON
GO

CREATE TABLE [srv].[RestoreSettingsDetail](
	[Row_GUID] [uniqueidentifier] NOT NULL,
	[DBName] [nvarchar](255) NOT NULL,
	[SourcePathRestore] [nvarchar](255) NOT NULL,
	TargetPathRestore [nvarchar](255) NOT NULL,
	[Ext] [nvarchar](255) NOT NULL,
	[InsertUTCDate] [datetime] NOT NULL,
 CONSTRAINT [PK_RestoreSettingsDetail] PRIMARY KEY CLUSTERED 
(
	[Row_GUID] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY];
GO

ALTER TABLE [srv].[RestoreSettingsDetail] ADD  CONSTRAINT [DF_RestoreSettingsDetail_Row_GUID]  DEFAULT (newid()) FOR [Row_GUID];
GO

ALTER TABLE [srv].[RestoreSettingsDetail] ADD  CONSTRAINT [DF_RestoreSettingsDetail_InsertUTCDate]  DEFAULT (getutcdate()) FOR [InsertUTCDate];
GO

Questa tabella è necessaria per definire i nomi file completi del database da ripristinare, che vengono poi utilizzati per ulteriori trasferimenti (ad esempio, [SourcePathRestore]='Logical file name' e [TargetPathRestore]='disk:\…\Physical file name ', mentre [Ext]='Estensione file')

In realtà, possiamo definire i nomi logici dei file di database utilizzando la seguente query:

RESTORE FILELISTONLY 
FROM DISK ='disk:\...\backup copy.BAK';

È possibile ottenere informazioni sulle copie di backup che si trovano in un file in questo modo:

RESTORE HEADERONLY
FROM DISK='disk:\...\backup copy.BAK';

Successivamente, abbiamo un'implementazione di una procedura memorizzata utilizzata per ripristinare un database da una copia di backup completa e verificarne l'integrità dei dati:

[expand title =”Codice “]

USE [DB_NAME]
GO

SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO

ALTER PROCEDURE [srv].[RunFullRestoreDB]
AS
BEGIN
	/*
		Recovering a DB from a full backup copy and checking the DB for integrity
	*/
	SET NOCOUNT ON;

    declare @dt datetime=DateAdd(day,-2,getdate());
	declare @year int=YEAR(@dt);
	declare @month int=MONTH(@dt);
	declare @day int=DAY(@dt);
	declare @hour int=DatePart(hour, @dt);
	declare @minute int=DatePart(minute, @dt);
	declare @second int=DatePart(second, @dt);
	declare @pathBackup nvarchar(255);
	declare @pathstr nvarchar(255);
	declare @DBName nvarchar(255);
	declare @backupName nvarchar(255);
	declare @sql nvarchar(max);
	declare @backupSetId as int;
	declare @FileNameLog nvarchar(255);
	declare @SourcePathRestore nvarchar(255);
	declare @TargetPathRestore nvarchar(255);
	declare @Ext nvarchar(255);
	
	declare @tbl table (
		[DBName] [nvarchar](255) NOT NULL,
		[FullPathRestore] [nvarchar](255) NOT NULL
	);

	declare @tbl_files table (
		[DBName] [nvarchar](255) NOT NULL,
		[SourcePathRestore] [nvarchar](255) NOT NULL,
		[TargetPathRestore] [nvarchar](255) NOT NULL,
		[Ext] [nvarchar](255) NOT NULL
	);
	
	--retrieving a list of DB names and the paths to full backup copies
	insert into @tbl (
	           [DBName]
	           ,[FullPathRestore]
	)
	select		[DBName]
	           ,[FullPathRestore]
	from [srv].[RestoreSettings];

	--retrieving detailed info about the new DB files location
	insert into @tbl_files (
	           [DBName]
	           ,[SourcePathRestore]
			   ,[TargetPathRestore]
			   ,[Ext]
	)
	select		[DBName]
	           ,[SourcePathRestore]
			   ,[TargetPathRestore]
			   ,[Ext]
	from [srv].[RestoreSettingsDetail];
	
	--processing each of the DBs we got earlier
	while(exists(select top(1) 1 from @tbl))
	begin
		set @backupSetId=NULL;

		select top(1)
		@DBName=[DBName],
		@pathBackup=[FullPathRestore]
		from @tbl;
	
		set @[email protected]+N'_Full_backup_'+cast(@year as nvarchar(255))+N'_'+cast(@month as nvarchar(255))+N'_'+cast(@day as nvarchar(255))--+N'_'
						--+cast(@hour as nvarchar(255))+N'_'+cast(@minute as nvarchar(255))+N'_'+cast(@second as nvarchar(255));
		set @[email protected]@sqldat.com+N'.bak';

		--creating a backup query and executing it
		set @sql=N'RESTORE DATABASE ['[email protected]+N'_Restore] FROM DISK = N'+N''''[email protected]+N''''+
				 N' WITH FILE = 1,';

		while(exists(select top(1) 1 from @tbl_files where [DBName][email protected]))
		begin
			select top(1)
			@SourcePathRestore=[SourcePathRestore],
			@TargetPathRestore=[TargetPathRestore],
			@Ext=[Ext]
			from @tbl_files
			where [DBName][email protected];

			set @[email protected]+N' MOVE N'+N''''[email protected]+N''''+N' TO N'+N''''[email protected]+N'_Restore.'[email protected]+N''''+N',';

			delete from @tbl_files
			where [DBName][email protected]
			and [SourcePathRestore][email protected]
			and [Ext][email protected];
		end

		set @[email protected]+N' NOUNLOAD,  REPLACE,  STATS = 5';

		exec(@sql);

		--checking the DB for integrity
		set @sql=N'DBCC CHECKDB(N'+N''''[email protected]+'_Restore'+N''''+N')  WITH NO_INFOMSGS';
	
		exec(@sql);
		
		delete from @tbl
		where [DBName][email protected];
	end
END

[/espandi]

Per specificare quale copia di backup completa deve essere utilizzata per il ripristino, viene utilizzato un nome file strutturato in modo speciale:

_Full_backup___.bak

Per automatizzare questo processo di ripristino del database, la chiamata della procedura memorizzata che abbiamo implementato dovrebbe essere inserita nell'Utilità di pianificazione di Windows, nei lavori dell'agente o in qualsiasi altro servizio disponibile simile.

È possibile visualizzare le copie di backup del database più recenti utilizzando la seguente rappresentazione:

USE [DB_NAME]
GO

SET ANSI_NULLS ON
GO

SET QUOTED_IDENTIFIER ON
GO

CREATE VIEW [inf].[vServerLastBackupDB] as
with backup_cte as
(
    select
        bs.[database_name],
        backup_type =
            case bs.[type]
                when 'D' then 'database'
                when 'L' then 'log'
                when 'I' then 'differential'
                else 'other'
            end,
        bs.[first_lsn],
		bs.[last_lsn],
		bs.[backup_start_date],
		bs.[backup_finish_date],
		cast(bs.[backup_size] as decimal(18,3))/1024/1024 as BackupSizeMb,
        rownum = 
            row_number() over
            (
                partition by bs.[database_name], type 
                order by bs.[backup_finish_date] desc
            ),
		LogicalDeviceName = bmf.[logical_device_name],
		PhysicalDeviceName = bmf.[physical_device_name],
		bs.[server_name],
		bs.[user_name]
    FROM msdb.dbo.backupset bs
    INNER JOIN msdb.dbo.backupmediafamily bmf 
        ON [bs].[media_set_id] = [bmf].[media_set_id]
)
select
    [server_name] as [ServerName],
	[database_name] as [DBName],
	[user_name] as [USerName],
    [backup_type] as [BackupType],
	[backup_start_date] as [BackupStartDate],
    [backup_finish_date] as [BackupFinishDate],
	[BackupSizeMb], --uncompressed size
	[LogicalDeviceName],
	[PhysicalDeviceName],
	[first_lsn] as [FirstLSN],
	[last_lsn] as [LastLSN]
from backup_cte
where rownum = 1;

Il risultato

In questa guida abbiamo esaminato un'implementazione del processo di backup automatico su un server e il conseguente ripristino su un altro (un server di prova, ad esempio).

Questo metodo ci consente di automatizzare il processo di creazione della copia di backup, di controllare le copie di backup ripristinandole e di mettere a punto i processi mostrati sopra.

Fonti:

Backup
Ripristina
Backupset
CHECKDB
SHRINKFILE
sys.master_files