{"id":1522,"date":"2020-05-25T10:32:03","date_gmt":"2020-05-25T13:32:03","guid":{"rendered":"https:\/\/thesqltimes.com\/blog\/?p=1522"},"modified":"2020-05-25T10:32:03","modified_gmt":"2020-05-25T13:32:03","slug":"recuperando-dados-deletados-do-sql-server-sem-backup-full-parte-5","status":"publish","type":"post","link":"https:\/\/thesqltimes.com\/blog\/2020\/05\/25\/recuperando-dados-deletados-do-sql-server-sem-backup-full-parte-5\/","title":{"rendered":"Recuperando dados deletados do SQL Server sem Backup Full &#8211; Parte 5"},"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=\"1522\" 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 5\/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! Hoje, dando continuidade a nossa s\u00e9rie, vou responder a pergunta do \u00faltimo post: E se nos dados que estou recuperando tinham valores NULL?<\/p>\n<p>E a resposta \u00e9 muito simples: Isso est\u00e1 junto no meio desse bin\u00e1rio todo! Como voc\u00ea estudou sobre a anatomia de um registro , com os v\u00e1rios links que eu coloquei nos posts anteriores (n\u00e9?), ent\u00e3o aprendeu que existe um neg\u00f3cio chamado<strong> NULL bitmap.\u00a0<\/strong>Basicamente \u00e9 uma sequencia de bytes que indicam quais colunas tem o valor nulo ou n\u00e3o. Recorra aos links que coloquei anteriormente para entender com mais detalhes sobre ele.<\/p>\n<p>Sabendo isso, tudo que precisamos \u00e9 identificar o NULL bitmap no meio desse bin\u00e1rio todo, de cada registro, e checar se a coluna est\u00e1 nula ou n\u00e3o. Identificar o NULL bitmap envolve, al\u00e9m de conhecer a anatomia do registro, um pouco de m\u00e1gica matem\u00e1tica:<\/p>\n<p id=\"BNGVdHz\"><img loading=\"lazy\" decoding=\"async\" width=\"1311\" height=\"336\" class=\"size-full wp-image-1377 aligncenter\" src=\"http:\/\/thesqltimes.com\/blog\/wp-content\/uploads\/2020\/04\/img_5e8a6b03dbcb6.png\" alt=\"\" srcset=\"https:\/\/thesqltimes.com\/blog\/wp-content\/uploads\/2020\/04\/img_5e8a6b03dbcb6.png 1311w, https:\/\/thesqltimes.com\/blog\/wp-content\/uploads\/2020\/04\/img_5e8a6b03dbcb6-300x77.png 300w, https:\/\/thesqltimes.com\/blog\/wp-content\/uploads\/2020\/04\/img_5e8a6b03dbcb6-1024x262.png 1024w, https:\/\/thesqltimes.com\/blog\/wp-content\/uploads\/2020\/04\/img_5e8a6b03dbcb6-768x197.png 768w\" sizes=\"auto, (max-width: 1311px) 100vw, 1311px\" \/><\/p>\n<p>Este trecho envolve basicamente achar alguns metadados que existem no registro (<a href=\"https:\/\/www.sqlskills.com\/blogs\/paul\/inside-the-storage-engine-anatomy-of-a-record\/\">Veja o Post do Paul Randal<\/a>) para encontrar o resto das informa\u00e7\u00f5es. Primeiro, eu preciso encontrar os bytes que indicam a quantidade de colunas, por dois motivos:<\/p>\n<ol>\n<li>Achando a posi\u00e7\u00e3o deles eu sei onde come\u00e7a o NULL bitmap<\/li>\n<li>O tamanho do NULL bitmap, isto \u00e9, quantidade de bytes que formam ele, depende da quantidade de colunas<br \/>\nA cada 8 colunas, ele precisa de um byte adicional. Por exemplo, uma tabela com 8 colunas, precisa de um NULL bitmap com 1 byte (8 bits, 1 pra cada coluna). J\u00e1 com 10 colunas, ele vai gastar 2 bytes (16 bits, sendo apenas 10 usados). Com 16 colunas, continuamos com os 2 bytes (agora usando todos os 16 bits) e por a\u00ed vai&#8230;<\/li>\n<\/ol>\n[stextbox id=&#8217;info&#8217; caption=&#8217;Mas pera a\u00ed Rodrigo \u270b&#8217;]Se toda linha sempre tem a mesma quantidade de colunas, porque o SQL Server armazena esse dado para cada linha?<\/p>\n<p>Pois \u00e9&#8230; corre el\u00e1 nas fontes sobre a anatomia do registro e voc\u00ea entender os porqu\u00eas disso&#8230;[\/stextbox]\n<p>Por essas raz\u00f5es, eu precisei gerar a coluna chamada <em>ColCount,\u00a0<\/em>que me indica a quantidade de colunas. Ela come\u00e7a no byte 27 porque a \u00faltima coluna de tipo fixo \u00e9 a DataCadastro, que come\u00e7a no byte 19 e usa 8 bytes&#8230; Os bytes dela s\u00e3o do 19 ao 26&#8230; A quantidade de colunas sempre gasta 2 bytes (Neste caso, bytes 27 e 28).<\/p>\n<p>Na subquery acima, a coluna NullBmp cont\u00e9m todo o NULL bitmap! Novamente, estou usando a fun\u00e7\u00e3o SUBSTRING com a coluna <em>Registro<\/em> (<strong>[RowLog Contents 0] de fn_dump_dblog<\/strong>) para extrair os bytes que comp\u00f5e o NULL bitmap. Neste caso, o NULL bitmap come\u00e7a no byte 29. E Pronto (como tenho 7 colunas, preciso de apenas 1 byte)! Agora que tenho o NULL bitmap completo, \u00e9 so checar cada bit dele para saber se a respectiva coluna \u00e9 nulo ou n\u00e3o:<\/p>\n<ul>\n<li>O primeiro bit (o bit 0), indica se a primeira coluna \u00e9 ou n\u00e3o nula (neste caso sempre vai ser zero, pois \u00e9 a coluna Id, que \u00e9 chave prim\u00e1ria)<\/li>\n<li>O bit 1 controla a da segunda<\/li>\n<li>O bit 2 da coluna 3 e por a\u00ed vai.<\/li>\n<\/ul>\n<figure id=\"attachment_1531\" aria-describedby=\"caption-attachment-1531\" style=\"width: 1028px\" class=\"wp-caption aligncenter\"><a href=\"https:\/\/thesqltimes.com\/blog\/wp-content\/uploads\/2020\/05\/img_5ecabd7ef1fff.png\"><img loading=\"lazy\" decoding=\"async\" class=\"wp-image-1531 size-full\" src=\"https:\/\/thesqltimes.com\/blog\/wp-content\/uploads\/2020\/05\/img_5ecabd7ef1fff.png\" alt=\"Demonstra\u00e7\u00e3o do NULL bitmap\" width=\"1028\" height=\"452\" srcset=\"https:\/\/thesqltimes.com\/blog\/wp-content\/uploads\/2020\/05\/img_5ecabd7ef1fff.png 1028w, https:\/\/thesqltimes.com\/blog\/wp-content\/uploads\/2020\/05\/img_5ecabd7ef1fff-300x132.png 300w, https:\/\/thesqltimes.com\/blog\/wp-content\/uploads\/2020\/05\/img_5ecabd7ef1fff-1024x450.png 1024w, https:\/\/thesqltimes.com\/blog\/wp-content\/uploads\/2020\/05\/img_5ecabd7ef1fff-768x338.png 768w\" sizes=\"auto, (max-width: 1028px) 100vw, 1028px\" \/><\/a><figcaption id=\"caption-attachment-1531\" class=\"wp-caption-text\">No exemplo, o null bitmap possui o valor hexadecimal 48, e convertendo pra bin\u00e1rio \u00e9 0100 1000. Come\u00e7ando do bit mais a direita, conseguimos mapear o NULL de cada coluna!<\/figcaption><\/figure>\n<p>&nbsp;<\/p>\n<p>De posse do NULL bitmap, testar o valor de um bin\u00e1rio no SQL Server, em uma posi\u00e7\u00e3o espec\u00edfica, requer apenas um pouquinho de matem\u00e1tica (e <a href=\"https:\/\/stackoverflow.com\/questions\/50686959\/sql-check-for-bit-flag\/50686988#50686988\">talvez uma pesquisa no stack overflow<\/a>):<\/p>\n<figure id=\"attachment_1382\" aria-describedby=\"caption-attachment-1382\" style=\"width: 800px\" class=\"wp-caption aligncenter\"><a href=\"http:\/\/thesqltimes.com\/blog\/wp-content\/uploads\/2020\/04\/img_5e8b15c00ff5d.png\"><img loading=\"lazy\" decoding=\"async\" class=\"wp-image-1382 size-full\" src=\"http:\/\/thesqltimes.com\/blog\/wp-content\/uploads\/2020\/04\/img_5e8b15c00ff5d.png\" alt=\"\" width=\"800\" height=\"360\" srcset=\"https:\/\/thesqltimes.com\/blog\/wp-content\/uploads\/2020\/04\/img_5e8b15c00ff5d.png 800w, https:\/\/thesqltimes.com\/blog\/wp-content\/uploads\/2020\/04\/img_5e8b15c00ff5d-300x135.png 300w, https:\/\/thesqltimes.com\/blog\/wp-content\/uploads\/2020\/04\/img_5e8b15c00ff5d-768x346.png 768w\" sizes=\"auto, (max-width: 800px) 100vw, 800px\" \/><\/a><figcaption id=\"caption-attachment-1382\" class=\"wp-caption-text\"><em>Utilizando mais um pouco de matem\u00e1tica\u00a0eu consigo testar cada bit do NULL bitmap e montar uma lista de colunas indicando qual se a respectiva coluna era NULL ou n\u00e3o<\/em><\/figcaption><\/figure>\n<p>A l\u00f3gica \u00e9: eu crio uma coluna para cada uma das colunas das tabelas. Basicamente o valor dessa coluna \u00e9 o valor do bit respectivo no NULL bitmap: 1 indica que \u00e9 NULL, 0 \u00e9 o contr\u00e1rio. Eu resolvi colocar numa lista separada para que a express\u00e3o no SELECT principal n\u00e3o ficasse monstruosa. Ent\u00e3o, l\u00e1 no SELECT final, eu apenas fa\u00e7o uma condi\u00e7\u00e3o mais simples: Se o valor da respectiva coluna for 1, ent\u00e3o significa que ela \u00e9 NULL, ent\u00e3o retorno o valor NULL. Caso contr\u00e1rio, retorna o valor resultante daquela express\u00e3o que extrai o valor original:<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"1076\" height=\"84\" class=\"size-full wp-image-1376 aligncenter\" src=\"http:\/\/thesqltimes.com\/blog\/wp-content\/uploads\/2020\/04\/img_5e8a6ad42a215.png\" alt=\"\" srcset=\"https:\/\/thesqltimes.com\/blog\/wp-content\/uploads\/2020\/04\/img_5e8a6ad42a215.png 1076w, https:\/\/thesqltimes.com\/blog\/wp-content\/uploads\/2020\/04\/img_5e8a6ad42a215-300x23.png 300w, https:\/\/thesqltimes.com\/blog\/wp-content\/uploads\/2020\/04\/img_5e8a6ad42a215-1024x80.png 1024w, https:\/\/thesqltimes.com\/blog\/wp-content\/uploads\/2020\/04\/img_5e8a6ad42a215-768x60.png 768w\" sizes=\"auto, (max-width: 1076px) 100vw, 1076px\" \/>Para as colunas que s\u00e3o NOT NULL, eu nem me dou o trabalho de fazer essa verifica\u00e7\u00e3o, pois o bit sempre ser\u00e1 0, como, por exemplo, \u00e9 o caso da coluna Id.<\/p>\n<p>Com o NULL bitmap, al\u00e9m de determinar quais colunas tem o valor NULL, voc\u00ea consegue identificar onde iniciam as colunas de tipos vari\u00e1veis. E no pr\u00f3ximo, e \u00faltimo post, vamos entender como lidamos com essas colunas, que, na minha opini\u00e3o, s\u00e3o as mais chatas de recuperar!<\/p>\n<p>At\u00e9 l\u00e1!<\/p>\n","protected":false},"excerpt":{"rendered":"<div class=\"seriesmeta\">This entry is part 5 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! Hoje, dando continuidade a nossa s\u00e9rie, vou responder a pergunta do \u00faltimo post: E se nos dados que estou recuperando tinham valores NULL? E a resposta \u00e9 muito simples: Isso est\u00e1 junto no meio desse bin\u00e1rio todo! Como voc\u00ea estudou sobre a anatomia de um registro , com os v\u00e1rios links que eu coloquei&hellip;&nbsp;<a href=\"https:\/\/thesqltimes.com\/blog\/2020\/05\/25\/recuperando-dados-deletados-do-sql-server-sem-backup-full-parte-5\/\" rel=\"bookmark\"><span class=\"screen-reader-text\">Recuperando dados deletados do SQL Server sem Backup Full &#8211; Parte 5<\/span><\/a><\/p>\n","protected":false},"author":1,"featured_media":1531,"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,339,338,336,337,145,340,341,334,335,316,73,332],"series":[294],"class_list":["post-1522","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-administracao","category-internals","category-sql-server","tag-binary","tag-bitmap","tag-bits","tag-bytes","tag-header","tag-internals","tag-null","tag-nulos","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\/1522","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=1522"}],"version-history":[{"count":8,"href":"https:\/\/thesqltimes.com\/blog\/wp-json\/wp\/v2\/posts\/1522\/revisions"}],"predecessor-version":[{"id":1547,"href":"https:\/\/thesqltimes.com\/blog\/wp-json\/wp\/v2\/posts\/1522\/revisions\/1547"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/thesqltimes.com\/blog\/wp-json\/wp\/v2\/media\/1531"}],"wp:attachment":[{"href":"https:\/\/thesqltimes.com\/blog\/wp-json\/wp\/v2\/media?parent=1522"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/thesqltimes.com\/blog\/wp-json\/wp\/v2\/categories?post=1522"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/thesqltimes.com\/blog\/wp-json\/wp\/v2\/tags?post=1522"},{"taxonomy":"series","embeddable":true,"href":"https:\/\/thesqltimes.com\/blog\/wp-json\/wp\/v2\/series?post=1522"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}