{"id":1497,"date":"2020-05-21T23:57:54","date_gmt":"2020-05-22T02:57:54","guid":{"rendered":"https:\/\/thesqltimes.com\/blog\/?p=1497"},"modified":"2020-05-21T23:57:54","modified_gmt":"2020-05-22T02:57:54","slug":"copia-recuperando-dados-deletados-do-sql-server-sem-backup-full-parte-4","status":"publish","type":"post","link":"https:\/\/thesqltimes.com\/blog\/2020\/05\/21\/copia-recuperando-dados-deletados-do-sql-server-sem-backup-full-parte-4\/","title":{"rendered":"Recuperando dados deletados do SQL Server sem Backup Full &#8211; Parte 4"},"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=\"1497\" 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 4\/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\"> 4<\/span> <span class=\"rt-label rt-postfix\">minutos<\/span><\/span><p>Ol\u00e1! Finalmente, chegamos a quarta parte da nossa s\u00e9rie sobre como recuperar registros deletados sem um Backup FULL. O resumo \u00e9 o seguinte: Por alguma raz\u00e3o, voc\u00ea deletou alguns registros de sua tabela e a \u00fanica coisa que tem \u00e9 o backup de log do hor\u00e1rio em que foi deletado (por exemplo, voc\u00ea perdeu o backup full pela mesma magia negra que fez voc\u00ea deletar registros incorretamente). Ent\u00e3o, at\u00e9 o post anterior, eu mostrei como \u00e9 poss\u00edvel recuperar seus dados somente com este backup de log e um pouco de conhecimento de como o SQL Server armazena os dados. Hoje, eu vou explicar melhor trechos do script que apresentei no post anterior.<\/p>\n<p>\u00c9 claro que tudo que eu mostrei at\u00e9 aqui n\u00e3o \u00e9 algo que voc\u00ea deve incluir em seu planejamento. Sua preocupa\u00e7\u00e3o sempre deve ser o backup (e ter o backup). O que voc\u00ea viu at\u00e9 agora \u00e9 apenas uma carta na manga e nem sempre vai dar certo por uma s\u00e9rie de raz\u00f5es como, por exemplo, se h\u00e1 compress\u00e3o ou n\u00e3o, se voc\u00ea sabe ou n\u00e3o a estrutura, tipos de dados, desempenho, etc&#8230; Ent\u00e3o, sabendo disso, continuemos a brincadeira&#8230;<\/p>\n<p><a href=\"https:\/\/github.com\/rrg92\/thesqltimes\/blob\/master\/recuperar-delete-sem-backup-full\/Recuperacao.sql\">O script que apresentei no post anterior<\/a> usa a nossa tabela <strong>LogDeletes<\/strong>. Cada linha tem uma coluna chamada <strong>Registro<\/strong> que \u00e9 a nossa <strong>[RowLog Contents 0]<\/strong>. Ela \u00e9 exatamente o registro bin\u00e1rio, e por isso come\u00e7a com 0x, uma vez que a <strong>fn_dump_dblog<\/strong> me retorna como um <em>varbinary<\/em>. Quando estes registros estavam nas p\u00e1gina de dados, esses bin\u00e1rios estavam um seguido do outro. (Isso mesmo: \u00e9 aquilo que voc\u00ea v\u00ea quando usa um <a href=\"https:\/\/techcommunity.microsoft.com\/t5\/sql-server\/how-to-use-dbcc-page\/ba-p\/383094\">DBCC PAGE<\/a>). Aqui est\u00e3o as primeiras linhas para refrescar sua mem\u00f3ria:<\/p>\n<p><a href=\"https:\/\/thesqltimes.com\/blog\/wp-content\/uploads\/2020\/04\/img_5e93c91e4a349.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter\" src=\"https:\/\/thesqltimes.com\/blog\/wp-content\/uploads\/2020\/04\/img_5e93c91e4a349.png\" alt=\"\" width=\"919\" height=\"427\" \/><\/a><\/p>\n<p>Se voc\u00ea fez o dever de casa e estudou sobre a anatomia do registro, ent\u00e3o descobriu que os primeiros 4 bytes do registro bin\u00e1rio s\u00e3o reservados para o header dele. Cada registro tem o seu e ele define algumas informa\u00e7\u00f5es que n\u00e3o s\u00e3o relevantes para este post. Isto \u00e9, no quinto byte come\u00e7a a primeira coluna de tipo fixo! No nosso caso, como descobrimos no post anterior, as colunas de tipo fixo, e o seus tamanhos s\u00e3o:<\/p>\n<ul>\n<li>Id, 4 bytes<\/li>\n<li>Idade, 1 byte<\/li>\n<li>Tipo, 1 byte<\/li>\n<li>DataCadastro, 8 bytes<\/li>\n<li>Codigo, 8 bytes<\/li>\n<\/ul>\n<p>Os valores das colunas s\u00e3o &#8220;grudados&#8221; um no outro. Vem a primeira coluna e seus bytes&#8230; A segunda coluna sempre come\u00e7a imediatamente ap\u00f3s a primeira, sem nada entre elas, e por a\u00ed vai at\u00e9 a \u00faltima coluna de tipo fixo. Aqui \u00e9 o tamanho do tipo de dados quem manda. Por exemplo, a coluna Id come\u00e7a no byte 5 e vai at\u00e9 o byte 8 (totalizando os 4 bytes, j\u00e1 que \u00e9 um int). J\u00e1 a coluna Idade, come\u00e7a no byte 9, gastando apenas 1 \u00fanico byte ( o pr\u00f3prio byte 9 somente). E ent\u00e3o chegamos a coluna Tipo, e por a\u00ed vai:<\/p>\n<figure id=\"attachment_1374\" aria-describedby=\"caption-attachment-1374\" style=\"width: 800px\" class=\"wp-caption aligncenter\"><a href=\"http:\/\/thesqltimes.com\/blog\/wp-content\/uploads\/2020\/04\/img_5e8a673e54c75.png\"><img loading=\"lazy\" decoding=\"async\" class=\"wp-image-1374 size-full\" src=\"http:\/\/thesqltimes.com\/blog\/wp-content\/uploads\/2020\/04\/img_5e8a673e54c75.png\" alt=\"Pequena demonstra\u00e7\u00e3o da disposi\u00e7\u00e3o do registro\" width=\"800\" height=\"333\" srcset=\"https:\/\/thesqltimes.com\/blog\/wp-content\/uploads\/2020\/04\/img_5e8a673e54c75.png 800w, https:\/\/thesqltimes.com\/blog\/wp-content\/uploads\/2020\/04\/img_5e8a673e54c75-300x125.png 300w, https:\/\/thesqltimes.com\/blog\/wp-content\/uploads\/2020\/04\/img_5e8a673e54c75-768x320.png 768w\" sizes=\"auto, (max-width: 800px) 100vw, 800px\" \/><\/a><figcaption id=\"caption-attachment-1374\" class=\"wp-caption-text\">Disposi\u00e7\u00e3o dos bytes no registro (apenas os primeiros 10 bytes )<\/figcaption><\/figure>\n<p>&nbsp;<\/p>\n<p>Esta \u00e9 a raz\u00e3o pela qual eu uso a fun\u00e7\u00e3o <a href=\"https:\/\/docs.microsoft.com\/en-us\/sql\/t-sql\/functions\/substring-transact-sql?view=sql-server-ver15\">SUBSTRING<\/a> na coluna Registro:<\/p>\n<p>&nbsp;<\/p>\n<p id=\"JdzXoFG\"><a href=\"https:\/\/thesqltimes.com\/blog\/wp-content\/uploads\/2020\/05\/img_5ec7368d2270c.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter wp-image-1517 size-full\" src=\"https:\/\/thesqltimes.com\/blog\/wp-content\/uploads\/2020\/05\/img_5ec7368d2270c.png\" alt=\"\" width=\"1442\" height=\"728\" srcset=\"https:\/\/thesqltimes.com\/blog\/wp-content\/uploads\/2020\/05\/img_5ec7368d2270c.png 1442w, https:\/\/thesqltimes.com\/blog\/wp-content\/uploads\/2020\/05\/img_5ec7368d2270c-300x151.png 300w, https:\/\/thesqltimes.com\/blog\/wp-content\/uploads\/2020\/05\/img_5ec7368d2270c-1024x517.png 1024w, https:\/\/thesqltimes.com\/blog\/wp-content\/uploads\/2020\/05\/img_5ec7368d2270c-768x388.png 768w\" sizes=\"auto, (max-width: 1442px) 100vw, 1442px\" \/><\/a><\/p>\n<p>Gra\u00e7as a esta fun\u00e7\u00e3o, que tamb\u00e9m consegue operar em tipos bin\u00e1rios, eu consigo extrair exatamente os bytes que eu preciso, assim como se eu estivesse extraindo caracteres de uma string. Precisando apenas saber onde os bytes come\u00e7am e terminam&#8230; E como vimos acima, temos esta informa\u00e7\u00e3o gra\u00e7as aos tipos de dados. Ora, se a primeira coluna sempre come\u00e7a no byte 5 e tenho os tipos de dados de cada uma delas, eu consigo determinar onde termina uma coluna e onde come\u00e7a a pr\u00f3xima.<\/p>\n<p>Note que em alguns pontos eu utilizo a fun\u00e7\u00e3o REVERSE e converto pra um tipo binary do mesmo tamanho do tipo de dados original da coluna, para s\u00f3 ent\u00e3o converter pro original. Isso \u00e9 devido a representa\u00e7\u00e3o e sequ\u00eancia de bytes. Meu processador \u00e9 little-endian (assim como a maioria dos processadores intel) e, por isso, o byte menos significativo vem primeiro. Ent\u00e3o, antes de converter para o valor final, eu preciso adequar! Isto \u00e9 necess\u00e1rio somente quando eu tenho um tipo de dados que usa mais de um byte para represent\u00e1-lo, como n\u00fameros e datas. Aqui est\u00e1 toda essa l\u00f3gica aplicada na coluna Id, que \u00e9 a primeira e come\u00e7a no byte 5;<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"1103\" height=\"597\" class=\"size-full wp-image-1379  aligncenter\" src=\"http:\/\/thesqltimes.com\/blog\/wp-content\/uploads\/2020\/04\/img_5e8b11fe17d7e.png\" alt=\"\" srcset=\"https:\/\/thesqltimes.com\/blog\/wp-content\/uploads\/2020\/04\/img_5e8b11fe17d7e.png 1103w, https:\/\/thesqltimes.com\/blog\/wp-content\/uploads\/2020\/04\/img_5e8b11fe17d7e-300x162.png 300w, https:\/\/thesqltimes.com\/blog\/wp-content\/uploads\/2020\/04\/img_5e8b11fe17d7e-1024x554.png 1024w, https:\/\/thesqltimes.com\/blog\/wp-content\/uploads\/2020\/04\/img_5e8b11fe17d7e-768x416.png 768w\" sizes=\"auto, (max-width: 1103px) 100vw, 1103px\" \/><\/p>\n<p>No final o que eu fa\u00e7o \u00e9 converter o bin\u00e1rio para o tipo de dados original. E tcharam: A m\u00e1gica acontece:<\/p>\n<p id=\"AqLvXYd\"><img loading=\"lazy\" decoding=\"async\" width=\"435\" height=\"278\" class=\"size-full wp-image-1518  aligncenter\" src=\"https:\/\/thesqltimes.com\/blog\/wp-content\/uploads\/2020\/05\/img_5ec738801b040.png\" alt=\"\" srcset=\"https:\/\/thesqltimes.com\/blog\/wp-content\/uploads\/2020\/05\/img_5ec738801b040.png 435w, https:\/\/thesqltimes.com\/blog\/wp-content\/uploads\/2020\/05\/img_5ec738801b040-300x192.png 300w\" sizes=\"auto, (max-width: 435px) 100vw, 435px\" \/><\/p>\n<p>&nbsp;<\/p>\n<p>Ok. E se a coluna tiver nulos? Como eu sei que o dado que estava ali era um NULL? E as outras colunas varchar? N\u00e3o perca os pr\u00f3ximos posts!<\/p>\n","protected":false},"excerpt":{"rendered":"<div class=\"seriesmeta\">This entry is part 4 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>Ol\u00e1! Finalmente, chegamos a quarta parte da nossa s\u00e9rie sobre como recuperar registros deletados sem um Backup FULL. O resumo \u00e9 o seguinte: Por alguma raz\u00e3o, voc\u00ea deletou alguns registros de sua tabela e a \u00fanica coisa que tem \u00e9 o backup de log do hor\u00e1rio em que foi deletado (por exemplo, voc\u00ea perdeu o&hellip;&nbsp;<a href=\"https:\/\/thesqltimes.com\/blog\/2020\/05\/21\/copia-recuperando-dados-deletados-do-sql-server-sem-backup-full-parte-4\/\" rel=\"bookmark\"><span class=\"screen-reader-text\">Recuperando dados deletados do SQL Server sem Backup Full &#8211; Parte 4<\/span><\/a><\/p>\n","protected":false},"author":1,"featured_media":1374,"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,336,337,145,333,334,335,316,73,332],"series":[294],"class_list":["post-1497","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-administracao","category-internals","category-sql-server","tag-binary","tag-bytes","tag-header","tag-internals","tag-log-record","tag-page","tag-pagina-de-dados","tag-recovery","tag-sql-server","tag-substring","series-recuperando-dados-deletados-sem-backup-full"],"_links":{"self":[{"href":"https:\/\/thesqltimes.com\/blog\/wp-json\/wp\/v2\/posts\/1497","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=1497"}],"version-history":[{"count":6,"href":"https:\/\/thesqltimes.com\/blog\/wp-json\/wp\/v2\/posts\/1497\/revisions"}],"predecessor-version":[{"id":1521,"href":"https:\/\/thesqltimes.com\/blog\/wp-json\/wp\/v2\/posts\/1497\/revisions\/1521"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/thesqltimes.com\/blog\/wp-json\/wp\/v2\/media\/1374"}],"wp:attachment":[{"href":"https:\/\/thesqltimes.com\/blog\/wp-json\/wp\/v2\/media?parent=1497"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/thesqltimes.com\/blog\/wp-json\/wp\/v2\/categories?post=1497"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/thesqltimes.com\/blog\/wp-json\/wp\/v2\/tags?post=1497"},{"taxonomy":"series","embeddable":true,"href":"https:\/\/thesqltimes.com\/blog\/wp-json\/wp\/v2\/series?post=1497"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}