{"id":1352,"date":"2020-06-01T13:00:16","date_gmt":"2020-06-01T16:00:16","guid":{"rendered":"http:\/\/thesqltimes.com\/blog\/?p=1352"},"modified":"2020-06-01T13:54:40","modified_gmt":"2020-06-01T16:54:40","slug":"recuperando-dados-deletados-do-sql-server-sem-backup-full-parte-6","status":"publish","type":"post","link":"https:\/\/thesqltimes.com\/blog\/2020\/06\/01\/recuperando-dados-deletados-do-sql-server-sem-backup-full-parte-6\/","title":{"rendered":"Recuperando dados deletados do SQL Server sem Backup Full &#8211; Parte 6"},"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=\"1352\" 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><div class=\"seriesmeta\">Post 6\/6. Este post \u00e9 parte da s\u00e9rie: <a href=\"https:\/\/thesqltimes.com\/blog\/series\/recuperando-dados-deletados-sem-backup-full\/\" class=\"series-294\" title=\"Recuperando dados deletados sem Backup Full\">Recuperando dados deletados sem Backup Full<\/a>\r\n<\/div>\r\n<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\"> 5<\/span> <span class=\"rt-label rt-postfix\">minutos<\/span><\/span><p>Finalmente, chegamos ao \u00faltimo post da s\u00e9rie que tentou mostrar como voc\u00ea pode, em uma situa\u00e7\u00e3o de emerg\u00eancia, recuperar dados sem um Backup FULL, usando apenas um arquivo de backup de log, conhecimentos internals do SQL Server e, porque n\u00e3o, um pouco de sorte!<\/p>\n<p>Hoje finalizamos a s\u00e9rie explicando como conseguimos recuperar as colunas que cont\u00e9m tipo de dados vari\u00e1veis. No nosso caso, falta recuperar dados de colunas com o tipo <em>varchar<\/em>! Como voc\u00ea j\u00e1 deve saber, o varchar somente armazena o que de fato \u00e9 usado, ao contr\u00e1rio do <em>char<\/em>. Isso o torna um tipo de dados de tamanho vari\u00e1vel, e o valor que se especifica na defini\u00e7\u00e3o da coluna, \u00e9 o m\u00e1ximo de caracteres! No caso do varchar, o m\u00e1ximo de caracteres = m\u00e1ximo bytes!<\/p>\n<p>As colunas de tipo vari\u00e1vel que temos s\u00e3o:<\/p>\n<p id=\"rpZHRVC\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter wp-image-1536 size-full\" src=\"https:\/\/thesqltimes.com\/blog\/wp-content\/uploads\/2020\/05\/img_5ecac45114ebd.png\" alt=\"Colunas vari\u00e1veis: Nome e Sobrenome\" width=\"797\" height=\"362\" srcset=\"https:\/\/thesqltimes.com\/blog\/wp-content\/uploads\/2020\/05\/img_5ecac45114ebd.png 797w, https:\/\/thesqltimes.com\/blog\/wp-content\/uploads\/2020\/05\/img_5ecac45114ebd-300x136.png 300w, https:\/\/thesqltimes.com\/blog\/wp-content\/uploads\/2020\/05\/img_5ecac45114ebd-768x349.png 768w\" sizes=\"auto, (max-width: 797px) 100vw, 797px\" \/><\/p>\n<p>&nbsp;<\/p>\n<p>E como voc\u00ea deve ter aprendido nas fontes que eu coloquei durante toda a s\u00e9rie, para que seja poss\u00edvel armazenar estes tipos de dados, o SQL Server guarda alguns metadados a mais:<\/p>\n<ul>\n<li>Quantidade de colunas vari\u00e1veis (2 bytes. Chamarei aqui de <strong>VarCount<\/strong>, igual no script)<br \/>\nCome\u00e7a logo ap\u00f3s o NULL bitmap. No nosso caso, NULL bitmap ocupa 1 byte (byte 29), logo o VarCount come\u00e7a no byte 30 e se estende ao byte 31.<br \/>\nEsta quantidade n\u00e3o \u00e9 a quantidade de colunas vari\u00e1veis que existem na tabela, e sim, a quantidade presente no respectivo registro. Por exemplo, se todas as colunas vari\u00e1veis tem algum dado, ent\u00e3o a quantidade ser\u00e1 2. Caso uma delas seja nulo, ent\u00e3o o valor desse metadado ser\u00e1 1.<\/li>\n<li>Offset do \u00faltimo byte de cada coluna<br \/>\nCome\u00e7a logo ap\u00f3s o VarCount, e o tamanho depende do n\u00famero de colunas vari\u00e1veis. Ele gasta 2 bytes pra cada coluna vari\u00e1vel. No nosso caso o tamanho total s\u00e3o 4 bytes (2 colunas), que compreende os bytes 32 ao 35.<\/li>\n<\/ul>\n<figure id=\"attachment_1538\" aria-describedby=\"caption-attachment-1538\" style=\"width: 1114px\" class=\"wp-caption aligncenter\"><a href=\"https:\/\/thesqltimes.com\/blog\/wp-content\/uploads\/2020\/05\/img_5ecad5d0847d6.png\"><img loading=\"lazy\" decoding=\"async\" class=\"wp-image-1538 size-full\" src=\"https:\/\/thesqltimes.com\/blog\/wp-content\/uploads\/2020\/05\/img_5ecad5d0847d6.png\" alt=\"Disposi\u00e7\u00e3o dos bytes envolvidos nas colunas de tipo vari\u00e1vel\" width=\"1114\" height=\"516\" srcset=\"https:\/\/thesqltimes.com\/blog\/wp-content\/uploads\/2020\/05\/img_5ecad5d0847d6.png 1114w, https:\/\/thesqltimes.com\/blog\/wp-content\/uploads\/2020\/05\/img_5ecad5d0847d6-300x139.png 300w, https:\/\/thesqltimes.com\/blog\/wp-content\/uploads\/2020\/05\/img_5ecad5d0847d6-1024x474.png 1024w, https:\/\/thesqltimes.com\/blog\/wp-content\/uploads\/2020\/05\/img_5ecad5d0847d6-768x356.png 768w\" sizes=\"auto, (max-width: 1114px) 100vw, 1114px\" \/><\/a><figcaption id=\"caption-attachment-1538\" class=\"wp-caption-text\">Perceba como os metadados envolvidos ajudam a mapear o fim de cada coluna vari\u00e1vel. Sabendo o fim de cada coluna vari\u00e1vel, conseguimos identificar o in\u00edcio da pr\u00f3xima.<\/figcaption><\/figure>\n<p>E ent\u00e3o, ap\u00f3s todos esses bytes apenas para metadados, temos, finalmente, os bytes com os dados que est\u00e3o armazenados nas colunas, um ap\u00f3s o outro (assim como no tipo fixo). No exemplo da imagem acima, a coluna Nome come\u00e7a em 36 e va at\u00e9 43, totalizando 8 bytes, e, como se trata de um varchar, s\u00e3o 8 caracteres. Por conta que o collation dessa coluna \u00e9 do code page 1252 (Latin1), basta usar a tabela ASCII e converter cada hexadecimal acima que voc\u00ea ver\u00e1 o texto final. N\u00f3s fazemos isso direto no script em o uso do collation para simplificar, como eu expliquei anteriormente na parte 3.<\/p>\n<p>Voltando ao script, o VarCount \u00e9 extra\u00eddo dessa maneira:<\/p>\n<figure id=\"attachment_1539\" aria-describedby=\"caption-attachment-1539\" style=\"width: 1236px\" class=\"wp-caption aligncenter\"><img loading=\"lazy\" decoding=\"async\" class=\"wp-image-1539 size-full\" src=\"https:\/\/thesqltimes.com\/blog\/wp-content\/uploads\/2020\/05\/img_5ecadaa1f0d43.png\" alt=\"\" width=\"1236\" height=\"360\" srcset=\"https:\/\/thesqltimes.com\/blog\/wp-content\/uploads\/2020\/05\/img_5ecadaa1f0d43.png 1236w, https:\/\/thesqltimes.com\/blog\/wp-content\/uploads\/2020\/05\/img_5ecadaa1f0d43-300x87.png 300w, https:\/\/thesqltimes.com\/blog\/wp-content\/uploads\/2020\/05\/img_5ecadaa1f0d43-1024x298.png 1024w, https:\/\/thesqltimes.com\/blog\/wp-content\/uploads\/2020\/05\/img_5ecadaa1f0d43-768x224.png 768w\" sizes=\"auto, (max-width: 1236px) 100vw, 1236px\" \/><figcaption id=\"caption-attachment-1539\" class=\"wp-caption-text\">Uma vez que Null bitmap usa somente o byte 29, \u00e9 no byte 30 que se inicia o VarCount. E ele sempre usa 2 bytes.<\/figcaption><\/figure>\n<p>No \u00faltimo subselect, eu defino estas tr\u00eas colunas:<\/p>\n<p id=\"SWQiBMN\"><img loading=\"lazy\" decoding=\"async\" width=\"1240\" height=\"222\" class=\"size-full wp-image-1540 aligncenter\" src=\"https:\/\/thesqltimes.com\/blog\/wp-content\/uploads\/2020\/05\/img_5ecaddcc8338d.png\" alt=\"\" srcset=\"https:\/\/thesqltimes.com\/blog\/wp-content\/uploads\/2020\/05\/img_5ecaddcc8338d.png 1240w, https:\/\/thesqltimes.com\/blog\/wp-content\/uploads\/2020\/05\/img_5ecaddcc8338d-300x54.png 300w, https:\/\/thesqltimes.com\/blog\/wp-content\/uploads\/2020\/05\/img_5ecaddcc8338d-1024x183.png 1024w, https:\/\/thesqltimes.com\/blog\/wp-content\/uploads\/2020\/05\/img_5ecaddcc8338d-768x137.png 768w\" sizes=\"auto, (max-width: 1240px) 100vw, 1240px\" \/><\/p>\n<p>A coluna VarStart \u00e9 usada para calcular onde come\u00e7a a primeira coluna de tipo vari\u00e1vel. No byte 32 inicia o offset das colunas e, como h\u00e1 2 bytes para cada coluna vari\u00e1vel, eu tenho que pular estes offset. Por isso\u00a0 VarStart \u00e9 essa soma com essa multiplica\u00e7\u00e3o. As colunas Col1Off e Col2Off representam os offsets do fim cada uma das respectivas colunas Nome e Sobrenome.<\/p>\n<p id=\"ZYvJyWl\"><img loading=\"lazy\" decoding=\"async\" width=\"1521\" height=\"154\" class=\"alignnone size-full wp-image-1380 \" src=\"http:\/\/thesqltimes.com\/blog\/wp-content\/uploads\/2020\/04\/img_5e8b13a652ba5.png\" alt=\"\" srcset=\"https:\/\/thesqltimes.com\/blog\/wp-content\/uploads\/2020\/04\/img_5e8b13a652ba5.png 1521w, https:\/\/thesqltimes.com\/blog\/wp-content\/uploads\/2020\/04\/img_5e8b13a652ba5-300x30.png 300w, https:\/\/thesqltimes.com\/blog\/wp-content\/uploads\/2020\/04\/img_5e8b13a652ba5-1024x104.png 1024w, https:\/\/thesqltimes.com\/blog\/wp-content\/uploads\/2020\/04\/img_5e8b13a652ba5-768x78.png 768w\" sizes=\"auto, (max-width: 1521px) 100vw, 1521px\" \/><\/p>\n<p id=\"rdtcPMx\">Para cada uma das colunas nos usamos o offset para calcular a quantidade de bytes que queremos extrair e o in\u00edcio dentro do registro bin\u00e1rio completo (coluna Registro). Uma coluna sempre vai iniciar 1 byte ap\u00f3s o offset da anterior, por isso Utilizamos o Col1Off+1 para Sobrenome. A primeira coluna de tipo vari\u00e1vel come\u00e7a com o primeiro byte das colunas de tipo vari\u00e1vel. Por isso usamos &#8220;VarStart&#8221; em Nome. Como a coluna Nome \u00e9 NOT NULL, eu nem me dou o trabalho de checar se ela tem o bit no NULL bitmap definido, mesmo que exista um bit l\u00e1. J\u00e1 a coluna Sobrenome, pode aceitar nulos, e por isso, fazemos a verifica\u00e7\u00e3o que ensinei no post anterior.<\/p>\n<p>E com isso, encerramos o nosso script! Voc\u00ea agora consegue aplicar todos estes conceitos pra sua realidade, se precisar!<\/p>\n<h2>Procedure<\/h2>\n<p>E a\u00ed, o qu\u00ea voc\u00ea\u00a0 achou dos procedimentos que fizemos? Se voc\u00ea achou algo \u00fatil, por\u00e9m muito complexo, ent\u00e3o, tem uma maneira um pouco mais &#8220;automatizada&#8221;. O <a title=\"Posts by Muhammad Imran\" href=\"https:\/\/raresql.com\/author\/mimran18\/\">Muhammad Imran<\/a> simplesmente criou uma procedure que faz tudo isso! Genial, n\u00e3o!? Eu j\u00e1 fiz o download e meus ajustes e \u00e9 ela que eu utilizo quando preciso, porque j\u00e1 faz tudo isso a\u00ed e muito mais! Confere <a href=\"https:\/\/raresql.com\/2011\/10\/22\/how-to-recover-deleted-data-from-sql-sever\/\">l\u00e1 no post dele<\/a> que \u00e9 de 2011&#8230;<\/p>\n<p>Mesmo assim, voc\u00ea precisa saber o que est\u00e1 envolvido por detr\u00e1s de tudo isso! Eu poderia simplesmente ter come\u00e7ado esta s\u00e9rie com esta procedure e facilitaria sua vida. Mas eu estaria expondo um m\u00e9todo perigoso sem apresentar e mostrar alguns dos riscos! Em suma: Utilize o m\u00e9todo manual, ou pela procedure, somente se voc\u00ea tem pleno controle e conhecimento de tudo que est\u00e1 envolvido e dos efeitos que podem ser causados!<\/p>\n<p>Ainda assim, tudo isso \u00e9 algo que voc\u00ea pode deixar ai na sua caixa de ferramentas e usar no momento adequado!<\/p>\n<hr \/>\n<p>Ent\u00e3o, depois de toda essa viagem, mostrei que, apesar de muito trabalho, \u00e9 poss\u00edvel sim recuperar registros sem um backup FULL. Conforme eu disse no in\u00edcio da s\u00e9rie, \u00e9 uma carta na manga que voc\u00ea pode ter e pode te ajudar em uma situa\u00e7\u00e3o mais cr\u00edtica! Isto \u00e9, nada aqui substitui o bom e velho Backup FULL, mas, se ele vier a falta, tudo isso pode te trazer algumas esperan\u00e7a. E digo mais: utilizando tudo isso que voc\u00ea viu aqui, eu j\u00e1 consegui recuperar registros sem backup nenhum! Um dia eu conto essa hist\u00f3ria&#8230;<\/p>\n<p>E, se voc\u00ea tiver um cen\u00e1rio mais complexo, chama a <a href=\"https:\/\/powertuning.com.br\/\">Power Tuning<\/a> e deixa que a gente analisar a melhor sa\u00edda, seja com essa, ou muitas outras t\u00e9cnicas!<\/p>\n<p>At\u00e9 a pr\u00f3xima!<\/p>\n<p>&nbsp;<\/p>\n<p>&nbsp;<\/p>\n<p>&nbsp;<\/p>\n<p>&nbsp;<\/p>\n","protected":false},"excerpt":{"rendered":"<div class=\"seriesmeta\">This entry is part 6 of 6 in the series <a href=\"https:\/\/thesqltimes.com\/blog\/series\/recuperando-dados-deletados-sem-backup-full\/\" class=\"series-294\" title=\"Recuperando dados deletados sem Backup Full\">Recuperando dados deletados sem Backup Full<\/a><\/div><p>Finalmente, chegamos ao \u00faltimo post da s\u00e9rie que tentou mostrar como voc\u00ea pode, em uma situa\u00e7\u00e3o de emerg\u00eancia, recuperar dados sem um Backup FULL, usando apenas um arquivo de backup de log, conhecimentos internals do SQL Server e, porque n\u00e3o, um pouco de sorte! Hoje finalizamos a s\u00e9rie explicando como conseguimos recuperar as colunas que&hellip;&nbsp;<a href=\"https:\/\/thesqltimes.com\/blog\/2020\/06\/01\/recuperando-dados-deletados-do-sql-server-sem-backup-full-parte-6\/\" rel=\"bookmark\"><span class=\"screen-reader-text\">Recuperando dados deletados do SQL Server sem Backup Full &#8211; Parte 6<\/span><\/a><\/p>\n","protected":false},"author":1,"featured_media":1538,"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":[1],"tags":[297,301,145,298,73,342],"series":[294],"class_list":["post-1352","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-uncategorized","tag-data-recovery","tag-fn_dump_dblog","tag-internals","tag-recuperacao","tag-sql-server","tag-varchar","series-recuperando-dados-deletados-sem-backup-full"],"_links":{"self":[{"href":"https:\/\/thesqltimes.com\/blog\/wp-json\/wp\/v2\/posts\/1352","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=1352"}],"version-history":[{"count":18,"href":"https:\/\/thesqltimes.com\/blog\/wp-json\/wp\/v2\/posts\/1352\/revisions"}],"predecessor-version":[{"id":1549,"href":"https:\/\/thesqltimes.com\/blog\/wp-json\/wp\/v2\/posts\/1352\/revisions\/1549"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/thesqltimes.com\/blog\/wp-json\/wp\/v2\/media\/1538"}],"wp:attachment":[{"href":"https:\/\/thesqltimes.com\/blog\/wp-json\/wp\/v2\/media?parent=1352"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/thesqltimes.com\/blog\/wp-json\/wp\/v2\/categories?post=1352"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/thesqltimes.com\/blog\/wp-json\/wp\/v2\/tags?post=1352"},{"taxonomy":"series","embeddable":true,"href":"https:\/\/thesqltimes.com\/blog\/wp-json\/wp\/v2\/series?post=1352"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}