Script – Gerando comando de restore dinamicamente

Olá pessoal,

Antes de qualquer coisa, desejo um feliz ano novo a todos…🙂

No post anterior, compartilhei um script que retorna o nome dos arquivos de backup na sua unidade de disco utilizando t-sql.

E como prometido, neste post compartilho com vocês um script para gerar dinamicamente o comando de restore de um banco de dados.

Existe uma preocupação, que não é só minha, com a padronização e automatização da administração do ambiente, mesmo que se trabalhe sozinho e principalmente quando em equipe.

No nosso dia-a-dia, temos solicitações de atualizações de bases de homologação e/ou desenvolvimento e até mesmo a necessidade de se ter um banco de dados de estrutura igual mais com um nome diferente em um ambiente qualquer.
Existem ferramentas que fazem isto, mas nem sempre temos disponível por diversos motivos .

Caso não seja definido um padrão, podemos ter “zilhões” de scripts espalhados fazendo a mesma coisa e/ou incompletos e em muitos casos emergências, dificuldade de um profissional que esteja iniciando em executar rapidamente o comando (Além da incerteza!)

Pensando nisto, criei este script que pra mim tem sido muito útil e espero que ajude a alguém.

Script da procedure

-- ============================================ --
-- TÍTULO: SCRIPT PADRÃO - RESTORE DE DATABASE --
-- TIPO : Procedure --
-- AUTOR : Leandro Ribeiro --
-- DATA DE CRIAÇÃO: 13/07/2012 --
-- ============================================ --
-- Twitter: @sqlleroy --
-- blog: sqlleroy.com --
-- email: sqlleroy@hotmail.com --
-- ============================================ --

SET NOCOUNT ON
SET ANSI_WARNINGS ON
SET ANSI_NULLS ON
GO

USE master
GO

IF OBJECT_ID('prd_RESTORE_DATABASE') IS NOT NULL
DROP PROCEDURE [prd_RESTORE_DATABASE]
GO

CREATE PROCEDURE [prd_RESTORE_DATABASE]
 @Database NVARCHAR(100) -- Nome do banco de dados a ser restaurado
 ,@AlterarNomeDatabase CHAR(1) -- S -> Alterar nome ; N -> Para não alterar o nome
 ,@NovoNomeDatabase NVARCHAR(100) -- Novo nome para o banco de dados a ser restaurado
 ,@PrintorExec CHAR(1) -- P -> Para printar ; E -> Para executar diretamente
 ,@PathBackup VARCHAR(100) -- Diretório onde se encontra o arquivo de backup a ser restaurado
 ,@ExtensaoBackup CHAR(4) -- Estensão do arquivo de backup a ser restaurado

-- Utilizados na procedure prd_SearchBackupArchive
 ,@SeekDaysAgo VARCHAR(3) -- Pesquisar por arquivos de backup com data modificada pela última
 -- vez menor ou igual a data atual menos "@SeekDaysAgo" dias.
 ,@DbSystem CHAR(1) -- N -> Para considerar bancos de sistema ; S -> para considerar
AS
SET NOCOUNT ON
SET ANSI_WARNINGS ON
SET ANSI_NULLS ON

-- ============================ --
-- Tratamento para as variáveis --
-- ============================ --
IF @Database IS NULL
 OR @AlterarNomeDatabase IS NULL
 OR @NovoNomeDatabase IS NULL
 OR @PrintorExec IS NULL
 OR @PathBackup IS NULL
 OR @ExtensaoBackup IS NULL
 OR @SeekDaysAgo IS NULL
 OR @DbSystem IS NULL
BEGIN;
 RAISERROR('Os Paramêtros não podem ser nulos', 16, 1);
 RETURN;
END;

IF @Database = ''
 OR @AlterarNomeDatabase = ''
 OR @PrintorExec = ''
 OR @PathBackup = ''
 OR @ExtensaoBackup = ''
 OR @SeekDaysAgo = ''
 OR @DbSystem = ''
BEGIN;
 RAISERROR('Os Paramêtros não podem ser vazios', 16, 1);
 RETURN;
END;

IF @AlterarNomeDatabase = 'S'
 AND @NovoNomeDatabase = ''
BEGIN;
 RAISERROR('O novo nome para o database não pode ser vazio quando setar "S" para @AlterarNomeDatabase', 16, 1);
 RETURN;
END;

-- =========================== --
-- Formata os nomes dos bancos --
-- =========================== --
SET @Database = UPPER(@Database)
IF @NovoNomeDatabase <> '' SET @NovoNomeDatabase = UPPER(@NovoNomeDatabase)

DECLARE
 @NomeArqBackup VARCHAR (100) -- Retorna o nome físico do arquivo de Backup
 ,@Sql VARCHAR (MAX) -- Armazena as queries a serem executadas
 ,@Dados NVARCHAR (256) -- Armazena o diretório default dos arquivos de Dados do SQL
 ,@Log NVARCHAR (256) -- Armazena o diretório default dos arquivos de Log do SQL
 ,@Backup NVARCHAR (256) -- Armazena o diretório default dos arquivos de Backup do SQL
 ,@MaxId TINYINT -- Utilizado para formatação de finalização do comando move

-- ======================== --
-- Cria tabelas temporárias --
-- ======================== --
IF OBJECT_ID('tempdb..#DefaultData') IS NOT NULL
DROP TABLE #DefaultData

CREATE TABLE #DefaultData ([Value] VARCHAR(100), [Data] NVARCHAR(256))

IF OBJECT_ID('tempdb..#Arquivos') IS NOT NULL
DROP TABLE #Arquivos

CREATE TABLE #Arquivos (Id TINYINT IDENTITY(1,1), LogicalName NVARCHAR(128),PhysicalName NVARCHAR(260), ExtensaoArq CHAR(4), Type CHAR(1) )

-- ========================================================================================= --
-- Tabela temporária que armazenará os nomes lógicos e físicos contidos no arquivo de backup --
-- ========================================================================================= --
IF OBJECT_ID ('tempdb..#RestoreFileListOnly') IS NOT NULL
DROP TABLE #RestoreFileListOnly

CREATE TABLE #RestoreFileListOnly
(
 LogicalName NVARCHAR(128)
 ,PhysicalName NVARCHAR(260)
 ,Type CHAR(1)
 ,FileGroupName NVARCHAR(128)
 ,Size NUMERIC(20,0)
 ,MaxSize NUMERIC(20,0)
 ,Fileid TINYINT
 ,CreateLSN NUMERIC(25,0)
 ,DropLSN NUMERIC(25, 0)
 ,UniqueID UNIQUEIDENTIFIER
 ,ReadOnlyLSN NUMERIC(25,0)
 ,ReadWriteLSN NUMERIC(25,0)
 ,BackupSizeInBytes BIGINT
 ,SourceBlocSize INT
 ,FileGroupId INT
 ,LogGroupGUID UNIQUEIDENTIFIER
 ,DifferentialBaseLSN NUMERIC(25,0)
 ,DifferentialBaseGUID UNIQUEIDENTIFIER
 ,IsReadOnly BIT
 ,IsPresent BIT
 ,TDEThumbprint VARCHAR(100)
)
-- ========================== --
-- Diretório Default de Dados --
-- ========================== --
INSERT INTO #DefaultData
EXEC sys.xp_instance_regread N'HKEY_LOCAL_MACHINE', N'Software\Microsoft\MSSQLServer\MSSQLServer', N'DefaultData'

SELECT @Dados = Data FROM #DefaultData WHERE Value = 'DefaultData'

-- ======================== --
-- Diretório Default de Log --
-- ======================== --
INSERT INTO #DefaultData
EXEC sys.xp_instance_regread N'HKEY_LOCAL_MACHINE', N'Software\Microsoft\MSSQLServer\MSSQLServer', N'DefaultLog'

SELECT @Log = Data FROM #DefaultData WHERE Value = 'DefaultLog'

-- =========================== --
-- Diretório Default de Backup --
-- =========================== --
INSERT INTO #DefaultData
EXEC sys.xp_instance_regread N'HKEY_LOCAL_MACHINE', N'Software\Microsoft\MSSQLServer\MSSQLServer', N'BackupDirectory'

SELECT @Backup = Data FROM #DefaultData WHERE Value = 'BackupDirectory'

-- ============================== --
-- Montando o comenado de RESTORE --
-- ============================== --

-- Procedure que retornará o nome físico do backup
EXEC [prd_SearchBackupArchive] @Database, @ExtensaoBackup, @PathBackup , @DbSystem, @SeekDaysAgo, @NomeArqBackup OUTPUT

IF SUBSTRING(@NomeArqBackup,1,6) <> 'ERROR:'
BEGIN

 -- Insere na tabela os dados contidos no arquivo de backup --
 INSERT INTO #RestoreFileListOnly
 EXEC ('RESTORE FILELISTONLY FROM DISK = ''' + @PathBackup + @NomeArqBackup + '''')

-- Recupera o nome lógico e físico do arquivo de Dados --
 INSERT INTO #Arquivos
 SELECT LogicalName,
 CASE Fileid
 WHEN 1 THEN substring(physicalname,charindex(LogicalName,physicalname),charindex('.mdf',physicalname))
 ELSE substring(physicalname,charindex(LogicalName,physicalname),charindex('.ndf',physicalname))
 END PhysicalName,
 CASE Fileid
 WHEN 1 THEN substring(physicalname,charindex('.mdf',physicalname),charindex('.mdf',physicalname))
 ELSE substring(physicalname,charindex('.ndf',physicalname),charindex('.ndf',physicalname))
 END ExtensaoArq,
 Type
 FROM #RestoreFileListOnly
 WHERE Type = 'D'

-- Recupera o nome lógico e físico do arquivo de Log --
 INSERT INTO #Arquivos
 SELECT
 LogicalName,
 substring(physicalname,charindex(LogicalName,physicalname),charindex('.ldf',physicalname)) PhysicalName,
 substring(physicalname,charindex('.ldf',physicalname),charindex('.ldf',physicalname)) ExtensaoArq,
 Type
 FROM #RestoreFileListOnly
 WHERE Type = 'L'

 -- Registra o maior ID para utilizar na formatação do comando move
 select @MaxId = MAX(Id) FROM #Arquivos

 -- Montando o comando dinamicamente
 IF @AlterarNomeDatabase = 'N'
 BEGIN
 -- Restore
 IF (SELECT name FROM sys.databases where name = @Database) IS NULL
 SET @Sql = CHAR(13) + 'RESTORE DATABASE ' + @Database + ' FROM DISK = ''' + @PathBackup + @NomeArqBackup + '''' + CHAR(13) +
 ' WITH STATS = 10,' + CHAR(13)
 ELSE
 SET @Sql = CHAR(13) + 'RESTORE DATABASE ' + @Database + ' FROM DISK = ''' + @PathBackup + @NomeArqBackup + '''' + CHAR(13) +
 ' WITH REPLACE, STATS = 10,' + CHAR(13)

-- Move para arquivos de dados
 SELECT @Sql = @Sql
 + ' MOVE ''' + LogicalName + ''' TO ''' + @Dados + '\' + PhysicalName + ''',' + CHAR(13)
 FROM #Arquivos
 WHERE TYPE = 'D'
 ORDER BY Id ASC

 -- Move para arquivos de log
 SELECT @Sql = @Sql
 + ' MOVE ''' + LogicalName + ''' TO ''' + @Log + '\' + PhysicalName + '''' +
 CASE WHEN @MaxId > Id THEN ',' + CHAR(13) ELSE CHAR(13) END
 FROM #Arquivos
 WHERE TYPE = 'L'
 ORDER BY Id ASC

 -- Forçando paramêtros de databases recomendados
 SET @Sql = @Sql + + CHAR(13) +
 'ALTER DATABASE ' + @Database + ' SET PAGE_VERIFY CHECKSUM;' + CHAR(13) +
 'ALTER DATABASE ' + @Database + ' SET AUTO_CLOSE OFF;' + CHAR(13) +
 'ALTER DATABASE ' + @Database + ' SET AUTO_SHRINK OFF;' + CHAR(13) +
 'ALTER DATABASE ' + @Database + ' SET AUTO_CREATE_STATISTICS ON;' + CHAR(13) +
 'ALTER DATABASE ' + @Database + ' SET AUTO_UPDATE_STATISTICS ON;' + CHAR(13)
 END
 ELSE
 BEGIN

 -- Restore
 IF (SELECT name FROM sys.databases where name = @NovoNomeDatabase) IS NULL
 SET @Sql = CHAR(13) + 'RESTORE DATABASE ' + @NovoNomeDatabase + ' FROM DISK = ''' + @PathBackup + @NomeArqBackup + '''' + CHAR(13) +
 ' WITH STATS = 10,' + CHAR(13)
 ELSE
 SET @Sql = CHAR(13) + 'RESTORE DATABASE ' + @NovoNomeDatabase + ' FROM DISK = ''' + @PathBackup + @NomeArqBackup + '''' + CHAR(13) +
 ' WITH REPLACE, STATS = 10,' + CHAR(13)

-- Move para arquivos de dados
 SELECT @Sql = @Sql
 + ' MOVE ''' + LogicalName + ''' TO ''' + @Dados + '\' + @NovoNomeDatabase + CONVERT(VARCHAR,Id) + ExtensaoArq + ''',' + CHAR(13)
 FROM #Arquivos
 WHERE TYPE = 'D'
 ORDER BY Id ASC

 -- Move para arquivos de log
 SELECT @Sql = @Sql
 + ' MOVE ''' + LogicalName + ''' TO ''' + @Log + '\' + @NovoNomeDatabase + CONVERT(VARCHAR,Id) + ExtensaoArq + '''' +
 CASE WHEN @MaxId > Id THEN ',' + CHAR(13) ELSE CHAR(13) END
 FROM #Arquivos
 WHERE TYPE = 'L'
 ORDER BY Id ASC

-- Alterando nome Logico do banco de dados --
 SELECT @Sql = @Sql + CHAR(13) +
 'ALTER DATABASE ' + @NovoNomeDatabase + ' MODIFY FILE (NAME = ' + LogicalName + ', NEWNAME = ' + @NovoNomeDatabase + CONVERT(VARCHAR,Id) + '_Data' + ')'
 FROM #Arquivos
 WHERE TYPE = 'D'

 SELECT @Sql = @Sql + CHAR(13) +
 'ALTER DATABASE ' + @NovoNomeDatabase + ' MODIFY FILE (NAME = ' + LogicalName + ', NEWNAME = ' + @NovoNomeDatabase + CONVERT(VARCHAR,Id) + '_Log' + ')'
 FROM #Arquivos
 WHERE TYPE = 'L'

-- Forçando paramêtros de databases recomendados
 SET @Sql = @Sql + CHAR(13) + CHAR(13) +
 'ALTER DATABASE ' + @NovoNomeDatabase + ' SET PAGE_VERIFY CHECKSUM;' + CHAR(13) +
 'ALTER DATABASE ' + @NovoNomeDatabase + ' SET AUTO_CLOSE OFF;' + CHAR(13) +
 'ALTER DATABASE ' + @NovoNomeDatabase + ' SET AUTO_SHRINK OFF;' + CHAR(13) +
 'ALTER DATABASE ' + @NovoNomeDatabase + ' SET AUTO_CREATE_STATISTICS ON;' + CHAR(13) +
 'ALTER DATABASE ' + @NovoNomeDatabase + ' SET AUTO_UPDATE_STATISTICS ON;' + CHAR(13)

 -- Primeiro backup para o novo banco de dados
 SET @Sql = @Sql + CHAR(13) + CHAR(13) +
 'BACKUP DATABASE ' + @NovoNomeDatabase + ' TO DISK = N''' + @Backup + '\' + @NovoNomeDatabase + '_backup_Inicial_' + CONVERT(varchar,GETDATE(),112) + '_' + REPLACE(CONVERT(varchar,GETDATE(),108),':','') + '.BAK'' WITH INIT, NAME = N''' + @NovoNomeDatabase + '_backup_Inicial'', STATS = 10, COMPRESSION;'
 END

 IF @PrintorExec = 'P'
 BEGIN
 PRINT @Sql;
 PRINT ''
 END

 IF @PrintorExec = 'E'
 EXEC (@Sql);
END
ELSE
 -- Retorna o Erro identificado
 SELECT @NomeArqBackup

Download do script (Formatação melhor!)

Este procedimento realiza as seguintes tarefas, resumidamente:

  • Lê no registro do windows os diretórios padrão do SQL Server para os arquivos de dados, Log e Backup
  • Pesquisa no diretório de backup informado pelo backup full desejado através da procedure “prd_SearchBackupArchive”
  • Permite escolher o backup que será restaurado através do parâmetro “@SeekDaysAgo” utilizada na procedure “prd_SearchBackupArchive”.
  • Recupera os nomes lógicos e físicos do banco de dados contidos no arquivo de backup através do comando RESTORE FILELISTONLY.
  • Monta dinamicamente o comando de RESTORE  movendo os arquivos de dados e log para os diretórios padrão da instância através do comando MOVE.
  • Permite alterar o nome do banco de dados, dos arquivos físicos e lógicos durante o Restore.
  • Força a utilização dos parâmetros recomendados para o banco de dados (Page verify Checksum, AutoClose off, AutoShrink off, AutoCreateStatistics on, AutoUpdateStatistics on).
  • Permite executar o restore diretamente ou exibir na tela através do comando PRINT para conferência e posterior execução.

Exemplos de execução:

Comando de Restore Simples

Comando de Restore Simples

Comando de Restore alterando o nome do Database

Comando de Restore alterando o nome do Database

Bem é isso pessoal… Espero que seja útil.

Se tiver alguma sugestão de melhoria para o script, esteja a vontade para enviar um email para sqlleroy@hotmail.com.

Até o próximo post.

**********************************************************************
ATUALIZAÇÃO – 15/02/2012
**********************************************************************
Fiz alguns ajustes no script  para se adaptar a alguns possíveis problemas e melhorar a sua legibilidade, tais como:

  • Alteração das variáveis de controle do tipo CHAR para o tipo BIT.
  • Alteração do banco existente para “RESTRICTED_USER” com “ROLLBACK IMMEDIATE” para execução do restore.
  • Inclusão de variáveis de controle  (@ExecBackup@AlteraParametros) a fim de se ter a opção de escolha de executar ou não o backup pós restore ou mesmo decidir se  irá alterar os parâmetros do banco de dados.
  • Verifica se o status do banco de dados está como “RESTORING” e executa o Drop do database (Pode ocorrer em caso de falha na execução do restore por uma corrupção do arquivo de backup, por exemplo).
  • Ajustes no script a fim de retirar redundâncias de código melhorando assim a legibilidade.

Se tiver alguma sugestão ou crítica para melhorar o script, podem enviar e-mails para sqlleroy@hotmail.com.

Vocês podem baixar o script atualizado aqui.

Aproveito para incluir o link de download  do script do post anterior que pesquisa e retorna o nome do arquivo de backup a partir do nome do banco de dados. Este script é utilizado na procedure de Restore.

Espero que seja útil.

Explore posts in the same categories: Administração, Scripts, SQL Server, Virtual PASS BR

Tags: , , ,

You can comment below, or link to this permanent URL from your own site.

5 Comentários em “Script – Gerando comando de restore dinamicamente”

  1. CrespiDB Says:

    Grande Leandro, eu estava mesmo precisando de um script assim, mas estava com preguiça de começar eheheh
    Abraço, Rodrigo.

  2. Alex - Lekao Says:

    Ola Leandro, boa tarde e um excelente 2013 para vc.

    Cara gostei do script vou utiliza-lo bastante pq uso base de testes locais na minha maquina utilizando os backups feitos durante a noite na empresa.

    Acredito que sera muito bom e util.

    Obrigado.

    Abraco.

    Alex – Lekao


    • Um excelente 2013 pra você também Alex.

      Utilizo bastante este script e me deu um enorme prazer ao escrever.
      Maior prazer é saber que também será útil pra outras pessoas.

      Fico feliz em ter contribuído.

      Abraço,
      Leandro Ribeiro
      @sqlleroy


  3. […] Script – Gerando comando de restore dinamicamente […]


Deixe uma resposta

Preencha os seus dados abaixo ou clique em um ícone para log in:

Logotipo do WordPress.com

Você está comentando utilizando sua conta WordPress.com. Sair / Alterar )

Imagem do Twitter

Você está comentando utilizando sua conta Twitter. Sair / Alterar )

Foto do Facebook

Você está comentando utilizando sua conta Facebook. Sair / Alterar )

Foto do Google+

Você está comentando utilizando sua conta Google+. Sair / Alterar )

Conectando a %s


%d blogueiros gostam disto: