{"id":226,"date":"2016-05-25T08:22:09","date_gmt":"2016-05-25T07:22:09","guid":{"rendered":"https:\/\/sqldoubleg.live-website.com\/?p=226"},"modified":"2016-05-25T08:28:46","modified_gmt":"2016-05-25T07:28:46","slug":"truncate-table-misconceptions","status":"publish","type":"post","link":"https:\/\/www.sqldoubleg.com\/es\/2016\/05\/25\/truncate-table-misconceptions\/","title":{"rendered":"TRUNCATE TABLE misconceptions"},"content":{"rendered":"<p>There is a number misconceptions around TRUNCATE TABLE which have been around for too long that people take them as the real truth, let&#8217;s debunk some of those&nbsp;TRUNCATE TABLE in SQL Server is well known for what it does as feature, but how it actually works is easily misunderstood, and for long time, the same wrong information goes on and on. <\/p>\n<p>Today&#8217;s post wants to show you some of those misconceptions and why they are untrue using demos anyone can go reproduce.<\/p>\n<p>&nbsp;<br \/>\n<strong>TRUNCATE TABLE <em>cannot be ROLLBACK<\/em><\/strong><\/p>\n<p>I chose this one first because it&#8217;s just so easy to prove that I&#8217;m surprised people still repeat it.<\/p>\n<p>Let&#8217;s go see  how that is not true<\/p>\n<pre class=\"brush: tsql; title: ; notranslate\" title=\"\">\r\nUSE AdventureWorks2014\r\nGO\r\nBEGIN TRAN\r\n\r\nTRUNCATE TABLE [dbo].[AWBuildVersion]\r\nSELECT * FROM [dbo].[AWBuildVersion]\r\n\r\nROLLBACK\r\nGO\r\nSELECT * FROM [dbo].[AWBuildVersion]\r\n<\/pre>\n<p><a href=\"https:\/\/sqldoubleg.live-website.com\/wp-content\/uploads\/2016\/05\/01_Truncate_rollback.png\" target=\"_blank\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/sqldoubleg.live-website.com\/wp-content\/uploads\/2016\/05\/01_Truncate_rollback.png\" alt=\"01_Truncate_rollback\" width=\"850\" height=\"168\" class=\"aligncenter size-full wp-image-227\" srcset=\"https:\/\/www.sqldoubleg.com\/wp-content\/uploads\/2016\/05\/01_Truncate_rollback.png 850w, https:\/\/www.sqldoubleg.com\/wp-content\/uploads\/2016\/05\/01_Truncate_rollback-300x59.png 300w, https:\/\/www.sqldoubleg.com\/wp-content\/uploads\/2016\/05\/01_Truncate_rollback-150x30.png 150w\" sizes=\"(max-width: 850px) 100vw, 850px\" \/><\/a><\/p>\n<p>That was easy, you can observe the first SELECT does not return any rows as it&#8217;s within the transaction scope, but once we issue a ROLLBACK, our row(s) will come back. I chose this table with a single row, but obviously rows will get back regardless the number.<\/p>\n<p>&nbsp;<br \/>\n<strong>TRUNCATE TABLE <em>deletes all the rows<\/em><\/strong><\/p>\n<p>This is actually true, what is sometimes misunderstood is how TRUNCATE TABLE does the same job as DELETE without WHERE clause. Just a look to <a href=\"https:\/\/msdn.microsoft.com\/en-GB\/library\/ms177570.aspx\" target=\"_blank\">BOL<\/a>, we can find <em>TRUNCATE TABLE removes the data by deallocating the data pages used to store the table data<\/em>. <\/p>\n<p>By deallocating pages instead of deleting each individual row, TRUNCATE runs immediately regardless the size of the table not like DELETE, where the more rows in the table, the longer will take to complete.<\/p>\n<p>To see how that happens, let&#8217;s get first all pages allocated to our table<\/p>\n<pre class=\"brush: tsql; title: ; notranslate\" title=\"\">\r\nUSE AdventureWorks2014\r\nGO\r\nDBCC IND('AdventureWorks2014','dbo.AWBuildVersion', 0)\r\n<\/pre>\n<p><a href=\"https:\/\/sqldoubleg.live-website.com\/wp-content\/uploads\/2016\/05\/02_DBCC_IND.png\" target=\"_blank\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/sqldoubleg.live-website.com\/wp-content\/uploads\/2016\/05\/02_DBCC_IND.png\" alt=\"02_DBCC_IND\" width=\"1108\" height=\"95\" class=\"aligncenter size-full wp-image-228\" srcset=\"https:\/\/www.sqldoubleg.com\/wp-content\/uploads\/2016\/05\/02_DBCC_IND.png 1108w, https:\/\/www.sqldoubleg.com\/wp-content\/uploads\/2016\/05\/02_DBCC_IND-300x26.png 300w, https:\/\/www.sqldoubleg.com\/wp-content\/uploads\/2016\/05\/02_DBCC_IND-1024x88.png 1024w, https:\/\/www.sqldoubleg.com\/wp-content\/uploads\/2016\/05\/02_DBCC_IND-150x13.png 150w\" sizes=\"(max-width: 1108px) 100vw, 1108px\" \/><\/a><\/p>\n<p>We have to 2 pages, one IAM page (PageType 10) and one DATA page (PageType 1), see more reference <a href=\"https:\/\/blogs.msdn.microsoft.com\/sqlserverstorageengine\/2006\/12\/13\/more-undocumented-fun-dbcc-ind-dbcc-page-and-off-row-columns\/\" target=\"_blank\">here<\/a><\/p>\n<p>Let&#8217;s check what&#8217;s inside those pages<\/p>\n<pre class=\"brush: tsql; title: ; notranslate\" title=\"\">\r\nDBCC TRACEON (3604)\r\n\r\nDBCC PAGE('AdventureWorks2014', 1, 883, 3)\r\n\r\n\/*\r\nIAM: Single Page Allocations @0x0000000010D5A08E\r\n\r\nSlot 0 = (1:882)                    Slot 1 = (0:0)                      Slot 2 = (0:0)\r\nSlot 3 = (0:0)                      Slot 4 = (0:0)                      Slot 5 = (0:0)\r\nSlot 6 = (0:0)                      Slot 7 = (0:0)                      \r\n*\/\r\n<\/pre>\n<p>This is the IAM page and we can see that our data page (882) has an entry on it.<\/p>\n<p>You can see the other page just contain the single row existing in the table<\/p>\n<pre class=\"brush: tsql; title: ; notranslate\" title=\"\">\r\nDBCC PAGE('AdventureWorks2014', 1, 882, 2)\r\n\/*\r\nDATA:\r\n\r\n\r\nMemory Dump @0x00000000125CA000\r\n\r\n00000000125CA000:   01010000 20020001 00000000 00001500 00000000  .... ...............\r\n00000000125CA014:   00000100 d6000000 701f8e00 72030000 01000000  ....\u00d6...p.\u008e.r.......\r\n00000000125CA028:   2a000000 78130000 16000000 5a050000 00000000  *...x.......Z.......\r\n00000000125CA03C:   56a4b06a 00000000 00000000 00000000 00000000  V\u00a4\u00b0j................\r\n00000000125CA050:   00000000 00000000 00000000 00000000 30001500  ................0...\r\n00000000125CA064:   01200f49 00d8a200 00000000 0062a300 00040000  . .I.\u00d8\u00a2......b\u00a3.....\r\n00000000125CA078:   01002e00 31003200 2e003000 2e003100 38003000  ....1.2...0...1.8.0.\r\n00000000125CA08C:   30000000 21212121 21212121 21212121 21212121  0...!!!!!!!!!!!!!!!!\r\n00000000125CA0A0:   21212121 21212121 21212121 21212121 21212121  !!!!!!!!!!!!!!!!!!!!\r\n00000000125CA0B4:   21212121 21212121 21212121 21212121 21212121  !!!!!!!!!!!!!!!!!!!!\r\n*\/\r\n<\/pre>\n<p>&nbsp;<br \/>\nNow I&#8217;m going to show you the same 2 pages after doing a TRUNCATE<\/p>\n<pre class=\"brush: tsql; title: ; notranslate\" title=\"\">\r\nUSE AdventureWorks2014\r\nGO\r\nBEGIN TRAN\r\n\r\nTRUNCATE TABLE [dbo].[AWBuildVersion]\r\n\r\nDBCC PAGE('AdventureWorks2014', 1, 883, 3)\r\n\r\n\/*\r\nIAM: Single Page Allocations @0x0000000012B5A08E\r\n\r\nSlot 0 = (0:0)                      Slot 1 = (0:0)                      Slot 2 = (0:0)\r\nSlot 3 = (0:0)                      Slot 4 = (0:0)                      Slot 5 = (0:0)\r\nSlot 6 = (0:0)                      Slot 7 = (0:0)                      \r\n*\/\r\n<\/pre>\n<p>And you can see how the entry where our data page was allocated before is no longer there.<\/p>\n<p>But, the data page is still there, completely untouched (only the header would reflect the change)<\/p>\n<pre class=\"brush: tsql; title: ; notranslate\" title=\"\">\r\nDBCC PAGE('AdventureWorks2014', 1, 882, 3)\r\n\/*\r\nDATA:\r\n\r\nMemory Dump @0x00000000125CA000\r\n\r\n00000000125CA000:   01010000 20020001 00000000 00001500 00000000  .... ...............\r\n00000000125CA014:   00000100 d6000000 701f8e00 72030000 01000000  ....\u00d6...p.\u008e.r.......\r\n00000000125CA028:   2a000000 78130000 16000000 5a050000 00000000  *...x.......Z.......\r\n00000000125CA03C:   56a4b06a 00000000 00000000 00000000 00000000  V\u00a4\u00b0j................\r\n00000000125CA050:   00000000 00000000 00000000 00000000 30001500  ................0...\r\n00000000125CA064:   01200f49 00d8a200 00000000 0062a300 00040000  . .I.\u00d8\u00a2......b\u00a3.....\r\n00000000125CA078:   01002e00 31003200 2e003000 2e003100 38003000  ....1.2...0...1.8.0.\r\n00000000125CA08C:   30000000 21212121 21212121 21212121 21212121  0...!!!!!!!!!!!!!!!!\r\n00000000125CA0A0:   21212121 21212121 21212121 21212121 21212121  !!!!!!!!!!!!!!!!!!!!\r\n00000000125CA0B4:   21212121 21212121 21212121 21212121 21212121  !!!!!!!!!!!!!!!!!!!!\r\n*\/\r\n\r\nROLLBACK\r\n<\/pre>\n<p>That is how we can ROLLBACK the statement, the SQL engine only has to allocate the page(s) back in place and all would be again like nothing happened.<\/p>\n<p>&nbsp;<br \/>\n<strong>TRUNCATE TABLE <em>It\u2019s not fully logged<\/em><\/strong><\/p>\n<p>This was already debunked by <a href=\"https:\/\/www.pluralsight.com\/courses\/sqlserver-myths\" target=\"_blank\">Paul Randal in a pluralsight Course called \u00abSQL Server: Myths and Misconceptions\u00bb<\/a> (which I strongly recommend), but I will add a demo to show you how this is just a misconception.<\/p>\n<p>Also from the two previous examples you might have realized that this is obviously not true, because how can you ROLLBACK something if there is no enough logging information? But let&#8217;s see that info to be certain.<\/p>\n<p>First I&#8217;ll clear the log<\/p>\n<pre class=\"brush: tsql; title: ; notranslate\" title=\"\">\r\nCHECKPOINT\r\n\r\nSELECT [Current LSN], Operation, [Transaction ID], AllocUnitName\r\n\t\t, [Page ID], [Lock Information], Description, [Transaction SID] \r\n\tFROM fn_dblog(NULL, NULL)\r\n<\/pre>\n<p><a href=\"https:\/\/sqldoubleg.live-website.com\/wp-content\/uploads\/2016\/05\/03_fn_fblog_before.png\" target=\"_blank\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/sqldoubleg.live-website.com\/wp-content\/uploads\/2016\/05\/03_fn_fblog_before.png\" alt=\"03_fn_fblog_before\" width=\"1002\" height=\"132\" class=\"aligncenter size-full wp-image-229\" srcset=\"https:\/\/www.sqldoubleg.com\/wp-content\/uploads\/2016\/05\/03_fn_fblog_before.png 1002w, https:\/\/www.sqldoubleg.com\/wp-content\/uploads\/2016\/05\/03_fn_fblog_before-300x40.png 300w, https:\/\/www.sqldoubleg.com\/wp-content\/uploads\/2016\/05\/03_fn_fblog_before-150x20.png 150w\" sizes=\"(max-width: 1002px) 100vw, 1002px\" \/><\/a><\/p>\n<p>And now we can start our TRANSACTION <\/p>\n<pre class=\"brush: tsql; title: ; notranslate\" title=\"\">\r\nBEGIN TRAN\r\n\r\nTRUNCATE TABLE dbo.[AWBuildVersion]\r\n\r\nSELECT [Current LSN], Operation, [Transaction ID], AllocUnitName\r\n\t\t, [Page ID], [Lock Information], Description, [Transaction SID] \r\n\tFROM fn_dblog(NULL, NULL)\r\n\r\nROLLBACK\r\n<\/pre>\n<p><a href=\"https:\/\/sqldoubleg.live-website.com\/wp-content\/uploads\/2016\/05\/04_fn_fblog_after.png\" target=\"_blank\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/sqldoubleg.live-website.com\/wp-content\/uploads\/2016\/05\/04_fn_fblog_after.png\" alt=\"04_fn_fblog_after\" width=\"1285\" height=\"526\" class=\"aligncenter size-full wp-image-230\" srcset=\"https:\/\/www.sqldoubleg.com\/wp-content\/uploads\/2016\/05\/04_fn_fblog_after.png 1285w, https:\/\/www.sqldoubleg.com\/wp-content\/uploads\/2016\/05\/04_fn_fblog_after-300x123.png 300w, https:\/\/www.sqldoubleg.com\/wp-content\/uploads\/2016\/05\/04_fn_fblog_after-1024x419.png 1024w, https:\/\/www.sqldoubleg.com\/wp-content\/uploads\/2016\/05\/04_fn_fblog_after-150x61.png 150w\" sizes=\"(max-width: 1285px) 100vw, 1285px\" \/><\/a><\/p>\n<p>At first glance we can see that there are 20 rows where it was 3 rows before, so there is log activity, and if we look at the last two columns we can see how pages are deallocated. There is also some metadata updated along the way, but that proves TRUNCATE TABLE is fully logged, and paraphrasing Paul Randal it&#8217;s <strong><em>very efficiently logged<\/em><\/strong>, just imaging the amount of log records generated by a DELETE statement in a big table and compare it to just deallocating a few pages. Very smart and very efficient. Love it!<\/p>\n<p>&nbsp;<br \/>\n<strong>TRUNCATE TABLE <em>drops and creates a new table<\/em><\/strong><\/p>\n<p>Well, since I have shown you what it does, I guess there is no need to expend much time on this. <\/p>\n<p>No, TRUNCATE table does not DROP and CREATE a new table, period.<\/p>\n<p>Some people might think so because of the impossibility of using TRUNCATE on a table which is referenced by a FOREIGN KEY constraint, but there is another explanation. <\/p>\n<p>We have seen TRUNCATE does not look at the content of individual data pages, just simply deallocates them, so the engine CANNOT know if there is any row on those pages which is referenced by the other table,<br \/>\nIt&#8217;d just be impossible to allow such operation without taking the risk of throwing away the referential integrity of our data, therefore is not permitted.<\/p>\n<p>And that&#8217;s again the reason why TRUNCATE TABLE does not fire triggers, because it does not work with rows, works with pages.<\/p>\n<p>&nbsp;<br \/>\n<strong>Conclusion<\/strong><\/p>\n<p>Just to wrap up I want to give you some links you might find useful and recommend you don&#8217;t believe everything you read  on the internet, specially if it does not come from trusted sources. (Or there is a demo you can reproduce \ud83d\ude42 ).<\/p>\n<p>Thanks for reading!<\/p>\n<ul>\n<li><a href=\"https:\/\/msdn.microsoft.com\/en-GB\/library\/ms177570.aspx\" target=\"_blank\">TRUNCATE TABLE<\/a><\/li>\n<li><a href=\"https:\/\/blogs.msdn.microsoft.com\/sqlserverstorageengine\/2006\/12\/13\/more-undocumented-fun-dbcc-ind-dbcc-page-and-off-row-columns\/\" target=\"_blank\">DBCC IND and DBCC PAGE<\/a><\/li>\n<li><a href=\"http:\/\/www.sqlskills.com\/blogs\/paul\/using-fn_dblog-fn_dump_dblog-and-restoring-with-stopbeforemark-to-an-lsn\/\" target=\"_blank\">fn_dblog()<\/a><\/li>\n<\/ul>\n<p>&nbsp;<\/p>\n","protected":false},"excerpt":{"rendered":"<p>There is a number misconceptions around TRUNCATE TABLE which have been around for too long that people take them as the real truth, let&#8217;s debunk some of those&nbsp;TRUNCATE TABLE in SQL Server&#8230;<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[4],"tags":[5,12,38],"_links":{"self":[{"href":"https:\/\/www.sqldoubleg.com\/es\/wp-json\/wp\/v2\/posts\/226"}],"collection":[{"href":"https:\/\/www.sqldoubleg.com\/es\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.sqldoubleg.com\/es\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.sqldoubleg.com\/es\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.sqldoubleg.com\/es\/wp-json\/wp\/v2\/comments?post=226"}],"version-history":[{"count":1,"href":"https:\/\/www.sqldoubleg.com\/es\/wp-json\/wp\/v2\/posts\/226\/revisions"}],"predecessor-version":[{"id":232,"href":"https:\/\/www.sqldoubleg.com\/es\/wp-json\/wp\/v2\/posts\/226\/revisions\/232"}],"wp:attachment":[{"href":"https:\/\/www.sqldoubleg.com\/es\/wp-json\/wp\/v2\/media?parent=226"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.sqldoubleg.com\/es\/wp-json\/wp\/v2\/categories?post=226"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.sqldoubleg.com\/es\/wp-json\/wp\/v2\/tags?post=226"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}