{"id":832,"date":"2016-04-05T00:31:00","date_gmt":"2016-04-05T03:31:00","guid":{"rendered":"http:\/\/thesqltimes.com\/blog\/?p=832"},"modified":"2016-04-05T19:58:00","modified_gmt":"2016-04-05T22:58:00","slug":"copiando-bases-sql-server-com-copy-sqldatabase","status":"publish","type":"post","link":"https:\/\/thesqltimes.com\/blog\/2016\/04\/05\/copiando-bases-sql-server-com-copy-sqldatabase\/","title":{"rendered":"Copiando bases SQL Server com Copy-SQLDatabase"},"content":{"rendered":"<div class=\"pld-like-dislike-wrap pld-template-1\">\r\n    <div class=\"pld-like-wrap  pld-common-wrap\">\r\n    <a href=\"javascript:void(0)\" class=\"pld-like-trigger pld-like-dislike-trigger  \" title=\"Muito \u00fatil!\" data-post-id=\"832\" data-trigger-type=\"like\" data-restriction=\"cookie\" data-already-liked=\"0\">\r\n                        <i class=\"fas fa-thumbs-up\"><\/i>\r\n                <\/a>\r\n    <span class=\"pld-like-count-wrap pld-count-wrap\">    <\/span>\r\n<\/div><\/div><span class=\"span-reading-time rt-reading-time\" style=\"display: block;\"><span class=\"rt-label rt-prefix\">Tempo de Leitura:<\/span> <span class=\"rt-time\"> 6<\/span> <span class=\"rt-label rt-postfix\">minutos<\/span><\/span><p>Copiar bases entre inst\u00e2ncias \u00e9 procedimento muito comum em qualquer ambiente SQL, na maioria dos casos para manter ambientes de homologa\u00e7\u00e3o, teste, desenvolvimento, etc. Esta tarefa pode ser muito simples, quando a base \u00e9 pequena e tem poucos arquivos, ou complicada, quando a base \u00e9 grande e h\u00e1 muitos arquivos envolvidos. A complexidade pode aumentar mais quando \u00e9 preciso manter as permiss\u00f5es, executar scripts de expurgo, etc.\u00a0 Pensando nisso, desenvolvi um script em powershell que tenta facilitar ao m\u00e1ximo este processo ao mesmo tempo que fornece uma s\u00e9rie de recursos para este procedimento. Este artigo vai apresentar algum destes recursos!<\/p>\n<p>H\u00e1 um s\u00e9rie de \u201cpequenos probleminhas\u201d que me incomodam bastante ao realizar restores de bancos de dados no SQL Server. Destaco os seguintes que contribuem para que este processo fique chato e cansativo:<\/p>\n<ol>\n<li>Estrutura de discos diferentes na inst\u00e2ncia de origem e destino<br \/>\nMount points, letras diferentes, espa\u00e7o dispon\u00edvel, etc., s\u00e3o elementos que resultam em retrabalhos e podem acarretar erros no processo de RESTORE.<\/li>\n<li>Manter as permiss\u00f5es na base de destino<br \/>\nAo longo do tempo, diferentes usu\u00e1rios ganham diferentes tipos de acesso \u00e0 base, e manter este controle para um posterior restore pode deixar mais caro ainda.<br \/>\nEste \u00e9 um tipo de solicita\u00e7\u00e3o bastante comum: Sobrescreva a base, mantendo os usu\u00e1rios e permiss\u00f5es!<\/li>\n<li>Executar scripts ap\u00f3s a c\u00f3pia<br \/>\n\u00c9 comum que um ou mais scripts precisem ser executados ap\u00f3s o restore para, por exemplo, alterar a senha de todos os usu\u00e1rios para 123456. Estes scripts podem variar conforme a necessidade do respons\u00e1vel pela mesma.<\/li>\n<\/ol>\n<p>Copy-SQLDatabase cuida de todos estas quest\u00f5es e muito mais, permitindo uma maneira mais flex\u00edvel de copiar bases.<\/p>\n<h3>Pr\u00e9-Requisitos<\/h3>\n<p>Para executar os exemplos a seguir voc\u00ea ir\u00e1 precisar do seguinte:<\/p>\n<ul>\n<li>Powershell 2.0 ou superior (Instalado junto com o Windows a partir da vers\u00e3o 7\/2008)<\/li>\n<li>M\u00f3dulo CustomMSSQL<br \/>\n<a href=\"http:\/\/scriptstore.thesqltimes.com\/custommssql\/sobre-custommssql\/custommssql\/\" target=\"_blank\">CustomMSSQL<\/a> \u00e9 um modulo em powershell, contendo v\u00e1rios cmdlets, inclusive <strong>Copy-SQLDatabase<\/strong>.<br \/>\nO link para download est\u00e1 acima e a instala\u00e7\u00e3o \u00e9 f\u00e1cil e r\u00e1pida.<br \/>\nPara executar os comandos, \u00e9 preciso importar o m\u00f3dulo usando o comando Import-Module. No link do download existem instru\u00e7\u00f5es de instala\u00e7\u00e3o e uso do m\u00f3dulo.<\/li>\n<li>Pelo menos uma inst\u00e2ncia SQL Server 2005 ou superior (\u00d3bvio que voc\u00ea n\u00e3o ir\u00e1 usar a produ\u00e7\u00e3o para testes, ok?)<\/li>\n<li>Ambiente powershell<\/li>\n<li>\n<ul>\n<li>Permita a execu\u00e7\u00e3o de scripts: Abra o powershell como Administrador e execute o comando:\n<pre class=\"lang:ps decode:true\" title=\"Habilitar execu\u00e7\u00e3o de scripts\">Set-ExecutionPolicy Unrestricted<\/pre>\n<\/li>\n<li>Certifique-se que sua m\u00e1quina tenha acesso ao servidor de destino (computador onde a inst\u00e2ncia de destino est\u00e1)<\/li>\n<li>\n<ul>\n<li>Verifique se o seguinte comando executa normalmente: Get-WmiObject Win32_Volume \u2013ComputerName &lt;IP-ou-Nome-Destino&gt; | select Name\n<ul>\n<li>Em caso de falhas, verifique as permiss\u00f5es de acesso do WMI na m\u00e1quina de destino.<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n<p>&nbsp;<\/p>\n<h3>Exemplo 1: C\u00f3pia simples<\/h3>\n<pre class=\"lang:ps decode:true\" title=\"Copy-SQLDatabase: C\u00f3pia simples\">Copy-SQLDatabase \u2013SourceServerInstance SQL1 \u2013SourceDatabase MeuBanco \u2013DestinationServerInstance SQL2 \u2013DestinationDatabase Meubanco \u2013BackupFolder \u201c\\\\SQL2\\c$\u201d<\/pre>\n<p>Neste exemplo, estamos usando a forma mais b\u00e1sica de c\u00f3pia, atrav\u00e9s do m\u00e9todo Backup\/Restore via T-SQL. Apenas especificamos as informa\u00e7\u00f5es de conex\u00e3o como inst\u00e2ncia de origem e de destino, al\u00e9m do nome da base na origem e o nome que a base ir\u00e1 ter no destino. Por padr\u00e3o, Windows Authentication ser\u00e1 usado. O Copy-SQLDatabase ainda n\u00e3o permite a conex\u00e3o usando SQL Authentication, mas dever\u00e1 ser implementando na pr\u00f3xima vers\u00e3o. O diret\u00f3rio especificado no par\u00e2metro \u201cBackupFolder\u201d indica o local onde o Backup ser\u00e1 gerado. As contas de servi\u00e7o das inst\u00e2ncias envolvidas dever\u00e3o ter as devidas permiss\u00f5es neste diret\u00f3rio. Na origem, Copy-SQLDatabase ir\u00e1 tentar usar as op\u00e7\u00f5es COMPRESSION e COPY_ONLY, se suportados.<\/p>\n<p>Note que n\u00e3o foi necess\u00e1rio especificar nenhum local para os arquivos. Este \u00e9 uma das grandes facilidades fornecidas por Copy-SQLDatabase. O cmdlet ir\u00e1 tentar determinar o melhor local para os arquivos da base. Usando o backup de origem, ele ir\u00e1 determinar os arquivos existentes, bem como o tamanho, e ir\u00e1 gerar o comando de RESTORE adequadamente. H\u00e1 alguns algoritmos que Copy-SQLDatabase ir\u00e1 usar para distribuir os arquivos nos volumes existentes no destino. O algoritmo usado vai depender de alguns fatores, como o conjunto de par\u00e2metros informados. O comportamento padr\u00e3o \u00e9 o seguinte:<\/p>\n<ul>\n<li>Se a base de destino n\u00e3o existe, ent\u00e3o ele ir\u00e1 partir do maior arquivo e encontrar o volume com maior espa\u00e7o livre. O processo vai se repetir at\u00e9 que todos os arquivos estejam com seus volumes determinados. Se n\u00e3o houver volume com espa\u00e7o livre para algum arquivo, o script ir\u00e1 encerrar a execu\u00e7\u00e3o reportando o erro \u201cNO_VOLUME_FOUND_FOR_FILE\u201d.<\/li>\n<li>Se a base de destino existe, ent\u00e3o ele ir\u00e1 alocar os arquivos no mesmo diret\u00f3rio que os arquivos existentes (mapeando atrav\u00e9s do nome l\u00f3gico do arquivo). Se houver um arquivo a mais, ele ir\u00e1 escolher um dos diret\u00f3rios usados pela base aleatoriamente para alocar o novo arquivo. Isso poder\u00e1 resultar em erros de espa\u00e7o retornados pelo comando de RESTORE, j\u00e1 que o cmdlet assume que se a base existe, ela est\u00e1 no melhor local poss\u00edvel e, portanto, n\u00e3o valida o espa\u00e7o. Se isto ocorrer, voc\u00ea pode usar o par\u00e2metro \u201cNoUseCurrentFilesFolder\u201d para for\u00e7ar o cmdlet a usar o algoritmo anterior, mesmo para a base existente:\n<pre class=\"lang:ps decode:true\" title=\"Copy-SQLDatabase: -NoUseCurrentFilesFolder\">Copy-SQLDatabase \u2013SourceServerInstance SQL1 \u2013SourceDatabase MeuBanco \u2013DestinationServerInstance SQL2 \u2013DestinationDatabase Meubanco \u2013BackupFolder \u201c\\\\SQL2\\c$\u201d \u2013NoUseCurrentFilesFolder<\/pre>\n<\/li>\n<\/ul>\n<p>Por padr\u00e3o, o script ir\u00e1 manter as permiss\u00f5es, caso a base exista. Antes de sobrescrever a base, as permiss\u00f5es, roles e seus membros, ser\u00e3o lidas e salvas na mem\u00f3ria. Ap\u00f3s o restore, as permiss\u00f5es ser\u00e3o aplicadas na base restaurada. Para desativar esse comportamento, utilize o par\u00e2metro \u201c-KeepPermissions $false\u201d<\/p>\n<h3>Exemplo 2: C\u00f3pia simples com execu\u00e7\u00e3o de scripts<\/h3>\n<pre class=\"wrap:true lang:ps decode:true\" title=\"Copy-SQLDatabase: Executando scripts\">Copy-SQLDatabase \u2013SourceServerInstance SQL1 \u2013SourceDatabase MeuBanco \u2013DestinationServerInstance SQL2 \u2013DestinationDatabase Meubanco \u2013BackupFolder \u201c\\\\SQL2\\c$\u201d \u2013PostScripts \u201cSELECT DB_NAME() as DBName\u201d,\u201dFILE:\\\\ARQUIVOS\\SQL\\Restore\\Script.sql\u201d,\u201dFOLDER:\\\\ARQUIVOS\\SQL\\Restore\\MeuBanco\\*.sql\u201d<\/pre>\n<p>Um das minhas features favortias deste cmdlet \u00e9 a capacidade de executar scripts ap\u00f3s o restore. Isso \u00e9 \u00fatil quando existe uma s\u00e9rie de scripts que o respons\u00e1vel pela base precisa que seja executado sempre que o restore for feito. O Par\u00e2metro \u201c-PostScripts\u201d aceita um Array de strings onde cada string especifica um comando SQL, um arquivo ou um diret\u00f3rio contendo scripts para serem executados. Cada script \u00e9 executado na ordem em que foi especificado, e no caso de diret\u00f3rios os scripts ser\u00e3o executados na ordem do nome do arquivo. No exemplo acima, a ordem ser\u00e1:<\/p>\n<ol>\n<li>O script SQL<\/li>\n<li>Arquivo Script.sql<\/li>\n<li>Arquivos\u00a0 no diret\u00f3rio \u201cMeuBanco\u201d, ordenados pelo nome (seguindo as regras de ordena\u00e7\u00e3o do Windows)<\/li>\n<\/ol>\n<p>Se um script falhar, por padr\u00e3o, a execu\u00e7\u00e3o dos scripts se encerrar\u00e1 e o erro do script ser\u00e1 logado. Voc\u00ea pode controlar esse comportamento usando o par\u00e2metro \u201c\u2013PostScriptsPolicy\u201d. Este par\u00e2metro indica o que o cmdlet ir\u00e1 fazer quando o script resultar em erro. Para ignorar o erro e executar o pr\u00f3ximo script, especifique o valor \u201cSkipErrors\u201d.<\/p>\n<p>Voc\u00ea pode se perguntar se isso n\u00e3o representaria um risco, pois o script pode conter c\u00f3digo malicioso, que prejudique outras bases ou mesmo a inst\u00e2ncia. Para evitar isto, o cmdlet aceita o par\u00e2metro \u201cUseLimitedUser\u201d. Com este par\u00e2metro, Copy-SQLDatabase ir\u00e1 criar um login na inst\u00e2ncia de destino com permiss\u00f5es somente na base de destino. Assim, os scripts poder\u00e3o afetar somente a base de destino (a menos que, por exemplo, a permiss\u00e3o de CONTROL SERVER seja garantida para a role public). Ap\u00f3s a execu\u00e7\u00e3o, o login\/usu\u00e1rio que foram criados, ser\u00e3o deletados. O nome do login ser\u00e1 formado pela string \u201cCopySQLDatabaseLimited_\u201d mais o nome da base de destino. Apesar do script gerar a senha, o comando de CREATE LOGIN \u00e9 transferido a inst\u00e2ncia em texto claro e, portanto, pode estar sujeito a ataques. Vers\u00f5es futuras podem mudar a forma como este par\u00e2metro \u00e9 implementado.<\/p>\n<p>Caso o script executado retorne um RESULTSET, o mesmo ser\u00e1 exibido no error log, linha por linha, o que pode encher o log do cmdlet.\u00a0 As pr\u00f3ximas vers\u00f5es ir\u00e3o apresentar melhorias neste processo, permitindo maiores controles sobre os resultados gerados pelo script.<\/p>\n<p>Copy-SQLDatabase possui muitas funcionalidades que iriam estender mais ainda este post. Voc\u00ea pode usar o comando <strong>Get-Help Copy-SQLDatabase<\/strong> para obter mais ajuda sobre o cmdlet e seus par\u00e2metros. Atualmente, apenas o m\u00e9todo Backup\/Restore usando T-SQL \u00e9 suportado, com algumas varia\u00e7\u00f5es. Em vers\u00f5es futuras, suporte a ferramentas de backup de terceiros podem ser adicionadas, bem como outros m\u00e9todos de c\u00f3pia.\u00a0 Antes de finalizar, deixo mais alguns recursos interessantes de Copy-SQLDatabase:<\/p>\n<ul>\n<li>Logging<br \/>\nCopy-SQLDatabase tem uma poderosa engine de log que permite usar arquivos e at\u00e9 scripts powershell como destino das mensagens de log gerada. H\u00e1 um modo especial para uso com o SQL Server Agent, devido ao modo como ele lida com logs e resultados.<\/li>\n<li>Arquivos<br \/>\nApesar do cmdlet tentar escolher o melhor local dos arquivos, podem existir casos onde ele erre ou n\u00e3o tenha as permiss\u00f5es suficientes. Alguns par\u00e2metros permitem especificar manualmente o mapeamento dos arquivos, como se estivesse usando a cl\u00e1usula MOVE. Tamb\u00e9m, \u00e9 poss\u00edvel especificar filtrar volumes e ainda especificar quais volumes s\u00e3o permitidos para logs e para dados.<\/li>\n<li>Fonte do Backup<br \/>\nAl\u00e9m de permitir que o backup em tempo real\u00a0seja feito, o cmdlet permite usar um backup existente. Tamb\u00e9m, \u00e9 poss\u00edvel for\u00e7ar que a base de origem fiquem em READ_ONLY antes que o backup seja feito. Isto pode ser \u00fatil em migra\u00e7\u00f5es. Ainda, o cmdlet permite que um script T-SQL customizado de backup seja especificado.<\/li>\n<li>Destino<br \/>\nAl\u00e9m da execu\u00e7\u00e3o de scripts no destino, o cmdlet fornece op\u00e7\u00f5es como colocar a base em RECOVERY SIMPLE, ou mesmo fazer o backup da base de destino antes que a base seja sobrescrita<\/li>\n<\/ul>\n<p>Copy-SQLDatabase, que ainda est\u00e1 na vers\u00e3o BETA, realmente pode ser um grande ferramenta e poupar boa parte do seu trabalho, permitindo que voc\u00ea concentre seus esfor\u00e7os em outros projetos. O mesmo j\u00e1 foi usado, inclusive, para migrar um ambiente de produ\u00e7\u00e3o, onde a origem era uma inst\u00e2ncia standalone e o destino uma inst\u00e2ncia em cluster, utilizando\u00a0mounts points, o que refor\u00e7a o potencial. Alguns detalhes desta migra\u00e7\u00e3o podem ser conferidos <a href=\"http:\/\/thesqltimes.com\/blog\/2015\/11\/08\/apresentando-custommssql\/\">neste post<\/a>.<\/p>\n<p>O m\u00f3dulo <a href=\"https:\/\/github.com\/rrg92\/CustomMSSQL\" target=\"_blank\">CustomMSSQL est\u00e1 publicado no GitHub<\/a>, portanto voc\u00ea pode melhor\u00e1-lo ou mesmo reportar bugs. J\u00e1 existem alguns <a href=\"https:\/\/github.com\/rrg92\/CustomMSSQL\/issues\" target=\"_blank\">issues regitrados<\/a> e voc\u00ea pode acompanhar as corre\u00e7\u00f5es e melhorias implementadas, bem como fazer sugest\u00f5es.<\/p>\n<p>Haver\u00e3o outros posts falando sobre Copy-SQLDatabase e outros cmdlets do m\u00f3dulo CustomMSSQL. Mas voc\u00ea pode usar a documenta\u00e7\u00e3o do mesmo para aprender mais. At\u00e9 a pr\u00f3xima!<\/p>\n<p align=\"right\">[]\u2019s<br \/>\nRodrigo Ribeiro Gomes<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Copiar bases entre inst\u00e2ncias \u00e9 procedimento muito comum em qualquer ambiente SQL, na maioria dos casos para manter ambientes de homologa\u00e7\u00e3o, teste, desenvolvimento, etc. Esta tarefa pode ser muito simples, quando a base \u00e9 pequena e tem poucos arquivos, ou complicada, quando a base \u00e9 grande e h\u00e1 muitos arquivos envolvidos. A complexidade pode aumentar&hellip;&nbsp;<a href=\"https:\/\/thesqltimes.com\/blog\/2016\/04\/05\/copiando-bases-sql-server-com-copy-sqldatabase\/\" rel=\"bookmark\"><span class=\"screen-reader-text\">Copiando bases SQL Server com Copy-SQLDatabase<\/span><\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"om_disable_all_campaigns":false,"_exactmetrics_skip_tracking":false,"_exactmetrics_sitenote_active":false,"_exactmetrics_sitenote_note":"","_exactmetrics_sitenote_category":0,"_monsterinsights_skip_tracking":false,"_monsterinsights_sitenote_active":false,"_monsterinsights_sitenote_note":"","_monsterinsights_sitenote_category":0,"neve_meta_sidebar":"","neve_meta_container":"","neve_meta_enable_content_width":"","neve_meta_content_width":0,"neve_meta_title_alignment":"","neve_meta_author_avatar":"","neve_post_elements_order":"","neve_meta_disable_header":"","neve_meta_disable_footer":"","neve_meta_disable_title":"","footnotes":""},"categories":[3,187,11,7],"tags":[174,15,171,172,188,96,175,73],"series":[],"class_list":["post-832","post","type-post","status-publish","format-standard","hentry","category-banco-de-dados-2","category-custommssql","category-desenvolvimento","category-sql-server","tag-backup","tag-banco-de-dados","tag-copy-sqldatabase","tag-custommssql","tag-migracoes","tag-powershell","tag-restore","tag-sql-server"],"_links":{"self":[{"href":"https:\/\/thesqltimes.com\/blog\/wp-json\/wp\/v2\/posts\/832","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/thesqltimes.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/thesqltimes.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/thesqltimes.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/thesqltimes.com\/blog\/wp-json\/wp\/v2\/comments?post=832"}],"version-history":[{"count":21,"href":"https:\/\/thesqltimes.com\/blog\/wp-json\/wp\/v2\/posts\/832\/revisions"}],"predecessor-version":[{"id":855,"href":"https:\/\/thesqltimes.com\/blog\/wp-json\/wp\/v2\/posts\/832\/revisions\/855"}],"wp:attachment":[{"href":"https:\/\/thesqltimes.com\/blog\/wp-json\/wp\/v2\/media?parent=832"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/thesqltimes.com\/blog\/wp-json\/wp\/v2\/categories?post=832"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/thesqltimes.com\/blog\/wp-json\/wp\/v2\/tags?post=832"},{"taxonomy":"series","embeddable":true,"href":"https:\/\/thesqltimes.com\/blog\/wp-json\/wp\/v2\/series?post=832"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}