sql >> Base de Datos >  >> RDS >> Database

Implementación de copias de seguridad y restauración automatizadas de bases de datos con medios predeterminados

Introducción

Puede encontrar muchas guías sobre cómo hacer copias de seguridad y restaurar bases de datos. En este, mostraremos cómo se puede hacer esto utilizando los medios predeterminados de MS SQL Server.

Este ejemplo cubrirá una serie de enfoques, desde comprobar la integridad de la base de datos antes de realizar una copia de seguridad hasta restaurar la base de datos a partir de una copia de seguridad creada previamente.

La solución

En primer lugar, veamos el algoritmo general que usaremos para hacer una copia de seguridad de una base de datos:

1) Definir qué bases de datos necesitan copia de seguridad
2) Verificar la integridad de las bases de datos elegidas
3) Crear una copia de seguridad (completa, diferencial o copia del registro de transacciones) para cada una de las bases de datos elegidas
4) Comprobación de las copias de seguridad creadas
5) Compresión de los registros de transacciones (si es necesario)

A continuación, puede encontrar un ejemplo de implementación de este algoritmo.

Para definir qué bases de datos necesitan copia de seguridad, crearemos la siguiente tabla:

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

El identificador de la base de datos se encuentra en la primera columna, 'FullPathBackup' contiene la ruta para la creación de una copia de seguridad completa (por ejemplo, 'disk:\…\'), y DiffPathBackup y LogPathBackup contienen rutas completas para la creación de copias diferenciales y del registro de transacciones. respectivamente. Si las columnas DiffPathBackup o LogPathBackup están vacías, no se creará la copia del registro diferencial y/o de transacciones para esta base de datos.

También podemos crear una representación basada en esta tabla:

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

Esta representación le permite comprobar de forma eficaz qué bases de datos participan en el proceso de copia de seguridad.

Ahora, creemos una representación que muestre la información del archivo de la base de datos de la representación del 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

Para crear copias de seguridad completas, implementemos el siguiente procedimiento almacenado:

[expandir título =”Código “]

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

[/expandir]

Según el código, podemos ver que este procedimiento proporciona una solución para los pasos restantes del algoritmo de creación de copias de seguridad.

Los procedimientos que crean copias diferenciales y de registros de transacciones se implementan de manera similar:

[expandir título =”Código “]

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

[/expandir]

Como comprobar la integridad de las bases de datos requiere muchos recursos, podemos omitirlo al crear una copia de seguridad diferencial.

[expandir título =”Código “]

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

[/expandir]

Como se dijo anteriormente, verificar la integridad de las bases de datos es una tarea que requiere muchos recursos. Combinado con el hecho de que las copias de respaldo del registro de transacciones generalmente deben crearse con bastante frecuencia, esto nos da una razón para omitir la verificación de integridad al crear una copia del registro de transacciones.

También tenga en cuenta que las copias de seguridad completas de las bases de datos "maestra", "msdb" y "modelo" deben realizarse periódicamente.

Para automatizar el proceso de creación de copias de seguridad, solo deberá realizar una llamada de los procedimientos implementados anteriormente en el Programador de tareas de Windows, trabajos de agentes o cualquier servicio similar disponible.

Deberá configurar la frecuencia de llamada para cada uno de esos procedimientos individualmente en función de los picos de carga, las mesetas de actividad, etc.

El enfoque básico es el siguiente:

1) Crear una copia de seguridad completa una vez al día
2) Crear copias de seguridad diferenciales cada 2 a 4 horas
3) Crear copias de seguridad del registro de transacciones cada 5 a 60 minutos

Tenga en cuenta que, por lo general, las bases de datos participan en el sistema de acceso rápido y a prueba de fallas. Y, si este último utiliza copias de seguridad del registro de transacciones, es de vital importancia no interferir con el procedimiento. Más específicamente, esto significa que varios procesos diferentes no deben crear copias del registro de transacciones; si esto sucede, se perderá la secuencia de copia de seguridad de estas copias.

Aquí, hemos visto ejemplos de cada base de datos procesada secuencialmente, una a la vez. Sin embargo, podemos lograr un procesamiento paralelo en el entorno de producción, lo que permite realizar varias copias de seguridad simultáneamente. Esto se puede abordar de diferentes maneras. Por ejemplo, llamando al siguiente procedimiento almacenado:

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

Aquí, la asincronía se logra creando dinámicamente los trabajos del Agente, ejecutándolos y eliminándolos después.

Ahora, veamos el algoritmo general para restaurar bases de datos a partir de copias de seguridad creadas previamente en un entorno diferente/de prueba:

1) Definir qué bases de datos se deben restaurar y la ubicación de sus copias de seguridad
2) Restaurar las bases de datos
3) Verificar la integridad de las bases de datos restauradas

Ahora, veremos una implementación de un algoritmo que restaura una base de datos a partir de una copia de seguridad completa. Para una copia diferencial, el procedimiento es similar:la única diferencia es que primero se debe restaurar una copia de respaldo completa, seguida de la copia diferencial.

Para definir qué bases de datos deben restaurarse, así como la ubicación de sus copias de seguridad, creemos dos tablas como se muestra a continuación:

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

Aquí, el propósito de las columnas es análogo al de la tabla [srv].[BackupSettings]. La única diferencia es que la ruta completa se usará para ubicar las copias de respaldo para la restauración y no para crear nuevas.

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

Esta tabla es necesaria para definir los nombres de archivo completos de la base de datos que se está restaurando, que luego se usan para transferencias adicionales (por ejemplo, [SourcePathRestore]='Nombre de archivo lógico' y [TargetPathRestore]='disco:\…\Nombre de archivo físico ', while [Ext]='Extensión de archivo')

En realidad, podemos definir nombres lógicos de los archivos de la base de datos usando la siguiente consulta:

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

Obtener información sobre las copias de seguridad ubicadas en un archivo se puede hacer de esta manera:

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

A continuación, tenemos una implementación de un procedimiento almacenado utilizado para restaurar una base de datos a partir de una copia de seguridad completa y verificar la integridad de los datos:

[expandir título =”Código “]

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

[/expandir]

Para especificar qué copia de seguridad completa se debe usar para la restauración, se usa un nombre de archivo especialmente estructurado:

_Full_backup___.bak

Para automatizar este proceso de restauración de la base de datos, la llamada del procedimiento almacenado que implementamos debe colocarse en el Programador de tareas de Windows, los trabajos del Agente o cualquier servicio disponible similar.

Puede ver las copias de seguridad de la base de datos más recientes utilizando la siguiente representación:

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;

El resultado

En esta guía, hemos analizado la implementación de un proceso de copia de seguridad automatizado en un servidor y la consiguiente restauración en otro diferente (un servidor de prueba, por ejemplo).

Este método nos permite automatizar el proceso de creación de copias de seguridad, verificar las copias de seguridad al restaurarlas y ajustar los procesos que se muestran arriba.

Fuentes:

Copia de seguridad
Restaurar
Copia de seguridad
CHECKDB
SHRINKFILE
sys.master_files