{"id":1428,"date":"2020-04-27T09:30:07","date_gmt":"2020-04-27T12:30:07","guid":{"rendered":"https:\/\/thesqltimes.com\/blog\/?p=1428"},"modified":"2020-05-24T15:59:15","modified_gmt":"2020-05-24T18:59:15","slug":"recuperar-delete-sem-bakup-full-3","status":"publish","type":"post","link":"https:\/\/thesqltimes.com\/blog\/2020\/04\/27\/recuperar-delete-sem-bakup-full-3\/","title":{"rendered":"Recuperando dados deletados do SQL Server sem Backup Full &#8211; Parte 3"},"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=\"1428\" 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 3\/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\"> 3<\/span> <span class=\"rt-label rt-postfix\">minutos<\/span><\/span><p>Nos \u00faltimos dois posts desta s\u00e9rie eu mostrei uma situa\u00e7\u00e3o onde voc\u00ea deletou &#8220;sem querer&#8221; 10 mil registros de uma tabela e n\u00e3o tinha backup FULL para recuperar. Ent\u00e3o, eu te mostrei uma arma secreta chamada <strong>fn_dump_dblog<\/strong> e aparentemente, conseguimos recuperar essas linhas do BACKUP do log de transa\u00e7\u00e3o! Neste post, vou mostrar como extrair os dados a partir do que pegamos do backup de log. <strong>Aqui come\u00e7a a escova\u00e7\u00e3o de bit!<\/strong><\/p>\n<p>A coluna <strong>[RowLog Contents 0]<\/strong> (a qual eu inseri na tabela LogDelete com o nome <strong>Registro<\/strong>), no caso das opera\u00e7\u00f5es de DELETE, cont\u00e9m o registro que estava na p\u00e1gina quando ele foi deletado, por\u00e9m em formato bin\u00e1rio. De novo, se voc\u00ea aprendeu direitinho a anatomia de um registro, vai conseguir decodificar tranquilamente.<\/p>\n[stextbox id=&#8221;info&#8221;]Lembre-se: Este post mostra exemplos com registros sem compress\u00e3o! Caso haja compress\u00e3o, voc\u00ea deve compreender a estrutura de um registro comprimido que \u00e9 diferente![\/stextbox]\n<p>J\u00e1 que vamos decodificar um registro, a informa\u00e7\u00e3o crucial que precisamos aqui \u00e9 da estrutura da tabela, especialmente os tipos de dados de cada coluna, pois \u00e9 baseado nisso que as colunas s\u00e3o dispostas neste bin\u00e1rio todo! Voc\u00ea pode consultar a estrutura direto no seu banco de produ\u00e7\u00e3o e caso n\u00e3o tenha dispon\u00edvel (o que eu acho dif\u00edcil, pois estamos falando de um DELETE FROM), pode tentar um backup FULL mais antigo ou uma c\u00f3pia da tabela que voc\u00ea saiba que exista. O desafio \u00e9 achar qual era a bendita estrutura da tabela no momento do DELETE.<\/p>\n<p>Um jeito r\u00e1pido \u00e9 utilizar <strong>sp_help<\/strong>:<\/p>\n<figure id=\"attachment_1534\" aria-describedby=\"caption-attachment-1534\" style=\"width: 781px\" class=\"wp-caption aligncenter\"><a href=\"https:\/\/thesqltimes.com\/blog\/wp-content\/uploads\/2020\/05\/img_5ecac37647b65.png\"><img loading=\"lazy\" decoding=\"async\" class=\"wp-image-1534 size-full\" src=\"https:\/\/thesqltimes.com\/blog\/wp-content\/uploads\/2020\/05\/img_5ecac37647b65.png\" alt=\"Informa\u00e7\u00f5es das colunas relevantes ao processo de recupera\u00e7\u00e3o\" width=\"781\" height=\"373\" srcset=\"https:\/\/thesqltimes.com\/blog\/wp-content\/uploads\/2020\/05\/img_5ecac37647b65.png 781w, https:\/\/thesqltimes.com\/blog\/wp-content\/uploads\/2020\/05\/img_5ecac37647b65-300x143.png 300w, https:\/\/thesqltimes.com\/blog\/wp-content\/uploads\/2020\/05\/img_5ecac37647b65-768x367.png 768w\" sizes=\"auto, (max-width: 781px) 100vw, 781px\" \/><\/a><figcaption id=\"caption-attachment-1534\" class=\"wp-caption-text\">As informa\u00e7\u00f5es principais que preciso s\u00e3o estas dentro do quadro vermelho!<\/figcaption><\/figure>\n<ul>\n<li>O nome;<\/li>\n<li>Tipo de dados (e a quantidade de bytes usados);<\/li>\n<li>Se aceita nulo, ou n\u00e3o;<\/li>\n<li>O collation \u00e9 importante tamb\u00e9m, mas para este exemplo, vou omitir o uso dele, para deixarm mais simples;<\/li>\n<\/ul>\n<p>\u00c9 muito importante que consiga todas essas informa\u00e7\u00f5es, exatamente como eram no momento no DELETE, pois sem isso, seu trabalho vai ganhar um grau de dificuldade alt\u00edssimo e suas chances v\u00e3o cair bastante.<\/p>\n<p>Pronto! Agora temos tudo que precisamos para come\u00e7ar a remontar nossas colunas. No post anterior, mostrei um script que gera uma tabela chamada <em><strong>LogDeletes<\/strong><\/em>. Ela tem uma coluna chamada &#8220;Registro&#8221; que \u00e9 a nossa [RowLog Contents 0]. Cada uma dessas linhas equivalem \u00e0s linhas que foram deletadas, por isso o total \u00e9 exatamente dez mil.<\/p>\n<p><a href=\"https:\/\/thesqltimes.com\/blog\/wp-content\/uploads\/2020\/04\/img_5e93c91e4a349.png\"><img loading=\"lazy\" decoding=\"async\" class=\"alignnone wp-image-1437 size-full\" src=\"https:\/\/thesqltimes.com\/blog\/wp-content\/uploads\/2020\/04\/img_5e93c91e4a349.png\" alt=\"\" width=\"919\" height=\"427\" srcset=\"https:\/\/thesqltimes.com\/blog\/wp-content\/uploads\/2020\/04\/img_5e93c91e4a349.png 919w, https:\/\/thesqltimes.com\/blog\/wp-content\/uploads\/2020\/04\/img_5e93c91e4a349-300x139.png 300w, https:\/\/thesqltimes.com\/blog\/wp-content\/uploads\/2020\/04\/img_5e93c91e4a349-768x357.png 768w\" sizes=\"auto, (max-width: 919px) 100vw, 919px\" \/><\/a><\/p>\n<p>Esta coluna \u00e9 o nosso registro e voc\u00ea est\u00e1 olhando para os valores das colunas de algumas das linhas que foram deletadas, por\u00e9m na representa\u00e7\u00e3o bin\u00e1ria. A <strong>fn_dump_dblog<\/strong> me retorna na coluna [RowLog Contents 0] (coluna <strong><em>Registro<\/em> <\/strong>na tabela LogDeletes) um tipo varbinary . Por isso come\u00e7a com <strong>0x<\/strong>. Se tudo isso estivesse numa p\u00e1gina de dados, esses bin\u00e1rios estariam um seguido do outro.<\/p>\n<p>Voc\u00ea consegue escrever uma query que fa\u00e7a as extra\u00e7\u00f5es utilizando algumas fun\u00e7\u00f5es que trabalhem com dados bin\u00e1rios, como <a href=\"https:\/\/docs.microsoft.com\/en-us\/sql\/t-sql\/functions\/substring-transact-sql?view=sql-server-ver15\">SUBSTRING<\/a>, <a href=\"https:\/\/docs.microsoft.com\/en-us\/sql\/t-sql\/functions\/reverse-transact-sql?view=sql-server-ver15\">REVERSE<\/a> e algumas convers\u00f5es que s\u00e3o necess\u00e1rias, usando a fun\u00e7\u00e3o <a href=\"https:\/\/docs.microsoft.com\/en-us\/sql\/t-sql\/functions\/cast-and-convert-transact-sql?view=sql-server-ver15\">CONVERT<\/a>. <a href=\"https:\/\/github.com\/rrg92\/thesqltimes\/blob\/master\/recuperar-delete-sem-backup-full\/Recuperacao.sql\">O script abaixo est\u00e1 no git<\/a>.<\/p>\n<p id=\"yexFmxZ\"><a href=\"https:\/\/thesqltimes.com\/blog\/wp-content\/uploads\/2020\/04\/img_5e8a8e440405f.png\"><img loading=\"lazy\" decoding=\"async\" class=\"alignnone wp-image-1378 size-full\" src=\"https:\/\/thesqltimes.com\/blog\/wp-content\/uploads\/2020\/04\/img_5e8a8e440405f.png\" alt=\"\" width=\"1832\" height=\"836\" srcset=\"https:\/\/thesqltimes.com\/blog\/wp-content\/uploads\/2020\/04\/img_5e8a8e440405f.png 1832w, https:\/\/thesqltimes.com\/blog\/wp-content\/uploads\/2020\/04\/img_5e8a8e440405f-300x137.png 300w, https:\/\/thesqltimes.com\/blog\/wp-content\/uploads\/2020\/04\/img_5e8a8e440405f-1024x467.png 1024w, https:\/\/thesqltimes.com\/blog\/wp-content\/uploads\/2020\/04\/img_5e8a8e440405f-768x350.png 768w, https:\/\/thesqltimes.com\/blog\/wp-content\/uploads\/2020\/04\/img_5e8a8e440405f-1536x701.png 1536w\" sizes=\"auto, (max-width: 1832px) 100vw, 1832px\" \/><\/a><\/p>\n<p>E a\u00ed est\u00e3o as minhas 10 mil linhas de volta!<\/p>\n<p>No pr\u00f3ximo post eu vou explicar cada um desses trechos. Ent\u00e3o, se ainda n\u00e3o leu sobre <a href=\"https:\/\/www.sqlskills.com\/blogs\/paul\/inside-the-storage-engine-anatomy-of-a-record\/\">anatomia do registro<\/a>, ainda d\u00e1 tempo!<\/p>\n<p>At\u00e9 l\u00e1!<\/p>\n","protected":false},"excerpt":{"rendered":"<div class=\"seriesmeta\">This entry is part 3 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>Nos \u00faltimos dois posts desta s\u00e9rie eu mostrei uma situa\u00e7\u00e3o onde voc\u00ea deletou &#8220;sem querer&#8221; 10 mil registros de uma tabela e n\u00e3o tinha backup FULL para recuperar. Ent\u00e3o, eu te mostrei uma arma secreta chamada fn_dump_dblog e aparentemente, conseguimos recuperar essas linhas do BACKUP do log de transa\u00e7\u00e3o! Neste post, vou mostrar como extrair&hellip;&nbsp;<a href=\"https:\/\/thesqltimes.com\/blog\/2020\/04\/27\/recuperar-delete-sem-bakup-full-3\/\" rel=\"bookmark\"><span class=\"screen-reader-text\">Recuperando dados deletados do SQL Server sem Backup Full &#8211; Parte 3<\/span><\/a><\/p>\n","protected":false},"author":1,"featured_media":1437,"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":[8,296,7],"tags":[323,301,316,317,73],"series":[294],"class_list":["post-1428","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-administracao","category-internals","category-sql-server","tag-binary","tag-fn_dump_dblog","tag-recovery","tag-rowlog-contents-0","tag-sql-server","series-recuperando-dados-deletados-sem-backup-full"],"_links":{"self":[{"href":"https:\/\/thesqltimes.com\/blog\/wp-json\/wp\/v2\/posts\/1428","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=1428"}],"version-history":[{"count":12,"href":"https:\/\/thesqltimes.com\/blog\/wp-json\/wp\/v2\/posts\/1428\/revisions"}],"predecessor-version":[{"id":1535,"href":"https:\/\/thesqltimes.com\/blog\/wp-json\/wp\/v2\/posts\/1428\/revisions\/1535"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/thesqltimes.com\/blog\/wp-json\/wp\/v2\/media\/1437"}],"wp:attachment":[{"href":"https:\/\/thesqltimes.com\/blog\/wp-json\/wp\/v2\/media?parent=1428"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/thesqltimes.com\/blog\/wp-json\/wp\/v2\/categories?post=1428"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/thesqltimes.com\/blog\/wp-json\/wp\/v2\/tags?post=1428"},{"taxonomy":"series","embeddable":true,"href":"https:\/\/thesqltimes.com\/blog\/wp-json\/wp\/v2\/series?post=1428"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}