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:
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 e @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.
