<?xml version="1.0" encoding="utf-8"?>
<rss xmlns:atom="http://www.w3.org/2005/Atom" xmlns:sy="http://purl.org/rss/1.0/modules/syndication/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
  version="2.0">
  <channel>
    <title>Blogs from Suhas</title>
    <atom:link
      href="https://docs.microsoft.com/archive/blogs/suhde/feed.xml"
      rel="self"
      type="application/rss+xml" />
    <link>https://docs.microsoft.com/archive/blogs/suhde/feed.xml</link>
    <description>Dirtying my hands in SQL Server</description>
    <lastBuildDate>Tue, 31 Mar 2015 16:56:23 GMT</lastBuildDate>
    <language>en-US</language>
    <sy:updatePeriod>hourly</sy:updatePeriod>
    <sy:updateFrequency>1</sy:updateFrequency>
    <item>
      <title>Internals of Database Snapshot – Part 3: Snapshot and Mirroring</title>
      <link>https://docs.microsoft.com/archive/blogs/suhde/internals-of-database-snapshot-part-3-snapshot-and-mirroring</link>
      <pubDate>Mon, 15 Aug 2011 06:25:32 GMT</pubDate>
      <dc:creator><![CDATA[Suhas De]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/suhde/2011/08/15/internals-of-database-snapshot-part-3-snapshot-and-mirroring/</guid>
      <description><![CDATA[In my earlier posts, we have seen how database snapshots work, and how the Buffer Pool is used when...]]></description>
      <content:encoded><![CDATA[<p align="justify"><font size="2" face="Arial">In my earlier posts, we have seen how database snapshots work, and how the Buffer Pool is used when we query Snapshot Databases. In this post, we will see how Database Snapshots work, when they are created against Mirrored Databases.</font></p>  <p align="justify"><font size="2" face="Arial">Let us assume we have a mirroring setup, as described in the below image. </font><font size="2" face="Arial">SQLServer1 hosts the Principal Database (MyDB) while the same database on SQLServer2 is the Mirror Database.      <br /></font><font size="2" face="Arial">     <br /><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/21/25/metablogapi/2158.image_20A1792C.png" original-url="http://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-21-25-metablogapi/2158.image_5F00_20A1792C.png"><img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="image" border="0" alt="image" src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/21/25/metablogapi/0412.image_thumb_03D7DE4A.png" original-url="http://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-21-25-metablogapi/0412.image_5F00_thumb_5F00_03D7DE4A.png" width="404" height="227" /></a></font></p>  <p align="justify"><font size="2" face="Arial">It is possible to create a Snapshot of the Mirrored Database (MyDB on SQLServer2), in case we want to offload some of the Reporting Load from the Principal Database. Of course, we have to remember that the Snapshot presents data specific to a point in time; hence, users will be reading stale data till the Snapshot is refreshed (dropped and re-created).</font></p>  <p align="justify"><font size="2" face="Arial">Pre-requisites for being able to create a Snapshot of the Mirrored Database are:</font></p>  <ul>   <li>     <div align="justify"><font size="2" face="Arial">The Principal and Mirror should be in Connected State.</font></div>   </li>    <li>     <div align="justify"><font size="2" face="Arial">The Principal and Mirror should be completely Synchronized at the time when the Snapshot is being created.</font></div>   </li> </ul>  <p align="justify"><font size="2" face="Arial">The Mirroring Mode (Asynchronous, Synchronous, and Synchronous with Automatic Failover) are not important as long as the pre-requisites above are true. However, in high transaction rate OLTP systems, with Asynchronous Mirroring setup, it is difficult to find a point in time when the Principal and Mirror are in a Synchronized state; hence, it is very difficult to create Snapshot on the Mirrored Database. If the databases are not synchronized, and we attempt to create a Snapshot on the Mirror Database, we will receive an error as below:</font></p>  <div id="codeSnippetWrapper">   <pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &#39;Courier New&#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px" id="codeSnippet"><span style="color: #ff0000">Msg 1822, Level 16, State 1, Line 1<br />The database must be online to have a database snapshot.</span></pre>

  <br /></div>

<p align="justify"><font size="2" face="Arial">The error can be misleading; because it says nothing about the Mirroring State issue that causes the Snapshot creation to fail; however, we have to keep in mind that error messages related to Database Mirroring are not very descriptive at the moment. The SQL Server Development Team is working on to make the error messages more meaningful, and it may take a while before we see more descriptive messages.</font></p>

<p align="justify"><font size="2" face="Arial">When it comes to the internals of how Database Snapshots work and how the Buffer Pool reacts to queries on the Snapshot Database, the same principals as mentioned in my previous posts still hold true. Only differences being:</font></p>

<ul>
  <li>
    <div align="justify"><font size="2" face="Arial">the Reads happen, not from the Online Principal Database, but from the Mirror Database, and</font></div>
  </li>

  <li>
    <div align="justify"><font size="2" face="Arial">the Copy-On-Write mechanism transfers pages from the Restoring Mirror Database and not the Online Principal Database.</font></div>
  </li>
</ul>

<p align="justify"><font size="2" face="Arial">If you are wondering how the Reads and Writes can happen from a Database that is in Restoring State, you must realize that the State of the Database is how the SQL Server Engine exposes the Logical Database for external users. The Physical Database Files, on the disk, are still available for Reads and Writes, and the SQL Server Engine does Read data from and Write data into these file. Additionally, there are quite a few undocumented DBCC commands that can work against the Restoring Database.</font></p>

<p align="justify"><font size="2" face="Arial">What happens when a role switching occurs is more important. During role switching, the Snapshot is not moved to the new Mirror Server. If the Snapshot was created on MyDB Database on SQLServer2 (in the image above), the Snapshot will still continue to exist on SQLServer2 even after SQLServer2 becomes the Principal Server. Now, if another role switch happens and SQLServer2 becomes the Mirror Server again, the Snapshot will still continue to be on SQLServer2. However, each Role Switch will cause the Database and the Snapshot to be restarted, and hence users will be temporarily disconnected.</font></p>

<p align="justify"><font size="2" face="Arial">One interesting question that was asked to me by one of my customers was: <strong>How can I give access to the Snapshot to a particular user without giving him access to the Principal Database?</strong></font></p>

<p align="justify"><font size="2" face="Arial">The answer is not so straight-forward. The Snapshot is a Read-Only copy of the Database; and you cannot simply create an user in the Snapshot. Below are the steps that can be followed for granting access:</font></p>

<p align="justify"><font size="2" face="Arial"><strong>For Windows Logins:</strong></font></p>

<ul>
  <li>
    <div align="justify"><font size="2" face="Arial">Use sp_grantlogin to grant the Windows Account access to the Principal Server.</font></div>
  </li>

  <li>
    <div align="justify"><font size="2" face="Arial">Use sp_grantlogin to grant the Windows Account access to the Mirror Server.</font></div>
  </li>

  <li>
    <div align="justify"><font size="2" face="Arial">Add the login as an user in the Principal Database with appropriate privileges. This change will be automatically propagated to the Mirror Database.</font></div>
  </li>

  <li>
    <div align="justify"><font size="2" face="Arial">Now, create a Snapshot of the Mirror Database.</font></div>
  </li>

  <li>
    <div align="justify"><font size="2" face="Arial">Now, drop the User and the Login from the Principal Database / Server.</font></div>
  </li>
</ul>

<p align="justify"><font size="2" face="Arial"><strong>For SQL Logins:</strong></font></p>

<ul>
  <li>
    <div align="justify"><font size="2" face="Arial">Use sp_addlogin to add the login to the Principal Server.</font></div>
  </li>

  <li>
    <div align="justify"><font size="2" face="Arial">Add the login as an user to the Principal Database.</font></div>
  </li>

  <li>
    <div align="justify"><font size="2" face="Arial">Use <a href="http://support.microsoft.com/kb/918992" target="_blank">KB918992</a> to move this login (along with the Password and the SID) to the Mirror Server.</font></div>
  </li>

  <li>
    <div align="justify"><font size="2" face="Arial">Now, create a Snapshot of the Mirror Database.</font></div>
  </li>

  <li>
    <div align="justify"><font size="2" face="Arial">Drop the User and Login from the Principal Database / Server.</font></div>
  </li>
</ul>

<p align="justify"><font size="2" face="Arial"><strong>Remember, this is not an One-Time activity. </strong>The steps outlined above will have to be repeated each time you refresh (drop and recreate) the Snapshot.</font></p>

<p align="justify"><strong><font face="Arial"><font size="2"><u>Disclaimer</u>: All information provided here is my personal opinion and is neither verified nor approved by Microsoft before it is published. All information, and code samples, if any, is provided &quot;AS IS&quot; with no warranties and confers no rights.</font></font></strong></p>]]></content:encoded>
    </item>
    <item>
      <title>Internals of Database Snapshot – Part 2: Snapshot and the Buffer Pool</title>
      <link>https://docs.microsoft.com/archive/blogs/suhde/internals-of-database-snapshot-part-2-snapshot-and-the-buffer-pool</link>
      <pubDate>Tue, 02 Aug 2011 05:46:32 GMT</pubDate>
      <dc:creator><![CDATA[Suhas De]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/suhde/2011/08/02/internals-of-database-snapshot-part-2-snapshot-and-the-buffer-pool/</guid>
      <description><![CDATA[In my earlier post, we have seen what Database Snapshots are, and how the Database Snapshots are...]]></description>
      <content:encoded><![CDATA[<p align="justify"><font size="2" face="Arial">In my earlier post, we have seen what Database Snapshots are, and how the Database Snapshots are maintained by the SQL Server Database Engine. In this post we will see how the data and index pages, belonging to the Snapshot, are managed in the Buffer Pool.</font></p>  <p align="justify"><font size="2" face="Arial">Before we start drilling down further, let me take a while to explain how we will pull up details about pages in the Buffer Pool. We have a DMV and a few Catalog Views that we can use:</font></p>  <ul>   <li>     <div align="justify"><font size="2" face="Arial"><a href="http://msdn.microsoft.com/en-us/library/ms173442.aspx" target="_blank">sys.dm_os_buffer_descriptors</a>: This DMV returns information about all the data pages that are currently in the SQL Server buffer pool. When a data page is read from disk, the page is copied into the SQL Server buffer pool and cached for reuse. Each cached data page has one buffer descriptor. Buffer descriptors uniquely identify each data page that is currently cached in an instance of SQL Server. sys.dm_os_buffer_descriptors returns cached pages for all user and system databases. This includes pages that are associated with the Resource database.</font></div>   </li>    <li>     <div align="justify"><font size="2" face="Arial"><a href="http://msdn.microsoft.com/en-us/library/ms189792.aspx" target="_blank">sys.allocation_units</a>: Contains a row for each allocation unit in the database. <strong>sys.dm_os_buffer_descriptors</strong> can be joined with <strong>sys.allocation_units</strong> based on the <strong>allocation_unit_id</strong> column values.</font></div>   </li>    <li>     <div align="justify"><font size="2" face="Arial"><a href="http://msdn.microsoft.com/en-us/library/ms175012.aspx" target="_blank">sys.partitions</a>: Contains a row for each partition of all the tables and most types of indexes in the database. Special index types like Fulltext, Spatial, and XML are not included in this view. All tables and indexes in SQL Server 2008 contain at least one partition, whether or not they are explicitly partitioned. The <strong>container_id column of sys.allocation_units</strong> can be joined with the <strong>hobt_id column of sys.partitions</strong> when the allocation <strong>type in sys.allocation_units</strong> is either 1 (In-row data) or 3 (Row-overflow data). When allocation <strong>type in sys.allocation_units</strong> is 3 (Large object (LOB) data (text, ntext, image, xml, large value types, and CLR user-defined types)), <strong>container_id column of sys.allocation_units</strong> can be joined with the <strong>partition_id column of sys.partitions</strong>.</font></div>   </li> </ul>  <p align="justify"><font size="2" face="Arial">Joining these, we can construct the following query that can give us the Count of Pages in the Buffer Pool on a Per Object and Page Type basis:</font></p>  <div align="justify">   <pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &#39;Courier New&#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px" id="codeSnippet"><p><span style="color: #0000ff"><font color="#008000">/*<br />This code has to be run under the context of the Database as<br />in Line 2. For example, if you want to get the details of the<br />database &quot;MyDB&quot;, change Line 2 to DB_ID('MyDB'), and execute<br />this under the context of MyDB.<br /></font></span><span style="color: #0000ff"><font color="#008000">*/<br /></font>DECLARE</span> @DBID <span style="color: #0000ff">INT</span><br /><span style="color: #0000ff">SELECT</span> @DBID = <font color="#ff00ff">DB_ID</font>(<span style="color: #ff0000">'Database_Name'</span>)<br /><span style="color: #0000ff">SELECT</span> <br />    M.[object_name] [<span style="color: #0000ff">Object</span> Name]<br />    , M.[page_type] [Page Type]<br />    , <span style="color: #0000ff"><font color="#ff00ff">COUNT</font></span>(M.[page_type]) <span style="color: #0000ff">AS</span> [Page_Count]<br /><span style="color: #0000ff">FROM</span><br />(<br />    <span style="color: #0000ff">SELECT</span> <br />        OSBD.[database_id]<br />        , OSBD.[allocation_unit_id]<br />        , OSBD.[page_type]<br />        , AU.[container_id]<br />        , P.[object_id]<br />        , S.[name] + <span style="color: #ff0000">'.'</span> + O.[name] <span style="color: #0000ff">AS</span> [object_name]<br />    <span style="color: #0000ff">FROM</span> <font color="#008000">sys.dm_os_buffer_descriptors</font> OSBD<br />    <span style="color: #0000ff">JOIN</span> <font color="#008000">sys.allocation_units</font> AU<br />    <span style="color: #0000ff">ON</span> OSBD.allocation_unit_id = AU.allocation_unit_id<br />    <span style="color: #0000ff">JOIN</span> <font color="#008000">sys.partitions</font> P<br />    <span style="color: #0000ff">ON</span> AU.[container_id] = P.[hobt_id]<br />    <span style="color: #0000ff">JOIN</span> <font color="#008000">sys.objects</font> O<br />    <span style="color: #0000ff">ON</span> P.[object_id] = O.[object_id]<br />    <span style="color: #0000ff">JOIN</span> <font color="#008000">sys.schemas</font> S<br />    <span style="color: #0000ff">ON</span> O.[schema_id] = S.[schema_id]<br />    <span style="color: #0000ff">WHERE</span> OSBD.database_id = @DBID<br />    <span style="color: #0000ff">AND</span> AU.[type] <span style="color: #0000ff">IN</span> (1, 3)<br />    <span style="color: #0000ff">UNION</span> <span style="color: #0000ff">ALL</span><br />    <span style="color: #0000ff">SELECT</span> <br />        OSBD.[database_id]<br />        , OSBD.[allocation_unit_id]<br />        , OSBD.[page_type]<br />        , AU.[container_id]<br />        , P.[object_id]<br />        , S.[name] + <span style="color: #ff0000">'.'</span> + O.[name]<br />    <span style="color: #0000ff">FROM</span> <font color="#008000">sys.dm_os_buffer_descriptors</font> OSBD<br />    <span style="color: #0000ff">JOIN</span> <font color="#008000">sys.allocation_units</font> AU<br />    <span style="color: #0000ff">ON</span> OSBD.allocation_unit_id = AU.allocation_unit_id<br />    <span style="color: #0000ff">JOIN</span> <font color="#008000">sys.partitions</font> P<br />    <span style="color: #0000ff">ON</span> AU.[container_id] = P.[partition_id]<br />    <span style="color: #0000ff">JOIN</span> <font color="#008000">sys.objects</font> O<br />    <span style="color: #0000ff">ON</span> P.[object_id] = O.[object_id]<br />    <span style="color: #0000ff">JOIN</span> <font color="#008000">sys.schemas</font> S<br />    <span style="color: #0000ff">ON</span> O.[schema_id] = S.[schema_id]<br />    <span style="color: #0000ff">WHERE</span> OSBD.database_id = @DBID<br />    <span style="color: #0000ff">AND</span> AU.[type] <span style="color: #0000ff">IN</span> (2)<br />) <span style="color: #0000ff">AS</span> M<br /><span style="color: #0000ff">GROUP</span> <span style="color: #0000ff">BY</span> M.[object_name], M.[page_type]<br /><span style="color: #0000ff">ORDER</span> <span style="color: #0000ff">BY</span> 1</p></pre>
</div>
<font size="2" face="Arial"></font>

<p align="justify"><font size="2" face="Arial">Let us now see what happens when we go ahead and create a Snapshot of a database. Before we create the Snapshot, we will clear off the Buffer Pool using the DBCC DROPCLEANBUFFERS command.</font></p>

<blockquote>
  <p align="justify"><font size="2" face="Arial"><font color="#ff0000"><strong>Note: </strong>After running the DBCC DROPCLEANBUFFERS command, make sure you execute the query mentioned above to ensure that the Buffer Pool is completely empty of User Objects. Few pages of system tables may continue to exist in the Buffer Pool, and this is fine.</font></font></p>
</blockquote>

<p align="justify"><font size="2" face="Arial">After we create the Database Snapshot, if we execute the query above, on both the Source Database and the Snapshot, we will see that the Buffer Pool contains <strong>NO</strong> user data or index pages. All pages for these databases will belong to the system tables, which indicates that some of the metadata is transformed to the Snapshot.</font></p>

<blockquote>
  <p align="justify"><font color="#ff0000" size="2" face="Arial"><strong>Note:</strong> In case you have defined Full Text Indexes on your tables, this will not hold good – the query against the Source Database will show some pages belonging to some of the User Tables.</font></p>
</blockquote>

<p align="justify"><font size="2" face="Arial">We will now query <strong>ONE </strong>row from one of the User Tables in the Snapshot. The table I am using is a heap with no indexes defined on it for simplicity, to avoid taking into consideration the Index Pages. You can try querying tables with Indexes (and even use complex queries so that multiple tables and indexes are touched) to observe the behavior.</font></p>

<div align="justify">
  <table border="0" cellspacing="0" cellpadding="2" width="584"><tbody>
      <tr>
        <td valign="top" width="307"><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/21/25/metablogapi/8461.image_14D78DD6.png" original-url="http://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-21-25-metablogapi/8461.image_5F00_14D78DD6.png"><img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="image" border="0" alt="image" src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/21/25/metablogapi/5722.image_thumb_20F907FD.png" original-url="http://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-21-25-metablogapi/5722.image_5F00_thumb_5F00_20F907FD.png" width="299" height="378" /></a> 

          <br />

          <br />

          <br /><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/21/25/metablogapi/4382.DatabaseSnapshot6_507E94A9.png" original-url="http://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-21-25-metablogapi/4382.DatabaseSnapshot6_5F00_507E94A9.png"><img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="DatabaseSnapshot6" border="0" alt="DatabaseSnapshot6" src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/21/25/metablogapi/6038.DatabaseSnapshot6_thumb_0A8D6189.png" original-url="http://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-21-25-metablogapi/6038.DatabaseSnapshot6_5F00_thumb_5F00_0A8D6189.png" width="298" height="165" /></a></td>

        <td valign="top" width="275">
          <p align="justify"><font size="2" face="Arial">As we have discussed in my earlier post, the pages in the Source Database have not changed yet, and hence the Snapshot contains no user data. So, now, the data has to be retrieved from the Source Database. </font></p>

          <p align="justify"><font size="2" face="Arial">The SQL Server Engine checks if the requested pages are already in the Buffer Pool under the context of the Snapshot Database. If yes, the data is returned back from the Buffer Pool without any additional work.</font></p>

          <p align="justify"><font size="2" face="Arial">However, if the pages are not already cached in the Buffer Pool, an In-Memory bitmap is consulted to find out which of the requested pages are available in the Sparse File, and which have to be read from the Source Database. IO requests are issued accordingly.</font></p>

          <p align="justify"><font size="2" face="Arial">This In-Memory bitmap resides in the Memory till the Snapshot is dropped. If SQL Server is recycled within the life-span of the Snapshot, the metadata of the Snapshot is queried to generate the bitmap again after SQL Server comes online.</font></p>

          <p align="justify"><font size="2" face="Arial">Since the Source Database has not been queried, the pages will not be cached under the Source Database; if later a different query needs this data from the Source Database, another set of IO requests will follow.</font></p>
        </td>
      </tr>
    </tbody></table>
</div>

<p align="justify"><font size="2" face="Arial">The same query (with the database name in Line 2 changed), when run against the Snapshot database, shows that the data pages traversed by the query are indeed cached in the Buffer Pool under the context of the Snapshot Database.</font></p>

<blockquote>
  <p align="justify"><font size="2" face="Arial"><font color="#ff0000"><strong>Remember:</strong> Databases do not share </font></font><font size="2" face="Arial"><font color="#ff0000">pages in the Buffer Pool.</font></font></p>
</blockquote>

<p align="justify"><font color="#000000" size="2" face="Arial">Now, let us see what happens when we modify a row in the Source Database. We will again use the Table with No Indexes for simplicity, and before we execute the update statement, we will clean up the Buffer Pool using the DBCC DROPCLEANBUFFERS command.</font></p>

<div align="justify">
  <table border="0" cellspacing="0" cellpadding="2" width="587"><tbody>
      <tr>
        <td valign="top" width="295"><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/21/25/metablogapi/2465.DatabaseSnapshot7_1FA2372D.png" original-url="http://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-21-25-metablogapi/2465.DatabaseSnapshot7_5F00_1FA2372D.png"><img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="DatabaseSnapshot7" border="0" alt="DatabaseSnapshot7" src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/21/25/metablogapi/5518.DatabaseSnapshot7_thumb_1D853864.png" original-url="http://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-21-25-metablogapi/5518.DatabaseSnapshot7_5F00_thumb_5F00_1D853864.png" width="288" height="350" /></a><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/21/25/metablogapi/8203.DatabaseSnapshot8_288B6F7D.png" original-url="http://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-21-25-metablogapi/8203.DatabaseSnapshot8_5F00_288B6F7D.png"><img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="DatabaseSnapshot8" border="0" alt="DatabaseSnapshot8" src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/21/25/metablogapi/1830.DatabaseSnapshot8_thumb_2AE4F17B.png" original-url="http://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-21-25-metablogapi/1830.DatabaseSnapshot8_5F00_thumb_5F00_2AE4F17B.png" width="288" height="379" /></a></td>

        <td valign="top" width="290">
          <p align="justify"><font size="2" face="Arial">When we execute the query to modify the data on the Source Database, the data pages are first cached into the Buffer Pool under the context of the Source Database, if not already cached.</font></p>

          <p align="justify"><font size="2" face="Arial">Also, since we have to do a Table Scan for locating the one row that we want to modify, all these traversed pages will be cached.</font></p>

          <p align="justify"><font size="2" face="Arial">Now, the Copy On Write mechanism kicks in and the to-be-dirtied pages are copied into the Snapshot sparse file. Once the copy is complete, the pages can be modified in the Buffer Pool.</font></p>

          <p align="justify"><font size="2" face="Arial">The query against the Source Database clearly shows that the pages are cached in the context of the Source Database.</font></p>

          <p align="justify"><font size="2" face="Arial">When we execute the query against the Snapshot Database, we see that none of the pages are cached in the Buffer Pool under the context of the Snapshot.</font></p>

          <p align="justify"><font size="2" face="Arial">This is kind of expected now, as we have already established that databases do not share pages in the Buffer Pool, and pages are cached in the Buffer Pool under the context of the database which read those pages from the disk.</font></p>

          <p align="justify"><font size="2" face="Arial"><font color="#ff0000"><strong>Note: </strong>The above experiments show that if the same pages of the database are read from the Source Database as well as from the Snapshot, two copies of the pages will be cached in the Buffer Pool – one set under the context of the Source Database, and the second set under the context of the Snapshot. This might mean a waste of Buffer Pool memory, especially if these pages have never been modified since the snapshot creation.</font></font></p>
        </td>
      </tr>
</tr></tbody></table>
</div>

<p align="justify"><font size="2" face="Arial">We will do another experiment this time. We will <strong>modify a page in the Source Database</strong> and <strong>read the same page from the Snapshot</strong>. Later, we will check the contents in the Buffer Pool for this one object for both databases. For this purpose, we will use a slightly different query, as pasted below:</font></p>

<div align="justify">
  <pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &#39;Courier New&#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px" id="codeSnippet"><span style="color: #008000">/*<br />This code has to be run under the context of the Database as</span><br /><span style="color: #008000">in Line 2. For example, if you want to get the details of the</span><br /><span style="color: #008000">database &quot;MyDB&quot;, change Line 2 to DB_ID('MyDB'), and execute</span><br /><span style="color: #008000">this under the context of MyDB.<br />*/</span><br /><span style="color: #0000ff">DECLARE</span> @DBID <span style="color: #0000ff">INT</span><br /><span style="color: #0000ff">SELECT</span> @DBID = <font color="#ff00ff">DB_ID</font>(<span style="color: #ff0000">'Database_Name'</span>)<br /><span style="color: #0000ff">SELECT</span> <br />    BP.[<span style="color: #0000ff">Database</span> Name]<br />    , BP.[<span style="color: #0000ff">Object</span> Name]<br />    , BP.[<span style="color: #0000ff">File</span> ID]<br />    , BP.[Page ID]<br />    , BP.[Page Type]<br />    , BP.[Number <span style="color: #0000ff">of</span> <span style="color: #0000ff">Rows</span>]<br />    , BP.[<span style="color: #0000ff">Is</span> Modified]<br /><span style="color: #0000ff">FROM</span><br />(<br />    <span style="color: #0000ff">SELECT</span> <br />        <font color="#ff00ff">DB_NAME</font>(OSBD.[database_id]) [<span style="color: #0000ff">Database</span> Name]<br />        , S.[name] + <span style="color: #ff0000">'.'</span> + O.[name] [<span style="color: #0000ff">Object</span> Name]<br />        , OSBD.[file_id] [<span style="color: #0000ff">File</span> ID]<br />        , OSBD.[page_id] [Page ID]<br />        , OSBD.[page_type] [Page Type]<br />        , OSBD.[row_count] [Number <span style="color: #0000ff">of</span> <span style="color: #0000ff">Rows</span>]<br />        , OSBD.[is_modified] [<span style="color: #0000ff">Is</span> Modified]<br />    <span style="color: #0000ff">FROM</span> <font color="#008000">sys.dm_os_buffer_descriptors</font> OSBD<br />    <span style="color: #0000ff">JOIN</span> <font color="#008000">sys.allocation_units</font> AU<br />    <span style="color: #0000ff">ON</span> OSBD.allocation_unit_id = AU.allocation_unit_id<br />    <span style="color: #0000ff">JOIN</span> <font color="#008000">sys.partitions</font> P<br />    <span style="color: #0000ff">ON</span> AU.[container_id] = P.[hobt_id]<br />    <span style="color: #0000ff">JOIN</span> <font color="#008000">sys.objects</font> O<br />    <span style="color: #0000ff">ON</span> P.[object_id] = O.[object_id]<br />    <span style="color: #0000ff">JOIN</span> <font color="#008000">sys.schemas</font> S<br />    <span style="color: #0000ff">ON</span> O.[schema_id] = S.[schema_id]<br />    <span style="color: #0000ff">WHERE</span> OSBD.database_id = @DBID<br />    <span style="color: #0000ff">AND</span> AU.[type] <span style="color: #0000ff">IN</span> (1, 3)<br />    <span style="color: #0000ff">UNION</span> <span style="color: #0000ff">ALL</span><br />    <span style="color: #0000ff">SELECT</span> <br />        <font color="#ff00ff">DB_NAME</font>(OSBD.[database_id]) [<span style="color: #0000ff">Database</span> Name]<br />        , S.[name] + <span style="color: #ff0000">'.'</span> + O.[name] [<span style="color: #0000ff">Object</span> Name]<br />        , OSBD.[file_id] [<span style="color: #0000ff">File</span> ID]<br />        , OSBD.[page_id] [Page ID]<br />        , OSBD.[page_type] [Page Type]<br />        , OSBD.[row_count] [Number <span style="color: #0000ff">of</span> <span style="color: #0000ff">Rows</span>]<br />        , OSBD.[is_modified] [<span style="color: #0000ff">Is</span> Modified]<br />    <span style="color: #0000ff">FROM</span> <font color="#008000">sys.dm_os_buffer_descriptors</font> OSBD<br />    <span style="color: #0000ff">JOIN</span> <font color="#008000">sys.allocation_units</font> AU<br />    <span style="color: #0000ff">ON</span> OSBD.allocation_unit_id = AU.allocation_unit_id<br />    <span style="color: #0000ff">JOIN</span> <font color="#008000">sys.partitions</font> P<br />    <span style="color: #0000ff">ON</span> AU.[container_id] = P.[partition_id]<br />    <span style="color: #0000ff">JOIN</span> <font color="#008000">sys.objects</font> O<br />    <span style="color: #0000ff">ON</span> P.[object_id] = O.[object_id]<br />    <span style="color: #0000ff">JOIN</span> <font color="#008000">sys.schemas</font> S<br />    <span style="color: #0000ff">ON</span> O.[schema_id] = S.[schema_id]<br />    <span style="color: #0000ff">WHERE</span> OSBD.database_id = @DBID<br />    <span style="color: #0000ff">AND</span> AU.[type] <span style="color: #0000ff">IN</span> (2)<br />) <span style="color: #0000ff">AS</span> BP<br /><span style="color: #0000ff">WHERE</span> BP.[<span style="color: #0000ff">Object</span> Name] = <span style="color: #ff0000">'SchemaName.TableName'</span><br /><span style="color: #0000ff">ORDER</span> <span style="color: #0000ff">BY</span> BP.[<span style="color: #0000ff">File</span> ID], BP.[Page ID]</pre>
</div>

<p align="justify"><font size="2" face="Arial">When we analyse the contents of the Buffer Pool, we will see that the modified page has been marked as Modified in the Source Database, and marked as Clean in the Snapshot (screenshots below). This conclusively proves that pages in the Buffer Pool are not shared and that different states of the same page might be present in the Buffer Pool depending on when the Snapshot was created and when the page was modified.</font></p>

<blockquote>
  <p align="justify"><font size="2" face="Arial"><font color="#ff0000"><strong>Note:</strong> The page marked as Modified will not be removed from the Buffer Pool until Checkpoint runs on the database and flushes the dirty page to the disk. Once the dirty page is flushed, the page is marked as Clean (the Is_Modified bit is cleared), and then this page can be removed from the Buffer Pool by using the DBCC DROPCLEANBUFFERS command.</font></font></p>
</blockquote>

<div align="justify">
  <table border="0" cellspacing="0" cellpadding="2" width="518"><tbody>
      <tr>
        <td valign="top" width="518"><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/21/25/metablogapi/7801.DatabaseSnapshot9_06902B41.png" original-url="http://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-21-25-metablogapi/7801.DatabaseSnapshot9_5F00_06902B41.png"><img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="DatabaseSnapshot9" border="0" alt="DatabaseSnapshot9" src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/21/25/metablogapi/0044.DatabaseSnapshot9_thumb_64C452AF.png" original-url="http://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-21-25-metablogapi/0044.DatabaseSnapshot9_5F00_thumb_5F00_64C452AF.png" width="567" height="185" /></a></td>
      </tr>

      <tr>
        <td valign="top" width="518"><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/21/25/metablogapi/0550.DatabaseSnapshot10_1432C17C.png" original-url="http://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-21-25-metablogapi/0550.DatabaseSnapshot10_5F00_1432C17C.png"><img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="DatabaseSnapshot10" border="0" alt="DatabaseSnapshot10" src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/21/25/metablogapi/6327.DatabaseSnapshot10_thumb_7266E8EA.png" original-url="http://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-21-25-metablogapi/6327.DatabaseSnapshot10_5F00_thumb_5F00_7266E8EA.png" width="568" height="181" /></a></td>
      </tr>
    </tbody></table>
</div>

<p align="justify"><font size="2" face="Arial"><strong>Summary:</strong> <strong>Databases do not share pages (memory) in the Buffer Pool. Each database (Source Database and the Snapshot Database) caches its own pages in the Buffer Pool depending on the data pages being read. Hence, if you are planning to create multiple snapshots of the same database, apart from considering the impact due to increased IO (as described in my earlier post), you will also need to consider the increased Buffer Pool usage, and impact of it on performance.</strong></font></p>

<p align="justify"><font size="2" face="Arial">In the next post, we will see what happens internally when we create Snapshots on Mirror databases.</font></p>

<p align="justify"><strong><font face="Arial"><font size="2"><u>Disclaimer</u>: All information provided here is my personal opinion and is neither verified nor approved by Microsoft before it is published. All information, and code samples, if any, is provided &quot;AS IS&quot; with no warranties and confers no rights.</font></font></strong></p>]]></content:encoded>
    </item>
    <item>
      <title>Internals of Database Snapshot</title>
      <link>https://docs.microsoft.com/archive/blogs/suhde/internals-of-database-snapshot</link>
      <pubDate>Sun, 31 Jul 2011 01:09:30 GMT</pubDate>
      <dc:creator><![CDATA[Suhas De]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/suhde/2011/07/31/internals-of-database-snapshot/</guid>
      <description><![CDATA[In this post, we will explore the internals of Database Snapshot. Most of this information is...]]></description>
      <content:encoded><![CDATA[<p align="justify"><font size="2" face="Arial">In this post, we will explore the internals of Database Snapshot. Most of this information is already available in the Internet, in the posts authored by the SQL Server Development Team and the CSS Escalation Team; however, through this post we will attempt to consolidate all of these information for the benefit of everybody.</font></p>  <p align="justify"><font size="2" face="Arial">All credit for this post goes to my colleague, <a href="http://sqlship.wordpress.com/" target="_blank">Mohammad Sufian</a>, without whose help, this post may have not been possible.</font></p>  <p align="justify"><font size="2" face="Arial"><strong>So, what are Database Snapshots?</strong> According to the <a href="http://msdn.microsoft.com/en-us/library/ms175158.aspx" target="_blank">Books OnLine</a>, “<em>A database snapshot provides a read-only, static view of a source database as it existed at snapshot creation, minus any uncommitted transactions. Uncommitted transactions are rolled back in a newly created database snapshot because the Database Engine runs recovery after the snapshot has been created (transactions in the database are not affected).</em>” This means that the database snapshot is a transactionally consistent image of the database as of the snapshot creation time.</font></p>  <p align="justify"><font size="2" face="Arial"><strong>What can I use Database Snapshots for?</strong> Again, according to <a href="http://msdn.microsoft.com/en-us/library/ms175472.aspx" target="_blank">Books OnLine</a>, some uses of Database Snapshots might be:</font></p>  <ul>   <li>     <div align="justify"><font size="2" face="Arial">Maintaining historical data for report generation.</font></div>   </li>    <li>     <div align="justify"><font size="2" face="Arial">Using snapshots on Mirrored databases can help in offloading reporting.</font></div>   </li>    <li>     <div align="justify"><font size="2" face="Arial">Safeguarding data against administrative and user errors.</font></div>   </li>    <li>     <div align="justify"><font size="2" face="Arial">Managing a test database.</font></div>   </li> </ul>  <p align="justify"><font size="2" face="Arial"><strong>What can’t I do in Database Snapshots?</strong> The following lists a few things that are not allowed or will not work in Database Snapshots:</font></p>  <ul>   <li>     <div align="justify"><font size="2" face="Arial">Database snapshots are specific to a SQL Server instance.</font></div>   </li>    <li>     <div align="justify"><font size="2" face="Arial">Database snapshots can be created only if the source database is ONLINE or if it is the mirrored database.</font></div>   </li>    <li>     <div align="justify"><font size="2" face="Arial">Database snapshots cannot be backed up or restored.</font></div>   </li>    <li>     <div align="justify"><font size="2" face="Arial">Database snapshots cannot be detached or attached.</font></div>   </li>    <li>     <div align="justify"><font size="2" face="Arial">Database snapshots are read-only so they cannot be renamed. Settings cannot be modified as well.</font></div>   </li>    <li>     <div align="justify"><font size="2" face="Arial">Database snapshots inherit security settings, filegroup state from the source database and these settings cannot be modified on the database snapshot.</font></div>   </li>    <li>     <div align="justify"><font size="2" face="Arial">Database snapshot file specifications cannot be modified.</font></div>   </li>    <li>     <div align="justify"><font size="2" face="Arial">Database snapshot is marked suspect if there is no disk space when a modified page is being copied from the source database.</font></div>   </li>    <li>     <div align="justify"><font size="2" face="Arial">The point-in-time of a database snapshot only approximate in nature and can be determined from the create_date column of sys.databases catalog view entry for the database snapshot.</font></div>   </li>    <li>     <div align="justify"><font size="2" face="Arial">Database snapshots created on two source databases that have say views pointing from one database to another will retain their original definition in the snapshots also. Any queries against those views in the database snapshots will refer to the source databases if they are online. If the source databases are mirrored to a different server and database snapshots are created against those then the queries will fail since the source databases will be in recovery mode.</font></div>   </li>    <li>     <div align="justify"><font size="2" face="Arial">Full-text indexing is not supported on database snapshots.</font></div>   </li>    <li>     <div align="justify"><font size="2" face="Arial">Database snapshots of system databases are not allowed.</font></div>   </li> </ul>  <p align="justify"><font size="2" face="Arial"><strong>How can we create a Snapshot and what happens when we create a snapshot?</strong> To create a snapshot of a database we can use the <font color="#c0504d" face="Courier New">CREATE DATABASE &lt;specifications&gt; AS SNAPSHOT OF &lt;DatabaseName&gt;</font> command. Below is an example of the command that I used to create a Snapshot of the AdventureWorks database:</font></p>  <div align="justify">   <pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &#39;Courier New&#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px" id="codeSnippet"><span style="color: #0000ff">CREATE</span> <span style="color: #0000ff">DATABASE</span> AdventureWorks_Snapshot<br /><span style="color: #0000ff">ON</span> <br />(<br />    NAME = AdventureWorks2008R2_Data,<br />    <font color="#0000ff">FILENAME</font> = <span style="color: #ff0000">'D:\AdventureWorks_Snapshot.ss'</span><br />) <span style="color: #0000ff">AS</span> <font color="#0000ff">SNAPSHOT</font> <span style="color: #0000ff">OF</span> AdventureWorks<br /><font color="#0000ff">GO</font></pre>
</div>

<div align="justify">&#160;</div>

<div align="justify"><font size="2" face="Arial">When we use this command to create a database snapshot, the snapshot is implemented as a <strong>sparse file</strong>. By definition, <em>Sparse files are a feature of the NTFS file system. Initially, a sparse file contains no user data, and disk space for user data is not allocated to it. When first created, a sparse file takes up very little disk space. As data is written to the sparse file, NTFS allocates disk space gradually and the file grows in size. Potentially, a sparse file can grow very large.</em></font></div>

<div align="justify"></div>

<blockquote>
  <p align="justify"><font size="2" face="Arial"><font color="#ff0000"><strong>Remember:</strong> To be able to understand all of the below discussion, we need a database that is not touched by user transactions. Hence, I am using the AdventureWorks database on my local machine.</font></font></p>
</blockquote>

<p align="justify"><font size="2" face="Arial">Don’t believe it? Let’s see… The query below will give us the Max Size and the Size on Disk for the Snapshot Database:</font></p>

<div align="justify">
  <pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &#39;Courier New&#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px" id="codeSnippet"><span style="color: #0000ff">DECLARE</span> @DBID <span style="color: #0000ff">INT</span><br /><span style="color: #0000ff">SELECT</span> @DBID = <font color="#ff00ff">DB_ID</font>(<span style="color: #ff0000">'AdventureWorks_Snapshot'</span>)<br /><span style="color: #0000ff">SELECT</span> <br />    <font color="#ff00ff">DB_NAME</font>(@DBID) <span style="color: #0000ff">AS</span> <font color="#000000">[<span style="color: #0000ff"><font color="#000000">Database</font></span> Name],</font><br />    VFS.[file_id] <span style="color: #0000ff">AS</span> [FileID],<br />    MF.[name] <span style="color: #0000ff">AS</span> [Logical <span style="color: #0000ff"><font color="#000000">File</font></span> Name],<br />    (MF.[<span style="color: #0000ff">size</span>] * 8) <span style="color: #0000ff">AS</span> [<font color="#000000"><span style="color: #0000ff"><font color="#000000">Max</font></span> </font><span style="color: #0000ff"><font color="#000000">Size</font></span> (KB)],<br />    (VFS.size_on_disk_bytes / 1024) <span style="color: #0000ff">AS</span> [<span style="color: #0000ff"><font color="#000000">Size</font></span> <span style="color: #0000ff"><font color="#000000">on</font></span> <span style="color: #0000ff"><font color="#000000">Disk</font></span> (KB)]<br /><span style="color: #0000ff">FROM</span> <font color="#008000">sys.dm_io_virtual_file_stats</font>(@DBID, -1) VFS<br /><span style="color: #0000ff">JOIN</span> <font color="#008000">sys.master_files</font> MF<br />    <span style="color: #0000ff">ON</span> VFS.[file_id] = MF.[file_id]<br /><span style="color: #0000ff">WHERE</span> VFS.database_id = MF.database_id<br /><span style="color: #0000ff">AND</span> MF.database_id = @DBID<br /><span style="color: #0000ff">AND</span> MF.type_desc != <span style="color: #ff0000">'LOG'</span><br />GO</pre>
</div>

<div align="justify"><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/21/25/metablogapi/8284.DatabaseSnapshot1_5C9EEDBE.png" original-url="http://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-21-25-metablogapi/8284.DatabaseSnapshot1_5F00_5C9EEDBE.png"><img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="DatabaseSnapshot1" border="0" alt="DatabaseSnapshot1" src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/21/25/metablogapi/2055.DatabaseSnapshot1_thumb_398E7C4E.png" original-url="http://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-21-25-metablogapi/2055.DatabaseSnapshot1_5F00_thumb_5F00_398E7C4E.png" width="594" height="399" /></a></div>

<p align="justify"><font size="2" face="Arial">Till now, the <strong><em>AdventureWorks_Snapshot.ss</em></strong> sparse file that we created contains no user data – just the necessary metadata for the database. The Max Size of the Snapshot is equal to the Size of the database when the snapshot was created, and the Size on Disk is the actual space used by the snapshot. Hence, 3200 KB or just a little above 3 MB is allocated, and this is sufficient for maintaining the metadata of the database. So, now, the obvious question is, how will data be returned if we query the snapshot? This is clearly documented in <a href="http://msdn.microsoft.com/en-us/library/ms187054.aspx" target="_blank">Books OnLine</a>:</font></p>

<div align="justify">
  <table border="0" cellspacing="0" cellpadding="2" width="587"><tbody>
      <tr>
        <td valign="top" width="190"><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/21/25/metablogapi/5852.DatabaseSnapshot2_2C31F4FD.png" original-url="http://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-21-25-metablogapi/5852.DatabaseSnapshot2_5F00_2C31F4FD.png"><img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="DatabaseSnapshot2" border="0" alt="DatabaseSnapshot2" src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/21/25/metablogapi/8015.DatabaseSnapshot2_thumb_05EF9BA5.png" original-url="http://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-21-25-metablogapi/8015.DatabaseSnapshot2_5F00_thumb_5F00_05EF9BA5.png" width="178" height="313" /></a></td>

        <td valign="top" width="395">
          <p align="justify"><font size="2" face="Arial">Since we have no user data in the snapshot at present and the data pages in the Source Database has not changed, the Read operation on the Snapshot reads the original page from the source database to retrieve the data from that page. The metadata in the Snapshot has the information about whether a page has been copied from the source; and based on this information, reads are redirected.</font></p>

          <p align="justify"><font size="2" face="Arial">This is, in fact, a very good design, as otherwise, we would have to copy huge sets of data (GBs or even TBs </font><font size="2" face="Arial">of data</font><font size="2" face="Arial">) to the Snapshot file. The copy operation would have taken huge time to complete, and </font><font size="2" face="Arial">would </font><font size="2" face="Arial">have exerted a huge load on the disk.</font></p>

          <p align="justify"><font size="2" face="Arial"><strong><font color="#ff0000">Myth 1</font><font color="#ff0000">: The sparse file contains all zero-d out pages. 
                  <br /></font></strong>Contrary to common belief, the sparse file does not contain zero-d out pages just yet. No extents are allocated, and the sparse file contains no data pages.</font></p>
        </td>
      </tr>
    </tbody></table>
</div>

<p align="justify"><font size="2" face="Arial">Now, when a page is modified on the source database, the pre-modified copy of the page is first copied to the database snapshot, and then the original page is modified. This operation is irrespective of whether the transaction that modified the page commits or rolls back. Here are the steps that are followed when a page is modified or dirtied:</font></p>

<ul>
  <li>
    <div align="justify"><font size="2" face="Arial">The Page is prepared to be dirtied.</font></div>
  </li>

  <li>
    <div align="justify"><font size="2" face="Arial">An EX Latch is acquired on the page.</font></div>
  </li>

  <li>
    <div align="justify"><font size="2" face="Arial">Replica writes are completed – this means that a replica of the page is created in the snapshot sparse file.</font></div>
  </li>

  <li>
    <div align="justify"><font size="2" face="Arial">Changes are now made to the page.</font></div>
  </li>

  <li>
    <div align="justify"><font size="2" face="Arial">The EX Latch on the page is now released.</font></div>
  </li>
</ul>

<p align="justify"><font size="2" face="Arial">This operation is known as the <strong>Copy On Write (COW)</strong> operation on database snapshots.</font></p>

<table border="0" cellspacing="0" cellpadding="2" width="587"><tbody>
    <tr>
      <td valign="top" width="190"><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/21/25/metablogapi/0028.DatabaseSnapshot5_28EDA6D8.png" original-url="http://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-21-25-metablogapi/0028.DatabaseSnapshot5_5F00_28EDA6D8.png"><img style="background-image: none; border-bottom: 0px; border-left: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top: 0px; border-right: 0px; padding-top: 0px" title="DatabaseSnapshot5" border="0" alt="DatabaseSnapshot5" src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/21/25/metablogapi/7701.DatabaseSnapshot5_thumb_6CE16522.png" original-url="http://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-21-25-metablogapi/7701.DatabaseSnapshot5_5F00_thumb_5F00_6CE16522.png" width="177" height="321" /></a></td>

      <td valign="top" width="395">
        <p align="justify"><font size="2" face="Arial">Once a page has been copied to the Snapshot, the metadata in the Snapshot is changed to reflect that the original state of the page is now available in the snapshot. </font></p>

        <p align="justify"><font size="2" face="Arial">Now, when a query attempts to read this page from the Snapshot (by querying the Snapshot Database), the page is returned directly from the Snapshot. For all other pages that have not been copied to the Snapshot, the source database will still be read to fetch the data.</font></p>

        <p align="justify"><font size="2" face="Arial"><strong>Note:</strong> Certain operations on the data, such as a Truncate Table, do not modify the data pages; rather, the allocations related to the table are removed from the metadata. So, when you run Truncate Table on a database, and cause a huge table (say 400 GB) to be emptied, the pages (data and index) belonging to the table are not copied over. When this same table or any other object attempts to re-use these allocations and over-write the pages – that’s when these pages are actually copied to the snapshot.</font></p>
      </td>
    </tr>
  </tbody></table>

<p align="justify"><font size="2" face="Arial"><strong><font color="#ff0000">Myth 2: Modified pages are placed adjacent to each other in the snapshot to reduce the sparse file used size. 
        <br /></font></strong>Contrary to common belief, modified pages are not placed adjacent to each other. Let us assume, Pages 50 and 150 are being modified. Let us also assume Page 50 belongs to Extent 7 and Page 150 belongs to Extent 19. So, Page 50 is the 2nd Page of Extent 7 and Page 150 is the 6th page of Extent 19.</font></p>

<p align="justify"><font size="2" face="Arial">When Page 50 is dirtied, a complete allocation of Extent 7 will be created in the sparse file at storage offset ((7 – 1) * 8 + 1) * 8192 bytes, and the contents of Page 50 will be copied to the 2nd Page in this extent. All other pages will be zero-d out. So, essentially, offset of the Page 50 will be 50 * 8192 bytes. When Page 51 is dirtied, a new extent will not be allocated; Extent 7 already has the place-holder for the Page 51, and hence, when this page is dirtied on the source, it will be copied in the data offset 51 * 8192 bytes.</font></p>

<p align="justify"><font size="2" face="Arial">Similarly, when Page 150 is modified, allocation of Extent 19 will be created at offset ((19 – 1) * 8 + 1) * 8192 = 145 * 8192 bytes. Page 150 will be the 6th Page in this extent and hence will be at offset 150 * 8192 bytes.. So, as you can see, these pages will be spread out in the sparse file at its own offset (depending on the number of the page). Below is a diagrammatic view of the same:</font></p>

<p align="justify"><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/21/25/metablogapi/1423.image_2DE86F13.png" original-url="http://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-21-25-metablogapi/1423.image_5F00_2DE86F13.png"><img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="image" border="0" alt="image" src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/21/25/metablogapi/4062.image_thumb_27C12278.png" original-url="http://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-21-25-metablogapi/4062.image_5F00_thumb_5F00_27C12278.png" width="593" height="158" /></a></p>

<p align="justify"></p>

<p align="justify"></p>

<p align="justify"><font size="2" face="Arial">Let us modify a few rows and see what happens to the snapshot. I will update the Sales.CreditCard table. Using undocumented commands, I know that:</font></p>

<ul>
  <li>
    <div align="justify"><font size="2" face="Arial">CreditCardID = 615 is the first row of page 1:2550.</font></div>
  </li>

  <li>
    <div align="justify"><font size="2" face="Arial">CreditCardID = 719 is the first row of page 1:2551.</font></div>
  </li>

  <li>
    <div align="justify"><font size="2" face="Arial">CreditCardID = 2663 is the first row of page 1:2570.</font></div>
  </li>
</ul>

<p align="justify"><font size="2" face="Arial">I will modify Page 2550 by updating the row with <font size="2" face="Arial">CreditCardID = 615. I used the following command:</font></font></p>

<div align="justify">
  <pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &#39;Courier New&#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px" id="codeSnippet"><span style="color: #0000ff">UPDATE</span> Sales.CreditCard <span style="color: #0000ff">SET</span> ExpMonth = 8 <span style="color: #0000ff">WHERE</span> CreditCardID = 615</pre>
</div>
<font size="2" face="Arial">
  <div align="justify">
    <br />After the update, if we check the size of the snapshot, we will see that the Size on Disk of the Snapshot file has increased to 3264 KB, which means that 1 extent has been allocated to the snapshot file.</div>
</font>

<p align="justify"><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/21/25/metablogapi/7624.DatabaseSnapshot3_774A9E01.png" original-url="http://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-21-25-metablogapi/7624.DatabaseSnapshot3_5F00_774A9E01.png"><img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="DatabaseSnapshot3" border="0" alt="DatabaseSnapshot3" src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/21/25/metablogapi/3022.DatabaseSnapshot3_thumb_6DA22FCB.png" original-url="http://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-21-25-metablogapi/3022.DatabaseSnapshot3_5F00_thumb_5F00_6DA22FCB.png" width="594" height="369" /></a></p>

<p align="justify"><font size="2" face="Arial">Now, lets modify Page 2551, and we will see that the Size on Disk of the Snapshot file will not change, as both of these pages belong to the same extent. However, if we modify Page 2570, which belongs to another extent, we will see that the Size on Disk of the snapshot will further increase by 64 KB (3264 KB + 64 KB = 3328 KB):</font></p>

<p align="justify"><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/21/25/metablogapi/2055.DatabaseSnapshot4_7AF81D04.png" original-url="http://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-21-25-metablogapi/2055.DatabaseSnapshot4_5F00_7AF81D04.png"><img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="DatabaseSnapshot4" border="0" alt="DatabaseSnapshot4" src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/21/25/metablogapi/7673.DatabaseSnapshot4_thumb_43625C16.png" original-url="http://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-21-25-metablogapi/7673.DatabaseSnapshot4_5F00_thumb_5F00_43625C16.png" width="595" height="381" /></a></p>

<p align="justify"><font size="2" face="Arial">All of the experiments above clearly demonstrate the <strong>Copy On Write </strong>mechanism as we had discussed above.</font></p>

<p align="justify"></p>

<blockquote>
  <p align="justify"><font size="2" face="Arial"><font color="#ff0000"><strong>Remember:</strong> Once a page has been copied over to the Snapshot, further modifications to the page will not cause it to be copied over.</font></font></p>
</blockquote>

<p align="justify"><font size="2" face="Arial">This means that for a Transactional Database, each page that is modified after the Snapshot is created, will cause the page to be copied over to the Snapshot. This also means that you will see a performance degradation when a page is modified for the first time after the Snapshot is created.</font></p>

<p align="justify"><font size="2" face="Arial">If you have multiple Snapshots of the same database, each page, when dirtied, will have to be copied to each Snapshot if it does not already exists in it. </font><font size="2" face="Arial">To explain this further, let us assume that you have created 3 Snapshots at the following times:</font></p>

<ul>
  <li>
    <div align="justify"><font size="2" face="Arial">Snapshot 1 is created at 1:00 AM.</font></div>
  </li>

  <li>
    <div align="justify"><font size="2" face="Arial">Snapshot 2 is created at 3:00 AM.</font></div>
  </li>

  <li>
    <div align="justify"><font size="2" face="Arial">Snapshot 3 is created at 5:00 AM.</font></div>
  </li>
</ul>

<p align="justify"><font size="2" face="Arial">If Page 2500 is modified, for the first time, at 6:00 AM after say, 12:30 AM, this page will have to be copied over to all 3 Snapshots before it can be modified. On the other hand, if Page 2890 is modified at 1:30 AM, and then at 5:30 AM, the modification at 1:30 AM will cause it to be copied over to Snapshot 1; and the modification at 5:30 AM will cause it to be copied over to Snapshot 2 and Snapshot 3 (it will not be copied to Snapshot 1 as the page already exists in Snapshot 1).</font></p>

<p align="justify"><font size="2" face="Arial"><strong>Summary: If you plan to create one or more Snapshots of your Production Database, please make sure you consider the performance degradation caused by the Snapshots.</strong></font></p>

<p align="justify"><font size="2" face="Arial">In the next post, we will investigate how data is retrieved from Database Snapshots and its effect on the Buffer Pool.</font></p>

<p align="justify"><font size="2" face="Arial"><strong>Additional Reading:</strong></font></p>

<ul>
  <li>
    <div align="justify"><font size="2" face="Arial"><a href="http://msdn.microsoft.com/en-us/library/ms175158.aspx" target="_blank">Database Snapshots on Books OnLine</a>.</font></div>
  </li>

  <li>
    <div align="justify"><font size="2" face="Arial"><a href="http://www.sqlskills.com/BLOGS/PAUL/post/Misconception-around-database-snapshots-and-transaction-rollbacks.aspx" target="_blank">Misconceptions around database snapshots and transaction rollbacks</a>.</font></div>
  </li>

  <li>
    <div align="justify"><font size="2" face="Arial"><a href="http://www.sqlskills.com/BLOGS/PAUL/post/Database-snapshots-when-things-go-wrong.aspx" target="_blank">Database snapshots - when things go wrong</a>.</font><!--EndFragment--></div>
  </li>

  <li>
    <div align="justify"><font size="2" face="Arial"><a href="http://blogs.msdn.com/b/psssql/archive/2008/02/07/how-it-works-sql-server-2005-database-snapshots-replica.aspx" target="_blank">How It Works: SQL Server 2005 Database Snapshots (Replica)</a>.</font></div>
  </li>

  <li>
    <div align="justify"><font size="2" face="Arial"><a href="http://blogs.msdn.com/b/psssql/archive/2008/03/24/how-it-works-shapshot-database-replica-dirty-page-copy-behavior-newpage.aspx" target="_blank">How It Works: Shapshot Database (Replica) Dirty Page Copy Behavior (NewPage)</a>.</font></div>
  </li>

  <li>
    <div align="justify"><font size="2" face="Arial"><a href="http://blogs.msdn.com/b/psssql/archive/2009/01/20/how-it-works-sql-server-sparse-files-dbcc-and-snapshot-databases-revisited.aspx" target="_blank">How It Works: SQL Server Sparse Files (DBCC and Snapshot Databases) Revisited</a>.</font></div>
  </li>
</ul>

<p align="justify"><strong><font face="Arial"><font size="2"><u>Disclaimer</u>: All information provided here is my personal opinion and is neither verified nor approved by Microsoft before it is published. All information, and code samples, if any, is provided &quot;AS IS&quot; with no warranties and confers no rights.</font></font></strong></p>]]></content:encoded>
    </item>
    <item>
      <title>Advanced Performance Tuning – 4 :: Designing for better performance contd…</title>
      <link>https://docs.microsoft.com/archive/blogs/suhde/advanced-performance-tuning-4-designing-for-better-performance-contd</link>
      <pubDate>Fri, 15 Jul 2011 07:46:01 GMT</pubDate>
      <dc:creator><![CDATA[Suhas De]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/suhde/2011/07/15/advanced-performance-tuning-4-designing-for-better-performance-contd/</guid>
      <description><![CDATA[Few months back, I was working with one of my customers, who was engaged by his client to design a...]]></description>
      <content:encoded><![CDATA[<p align="justify"><font size="2" face="Arial">Few months back, I was working with one of my customers, who was engaged by his client to design a Survey Module. Nothing very fancy about the module, but the kind of reports that were to be delivered made it really tricky.</font></p>  <p align="justify"><font size="2" face="Arial">Let me explain the requirements first: When customers click on a Survey Link, they are redirected to a web page. This page has 20 questions; for each question, customers are supposed to provide a rating between 1 and 5, both inclusive. Of course, customers can skip any question they do not want to rate; and finally, there will be a text box where they can enter their comments. Click on Save, and the data gets saved into a database. Pretty straight-forward, isn’t it?</font></p>  <p align="justify"><font size="2" face="Arial">My customer designed the database schema such that the main tables that had the Survey Results was designed as below:</font></p>  <div id="codeSnippetWrapper">   <div id="codeSnippetWrapper">     <pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &#39;Courier New&#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px" id="codeSnippet"><p><span style="color: #0000ff">CREATE</span> <span style="color: #0000ff">TABLE</span> Surveys (<br />    SurveyID   <span style="color: #0000ff">INT</span>       <span style="color: #0000ff">IDENTITY</span>(1,1)  <span style="color: #0000ff">NOT</span> <span style="color: #0000ff">NULL</span>,<br />    RefNo      <span style="color: #0000ff">CHAR</span>(10)                 <span style="color: #0000ff">NOT</span> <span style="color: #0000ff">NULL</span>,<br />    SurveyDate <font color="#0000ff">DATETIME</font>                 <span style="color: #0000ff">NOT</span> <span style="color: #0000ff">NULL</span>,<br />    ...<br />  <span style="color: #0000ff">CONSTRAINT</span> [PK_Surveys] <span style="color: #0000ff">PRIMARY</span> <span style="color: #0000ff">KEY</span> <span style="color: #0000ff">CLUSTERED</span> <br />  (    [SurveyID] <span style="color: #0000ff">ASC</span> ),<br />  <span style="color: #0000ff">CONSTRAINT</span> [CK_RefNo] <span style="color: #0000ff">UNIQUE</span> <span style="color: #0000ff">NONCLUSTERED</span> <br />  (    [RefNo] <span style="color: #0000ff">ASC</span> )<br />)<br /><span style="color: #0000ff">GO</span><br /><br /><span style="color: #0000ff">CREATE</span> <span style="color: #0000ff">TABLE</span> SurveyResults (<br />    SurveyID    <span style="color: #0000ff">INT</span>        <span style="color: #0000ff">NOT</span> <span style="color: #0000ff">NULL</span>,<br />    Answer1     <span style="color: #0000ff">SMALLINT</span>   <span style="color: #0000ff">NULL</span>,<br />    Answer2     <span style="color: #0000ff">INT</span>        <span style="color: #0000ff">NULL</span>,<br />    Answer3     <span style="color: #0000ff">INT</span>        <span style="color: #0000ff">NULL</span>,<br />    Answer4     <span style="color: #0000ff">INT</span>        <span style="color: #0000ff">NULL</span>,<br />    Answer5     <span style="color: #0000ff">INT</span>        <span style="color: #0000ff">NULL</span>,<br />    Answer6     <span style="color: #0000ff">INT</span>        <span style="color: #0000ff">NULL</span>,<br />    Answer7     <span style="color: #0000ff">INT</span>        <span style="color: #0000ff">NULL</span>,<br />    Answer8     <span style="color: #0000ff">INT</span>        <span style="color: #0000ff">NULL</span>,<br />    Answer9     <span style="color: #0000ff">INT</span>        <span style="color: #0000ff">NULL</span>,<br />    Answer10    <span style="color: #0000ff">INT</span>        <span style="color: #0000ff">NULL</span>,<br />    Answer11    <span style="color: #0000ff">INT</span>        <span style="color: #0000ff">NULL</span>,<br />    Answer12    <span style="color: #0000ff">INT</span>        <span style="color: #0000ff">NULL</span>,<br />    Answer13    <span style="color: #0000ff">INT</span>        <span style="color: #0000ff">NULL</span>,<br />    Answer14    <span style="color: #0000ff">INT</span>        <span style="color: #0000ff">NULL</span>,<br />    Answer15    <span style="color: #0000ff">INT</span>        <span style="color: #0000ff">NULL</span>,<br />    Answer16    <span style="color: #0000ff">INT</span>        <span style="color: #0000ff">NULL</span>,<br />    Answer17    <span style="color: #0000ff">INT</span>        <span style="color: #0000ff">NULL</span>,<br />    Answer18    <span style="color: #0000ff">INT</span>        <span style="color: #0000ff">NULL</span>,<br />    Answer19    <span style="color: #0000ff">INT</span>        <span style="color: #0000ff">NULL</span>,<br />    Answer20    <span style="color: #0000ff">INT</span>        <span style="color: #0000ff">NULL</span> )<br /><span style="color: #0000ff">GO</span><br /><br /><span style="color: #0000ff">CREATE</span> <span style="color: #0000ff">TABLE</span> SurveyComments (<br />    SurveyID    <span style="color: #0000ff">INT</span>             <span style="color: #0000ff">NOT</span> <span style="color: #0000ff">NULL</span>,<br />    Comments    <span style="color: #0000ff">VARCHAR</span>(500)    <span style="color: #0000ff">NULL</span> )<br />GO</p></pre>

    <br /><font size="2" face="Arial">I must say, the design was very impressive, and, added to that, appropriate indexes made the Reporting data retrieval very fast.</font></div>
</div>

<p align="justify"><font size="2" face="Arial">And then, one fine day, their client asked them to design a set of new Reports; they coded the stored procedures and queries, but unfortunately, they were unable to get these queries perform as expected. Some of the requirements that were shared with me were:</font></p>

<ul>
  <li>
    <div align="justify"><font size="2" face="Arial">List of Surveys where customers had rated 4 or 5 in at least 10 questions.</font></div>
  </li>

  <li>
    <div align="justify"><font size="2" face="Arial">List of Surveys where customers had rated 2 or less in at most 5 questions.</font></div>
  </li>

  <li>
    <div align="justify"><font size="2" face="Arial">List of Surveys where customers had rated 5 or 1 in all questions (possibly junk surveys).</font></div>
  </li>

  <li>
    <div align="justify"><font size="2" face="Arial">List of Surveys where the average rating was 3 or more.</font></div>
  </li>
</ul>

<p align="justify"><font size="2" face="Arial">With this design, the developer had to push the data into a Temp Table and then apply all sorts of logic to the data in the Temp Table to get the desired output. This not only took a lot of time, but also increased I/O and CPU utilization. At this point, they came to me for help.</font></p>

<p align="justify"><font size="2" face="Arial">After carefully studying and analysing the situation, I came to the conclusion that with the existing design and code, further optimization was nearly impossible. We could have worked on the code, but I was not sure if we could achieve the benchmarks that were already set. I suggested a change in the schema of the <strong>SurveyResults</strong> table. My suggestion of the schema was as follows:</font></p>

<div>
  <pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &#39;Courier New&#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px" id="codeSnippet"><span style="color: #0000ff">CREATE</span> <span style="color: #0000ff">TABLE</span> SurveyResults (<br />    SurveyID     <span style="color: #0000ff">INT</span>        <span style="color: #0000ff">NOT</span> <span style="color: #0000ff">NULL</span>,<br />    QuestionID   <span style="color: #0000ff">SMALLINT</span>   <span style="color: #0000ff">NOT</span> <span style="color: #0000ff">NULL</span>,<br />    Answer       <span style="color: #0000ff">SMALLINT</span>   <span style="color: #0000ff">NULL</span> )<br /><font color="#0000ff">GO</font></pre>
</div>

<p align="justify"><font size="2" face="Arial">With this design, each Survey would be saved as 20 rows, per question. Of course, this design change warranted change in all the reporting queries; however, given the complexity of Reporting requirements, this was probably the most appropriate change. The developers made all the necessary code changes, data was transformed, and we tested, tested and tested all over again; and with all necessary Indexes in place, we were able to provide the expected performance.</font></p>

<p align="justify"><font size="2" face="Arial">This exercise, clearly demonstrated how, a schema design can affect or improve query performance.</font></p>

<p align="justify"><strong><font face="Arial"><font size="2"><font color="#000000"><u>Disclaimer</u>: All information provided here is my personal opinion and is neither verified nor approved by Microsoft before it is published. All information, and code samples, if any, is provided &quot;AS IS&quot; with no warranties and confers no rights.</font></font></font></strong></p>]]></content:encoded>
    </item>
    <item>
      <title>Advanced Performance Tuning – 3 :: Designing for better performance</title>
      <link>https://docs.microsoft.com/archive/blogs/suhde/advanced-performance-tuning-3-designing-for-better-performance</link>
      <pubDate>Thu, 14 Jul 2011 09:09:16 GMT</pubDate>
      <dc:creator><![CDATA[Suhas De]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/suhde/2011/07/14/advanced-performance-tuning-3-designing-for-better-performance/</guid>
      <description><![CDATA[If you have read my earlier post, you might be saying “Heck! This guy loves to post questions...]]></description>
      <content:encoded><![CDATA[<p align="justify"><font size="2" face="Arial">If you have read my <a href="http://blogs.msdn.com/b/suhde/archive/2011/07/13/advanced-performance-tuning-2-which-side-of-the-operator.aspx" target="_blank">earlier post</a>, you might be saying “Heck! This guy loves to post questions without providing proper answers!” Believe me, that is not my intention. My intention here is to help you understand common mistakes that people make, so that you can avoid them in your projects.</font></p>  <p align="justify"><font size="2" face="Arial">The biggest challenge for a SQL Server Developer is to write efficient code. The expectations are that the code must complete in the least possible time, produce correct results and use minimum resources possible. </font><font size="2" face="Arial">We all know that there are various ways to tune slow performing queries – Indexes (with their varieties and flavours), Query Hints, PlanGuides; you name it! </font></p>  <p align="justify"><font size="2" face="Arial">This is, however, not the intention behind this post. </font><font size="2" face="Arial">My intention here is to emphasize the importance of Database Design on the Query Performance. All of us understand and agree that bad code can never provide us good performance; however, we seem to overlook that good (read: Excellent!) code working on a poorly designed database can never provide us the Performance we expect. Most organizations that I have worked with, have Architect Teams and Developer Teams; however, it is unfortunate that these teams work in their own silos without much collaboration between them.</font></p>  <p align="justify"><font size="2" face="Arial">Let us consider the example we have seen in my previous post in Scenario 2. The <strong>fn_CalculateTaxPaid </strong>function takes the EmpID as input, and calculates the Tax for that employee. What is the best way to retrieve the list of employees who have paid more than 10,000 USD as Tax? Do I want to write the query as:</font></p>  <div id="codeSnippetWrapper">   <pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &#39;Courier New&#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px" id="codeSnippet"><span style="color: #0000ff">SELECT</span> &lt;Column List&gt;<br /><font color="#0000ff">FROM</font> &lt;Table Name&gt;<br /><font color="#0000ff">WHERE</font> fn_CalculateTaxPaid (EmpID) &gt; 10000</pre>

  <br /></div>

<p align="justify"><font size="2" face="Arial">You might be saying – “Obviously not! You told me <strong>not</strong> to use a function on the Left of the Operator.” But then, what other options do we have?</font></p>

<p align="justify"><font size="2" face="Arial">We do have options; and this is one classic example how performance can benefit from a good database design. I can think of two ways at present, but I am sure that there will be many more ways to achieve the same.</font></p>

<p align="justify"><font color="#ff0000" size="2" face="Arial"><strong>Option 1:</strong></font></p>

<p align="justify"><font size="2" face="Arial">Add a Computed Column to the table which stores the Tax paid by that employee. Now, create an Index on the Computed Column. Definition to add the Computed Column might look like the following:</font></p>

<div id="codeSnippetWrapper">
  <pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &#39;Courier New&#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px" id="codeSnippet"><p><span style="color: #0000ff">ALTER</span> <span style="color: #0000ff">TABLE</span> dbo.EmpSal <br /><span style="color: #0000ff">ADD</span> EmpTAX <span style="color: #0000ff">AS</span> dbo.fn_CalculateTaxPaid(EmpID)<br /><font color="#0000ff">PERSISTED<br />GO</font></p></pre>

  <br /></div>

<p><font size="2" face="Arial">Now, create an Index on the EmpTAX column.</font></p>

<div id="codeSnippetWrapper">
  <pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &#39;Courier New&#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px" id="codeSnippet"><span style="color: #0000ff">CREATE</span> <span style="color: #0000ff">NONCLUSTERED</span> <span style="color: #0000ff">INDEX</span> [IX_EmpTax] <span style="color: #0000ff">ON</span> [dbo].[EmpSal] <br />(<br />    [EmpTAX] <span style="color: #0000ff">ASC</span><br />)<br /><font color="#0000ff">GO</font></pre>

  <br /></div>

<p><font size="2" face="Arial">Now, if the original query is modified as below, can we utilize the Index and make the query faster?</font></p>

<pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &#39;Courier New&#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px" id="codeSnippet"><span style="color: #0000ff">SELECT</span> &lt;Column List&gt;<br /><font color="#0000ff">FROM</font> &lt;Table Name&gt;<br /><font color="#0000ff">WHERE</font> [EmpTAX] &gt; 10000</pre>

<blockquote>
  <p><font size="2" face="Arial"><strong>Important Note:</strong> The article <a href="http://msdn.microsoft.com/en-us/library/ms189292.aspx" target="_blank">Creating Indexes on Computed Columns</a> lists all requirements for defining Indexes on Computed Columns.</font></p>
</blockquote>

<p><font color="#ff0000" size="2" face="Arial"><strong>Option 2:</strong></font></p>

<p align="justify"><font size="2" face="Arial">Let us assume that due to various constraints, you are unable to add an additional column to the table. In that case, we can make use of Indexed Views. We can use the following code to create an Indexed View for our purpose:</font></p>

<div>
  <pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &#39;Courier New&#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px" id="codeSnippet"><p><span style="color: #0000ff">CREATE</span> <span style="color: #0000ff">VIEW</span> [dbo].[vw_EmpSal]<br /><span style="color: #0000ff">WITH</span> <font color="#0000ff">SCHEMABINDING</font> <br /><span style="color: #0000ff">AS</span> <br /><span style="color: #0000ff">SELECT</span> [EmpID], [Salary], dbo.fn_CalculateTaxPaid(EmpID) <span style="color: #0000ff">AS</span> [EmpTAX]<br /><span style="color: #0000ff">FROM</span> dbo.EmpSal<br /><span style="color: #0000ff">GO</span></p><p><span style="color: #0000ff">CREATE</span> <span style="color: #0000ff">UNIQUE</span> <span style="color: #0000ff">CLUSTERED</span> <span style="color: #0000ff">INDEX</span> [IX_EmpID] <span style="color: #0000ff">ON</span> [dbo].[vw_EmpSal] <br />(<br />    [EmpID] <span style="color: #0000ff">ASC</span><br />)<br /><span style="color: #0000ff">GO</span></p><p><span style="color: #0000ff">CREATE</span> <span style="color: #0000ff">NONCLUSTERED</span> <span style="color: #0000ff">INDEX</span> [IX_EmpSal] <span style="color: #0000ff">ON</span> [dbo].[vw_EmpSal] <br />(<br />    [EmpTAX] <span style="color: #0000ff">ASC</span><br />)<br /><font color="#0000ff">GO</font></p></pre>
</div>

<div>&#160;</div>

<blockquote>
  <p><font size="2" face="Arial"><strong>Important Note:</strong> The <a href="http://msdn.microsoft.com/en-us/library/ms191432.aspx" target="_blank">Creating Indexed Views</a> article lists all requirements for creating Indexes Views.</font></p>
</blockquote>

<p align="justify"><font size="2" face="Arial">I hope, you can understand very well how, a great database design can influence Query Performance. A good Database Design is as important as writing good code to support your Business Requirements. There is no better ingredient for success than Great Design couples with Great Code.</font></p>

<p align="justify"><strong><font face="Arial"><font size="2"><u>Disclaimer</u>: All information provided here is my personal opinion and is neither verified nor approved by Microsoft before it is published. All information, and code samples, if any, is provided &quot;AS IS&quot; with no warranties and confers no rights.</font></font></strong></p>]]></content:encoded>
    </item>
    <item>
      <title>Advanced Performance Tuning – 2 :: Which side of the operator</title>
      <link>https://docs.microsoft.com/archive/blogs/suhde/advanced-performance-tuning-2-which-side-of-the-operator</link>
      <pubDate>Wed, 13 Jul 2011 06:14:40 GMT</pubDate>
      <dc:creator><![CDATA[Suhas De]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/suhde/2011/07/13/advanced-performance-tuning-2-which-side-of-the-operator/</guid>
      <description><![CDATA[If you have read my earlier blog post on the Importance of data-types, you might be wondering, “What...]]></description>
      <content:encoded><![CDATA[<p align="justify"><font size="2" face="Arial">If you have read my earlier blog post on the <a href="http://blogs.msdn.com/b/suhde/archive/2010/09/12/advanced-performance-tuning-i-importance-of-data-types.aspx" target="_blank">Importance of data-types</a>, you might be wondering, “What the heck is going on here?”</font></p>  <p align="justify"><font size="2" face="Arial">I had already written in that post the reason behind the slow performance – the EmpCountry column in the Employee table is CHAR(2) which cannot be directly compared with NCHAR(2); hence, SQL Server is forced to CONVERT the EmpCountry column values to NCHAR(2) before it can perform the comparison. Again, SQL Server does not know what the column values will look like after converting the Non-Unicode character values to Unicode. Hence, it converts each column value to Unicode and then compares the result with the value passed. Hence, the Index Scan.</font></p>  <p align="justify"><font size="2" face="Arial">What I want you to note here is the CONVERT_IMPLICIT operation that SQL Server performs; it applies this operation against each value of EmpCountry in the table. Wouldn’t it have been much easier if SQL Server was to convert the @Country parameter to CHAR(2) and then do the comparison? Well, probably… probably not. There is a huge difference between Unicode and Non-Unicode character sets; why will the designers of SQL Server take so much risk to convert the user defined value to another character set and then run into the risk of doing an inappropriate comparison?</font></p>  <p align="justify"><font size="2" face="Arial">Also, the conversion is not always so easy. Ok, lets take some examples.</font></p>  <p align="justify"><font size="2" face="Arial"><strong><font color="#ff0000">Scenario 1:</font></strong></font></p>  <p align="justify"><font color="#000000" size="2" face="Arial">Assume that the CEO of the company wants information about Employees who have joined the Organization less than an year ago. Most of the developers that I have come across, will prefer writing the query as below:</font></p>  <div id="codeSnippetWrapper">   <pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &#39;Courier New&#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px" id="codeSnippet"><span style="color: #0000ff">SELECT</span> &lt;Column List&gt;<br /><font color="#0000ff">FROM</font> &lt;Table Name&gt;<br /><font color="#0000ff">WHERE</font> <font color="#ff00ff">DATEDIFF</font>(d, Join_Date, <font color="#ff00ff">GETDATE</font>()) &lt; 365</pre>

  <br /></div>

<p align="justify"><font size="2" face="Arial">Can this function be Reversed Engineered to get a Join_Date which is exactly 365 days earlier than the Current Date? Well, yes, and its very easy to do so as well. </font></p>

<p align="justify"><font size="2" face="Arial">Will SQL Server do it? No, because SQL Server has not been instructed to do so. Why will SQL Server modify the query [to: <font face="Courier New"><font color="#0000ff">WHERE</font> Join_Date &gt; <font color="#ff00ff">DATEADD</font>(d, –365, <font color="#ff00ff">GETDATE</font>())</font>]? Although it looks like an easy change considering the simplicity of the query, but in real life, queries will not be so simple. Also, such a change in the query can cause implemented Business Logic to change, and there by provide incorrect results.</font></p>

<p align="justify"><font size="2" face="Arial">The result of this query? Obviously a Table Scan will follow (considering that this is a one time query and there will be no Indexes on the Join_Date), and the query will take minutes to complete.</font></p>

<p align="justify"><font color="#ff0000" size="2" face="Arial"><strong>Scenario 2:</strong></font></p>

<p align="justify"><font size="2" face="Arial">Assume that the Head of the Payroll Team wants information about Employees who have paid more than, say, 10,000 USD as Tax. The database has a function <strong>fn_CalculateTaxPaid</strong> that takes the Annual Salary as input and provides the amount of Tax that this accounts to. You will probably now write the query as:</font></p>

<div id="codeSnippetWrapper">
  <pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &#39;Courier New&#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px" id="codeSnippet"><span style="color: #0000ff">SELECT</span> &lt;Column List&gt;<br /><font color="#0000ff">FROM</font> &lt;Table Name&gt;<br /><font color="#0000ff">WHERE</font> fn_CalculateTaxPaid (Annual_Gross) &gt; 10000</pre>

  <br /></div>

<p align="justify"><font size="2" face="Arial"><strong>Think:</strong> Is there a way to Reverse Engineer the <strong>fn_CalculateTaxPaid</strong> function, and calculate the <strong>Annual_Gross</strong> that corresponds to a Tax of 10,000 USD? Probably yes, but is that easy? I do not think so.</font></p>

<p align="justify"><font size="2" face="Arial">Now, lets complicate matters a bit further. Lets say, the <strong>fn_CalculateTaxPaid</strong> function takes the <strong>EmpID</strong> as input, and calculates the Tax based on his/her <strong>Annual_Gross</strong>, <strong>Other_Income</strong>, <strong>Deduction1</strong>, <strong>Deduction2</strong>, etc. Now the Query will look like:</font></p>

<pre style="border-bottom-style: none; text-align: left; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; margin: 0em; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: &#39;Courier New&#39;, courier, monospace; direction: ltr; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px" id="codeSnippet"><span style="color: #0000ff">SELECT</span> &lt;Column List&gt;<br /><font color="#0000ff">FROM</font> &lt;Table Name&gt;<br /><font color="#0000ff">WHERE</font> fn_CalculateTaxPaid (EmpID) &gt; 10000</pre>

<p><font size="2" face="Arial">Now, is there a way to Reverse Engineer the function to get a list of EmpIDs that have paid a Tax more then 10,000 USD? I hope you are getting the point now.</font></p>

<p><font size="2" face="Arial">So, now, what will SQL Server do when it encounters such a query. Obviously, a Table Scan or an Index Scan or a Clustered Index Scan will follow (depending on whether there are supporting Indexes and the type of the Indexes) and the function will be applied against each row of the table, the resulting value will be compared with the input, and the result set formed.</font></p>

<p><font color="#ff0000" size="2" face="Arial"><strong>Conclusion:</strong></font></p>

<p><font size="2" face="Arial">What I am trying to explain in this post is, use of Functions, Calculations, or for that matter, any operation on the Left of the Operator will cause bad execution plans. In Scenario 1, the operator was Greater Than (<strong>&gt;</strong>), while in the second, the operator was Less Than (<strong>&lt;</strong>). And in both cases, we had asked SQL Server to perform an operation to the Left of the Operator, which caused the queries to choose bad execution plans. If we can avoid the use of Functions or Calculations on the Left of the Operator, we can use the existing Indexes to generate a cost effective Execution Plan, there by decreasing the overall Execution Time (which might mean shorter tea-breaks for some – like me <font size="1"><img style="border-bottom-style: none; border-left-style: none; border-top-style: none; border-right-style: none" class="wlEmoticon wlEmoticon-smile" alt="Smile" src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/21/25/metablogapi/2605.wlEmoticon-smile_52AD3839.png" original-url="http://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-21-25-metablogapi/2605.wlEmoticon_2D00_smile_5F00_52AD3839.png" /></font>).</font></p>

<p><strong><font size="2" face="Arial"><font color="#333333"><u></u></font></font></strong></p>

<p><strong><font size="2" face="Arial"><font color="#333333"><u>Disclaimer</u>: All information provided here is my personal opinion and is neither verified nor approved by Microsoft before it is published. All information, and code samples, if any, is provided &quot;AS IS&quot; with no warranties and confers no rights.</font></font></strong></p>]]></content:encoded>
    </item>
    <item>
      <title>Transaction Log Backup Size in Bulk Logged Recovery Model</title>
      <link>https://docs.microsoft.com/archive/blogs/suhde/transaction-log-backup-size-in-bulk-logged-recovery-model</link>
      <pubDate>Wed, 29 Dec 2010 03:29:00 GMT</pubDate>
      <dc:creator><![CDATA[Suhas De]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/suhde/2010/12/29/transaction-log-backup-size-in-bulk-logged-recovery-model/</guid>
      <description><![CDATA[Assume that I have a database in Bulk Logged Recovery Model, and I perform a bulk operation that is...]]></description>
      <content:encoded><![CDATA[<p align="justify"><font size="2" face="Arial">Assume that I have a database in Bulk Logged Recovery Model, and I perform a bulk operation that is minimally logged. Now, if I take a Log Backup, should not my Log Backup File Size be smaller than if I had performed the same operation in Full Recovery Model?</font></p>  <p align="justify"><font size="2" face="Arial">I have seen many people confused by this question. When I say, “No, please do not expect this”, I am thrown back another question – what, then, is the purpose of having the Bulk Logged Recovery Model?</font></p>  <p align="justify"><font size="2" face="Arial">Let’s first have a look at an extract from the Books Online (<a title="http://msdn.microsoft.com/en-us/library/ms189275(v=SQL.90).aspx" href="http://msdn.microsoft.com/en-us/library/ms189275(v=SQL.90).aspx">http://msdn.microsoft.com/en-us/library/ms189275(v=SQL.90).aspx</a>). Note the parts marked in Bold Red below:</font></p>  <p align="justify"><font color="#666666" size="1" face="Verdana">This recovery model bulk logs most bulk operations. It is intended solely as an adjunct to the full recovery model. For certain large-scale bulk operations such as bulk import or index creation, switching temporarily to the bulk-logged recovery model <strong><font color="#ff0000">increases performance and reduces log space consumption</font></strong>. Log backups are still required. Like the full recovery model, the bulk-logged recovery model retains transaction log records until after they are backed up. <strong><font color="#ff0000">The trade-offs are bigger log backups and increased work-loss exposure</font></strong> because the bulk-logged recovery model does not support point-in-time recovery.</font></p>  <p align="justify"><font size="2" face="Arial">Hence, in short, the sole purpose of the Bulk Logged Recovery Model is to reduce Log Space Consumption by minimally logging some bulk activities, there by improving the overall performance of the bulk operations. However, this recovery model will not result in smaller log backups (smaller than in Full Recovery Model).</font></p>  <p align="justify"><font size="2" face="Arial">Before going into further details, let’s do small hands-on. We will create two databases, change their Recovery Models and create two tables in each database:</font></p> <font color="#0000ff" size="2" face="Courier New">   <blockquote>     <p align="justify"><font size="1" face="Verdana">CREATE </font><font size="1"><font face="Verdana"><font color="#0000ff">DATABASE</font></font></font><font color="#000000" size="1" face="Verdana"> FullRecoveryTest          <br /></font><font size="1"><font face="Verdana"><font color="#0000ff">GO              <br />CREATE</font><font color="#000000"> </font><font color="#0000ff">DATABASE</font></font></font><font size="1"><font face="Verdana"><font color="#000000"> BulkLoggedRecoveryTest              <br /></font><font color="#0000ff">GO</font></font></font></p>   </blockquote>    <blockquote>     <p align="justify"><font size="1"><font face="Verdana"><font color="#0000ff">ALTER</font><font color="#000000"> </font><font color="#0000ff">DATABASE</font><font color="#000000"> FullRecoveryTest </font><font color="#0000ff">SET</font><font color="#000000"> </font><font color="#0000ff">RECOVERY</font><font color="#000000"> </font></font></font><font size="1"><font face="Verdana"><font color="#0000ff">FULL              <br />GO               <br />ALTER</font><font color="#000000"> </font><font color="#0000ff">DATABASE</font><font color="#000000"> BulkLoggedRecoveryTest </font><font color="#0000ff">SET</font><font color="#000000"> </font><font color="#0000ff">RECOVERY</font><font color="#000000"> </font></font></font><font color="#0000ff" size="1" face="Verdana">BULK_LOGGED          <br />GO</font></p>      <p align="justify"><font color="#0000ff" size="1" face="Verdana">USE</font><font color="#000000" size="1" face="Verdana"> FullRecoveryTest          <br /></font><font size="1"><font face="Verdana"><font color="#0000ff">GO              <br />CREATE</font><font color="#000000"> </font><font color="#0000ff">TABLE</font><font color="#000000"> Tab1</font><font color="#0000ff"> </font><font color="#808080">(</font><font color="#000000">Col1 </font><font color="#0000ff">INT</font><font color="#000000"> </font><font color="#808080">NOT</font><font color="#000000"> </font><font color="#808080">NULL,</font><font color="#000000"> </font><font color="#000000">Col2 </font><font color="#0000ff">CHAR</font><font color="#808080">(</font><font color="#000000">5</font><font color="#808080">)</font><font color="#000000"> </font><font color="#808080">NOT</font><font color="#000000"> </font><font color="#808080">NULL,</font><font color="#000000"> </font><font color="#000000">Col3 </font><font color="#0000ff">INT</font><font color="#000000"> </font><font color="#808080">NOT</font><font color="#000000"> </font><font color="#808080">NULL,</font><font color="#000000"> </font><font color="#000000">Col4 </font><font color="#0000ff">CHAR</font><font color="#808080">(</font><font color="#000000">7</font><font color="#808080">)</font><font color="#000000"> </font><font color="#808080">NOT</font><font color="#000000"> </font><font color="#808080">NULL</font></font></font><font color="#808080" size="1" face="Verdana">)</font><font color="#008000" size="1" face="Verdana">          <br /></font><font size="1"><font face="Verdana"><font color="#0000ff">GO              <br />CREATE</font><font color="#000000"> </font><font color="#0000ff">TABLE</font><font color="#000000"> Tab2</font><font color="#0000ff"> </font><font color="#808080">(</font><font color="#000000">Col1 </font><font color="#0000ff">INT</font><font color="#000000"> </font><font color="#808080">NOT</font><font color="#000000"> </font><font color="#808080">NULL,</font><font color="#000000"> </font><font color="#000000">Col2 </font><font color="#0000ff">BIGINT</font><font color="#000000"> </font><font color="#808080">NOT</font><font color="#000000"> </font><font color="#808080">NULL,</font><font color="#000000"> </font><font color="#000000">Col3 </font><font color="#0000ff">CHAR</font><font color="#808080">(</font><font color="#000000">200</font><font color="#808080">)</font><font color="#000000"> </font><font color="#808080">NOT</font><font color="#000000"> </font><font color="#808080">NULL,</font><font color="#000000"> </font><font color="#000000">Col4 </font><font color="#0000ff">CHAR</font><font color="#808080">(</font><font color="#000000">1000</font><font color="#808080">)</font><font color="#000000"> </font><font color="#808080">NOT</font><font color="#000000"> </font><font color="#808080">NULL,</font><font color="#000000"> </font><font color="#000000">Col5 </font><font color="#0000ff">CHAR</font><font color="#808080">(</font><font color="#000000">4000</font><font color="#808080">)</font><font color="#000000"> </font><font color="#808080">NOT</font><font color="#000000"> </font><font color="#808080">NULL</font></font></font><font color="#808080" size="1" face="Verdana">)</font><font color="#008000" size="1" face="Verdana">          <br /></font><font color="#0000ff"><font size="1" face="Verdana">GO</font></font> </p>      <p align="justify"><font size="1" face="Verdana"></font></p>      <p align="justify"><font size="1" face="Verdana">USE </font><font color="#000000" size="1" face="Verdana">BulkLoggedRecoveryTest          <br /></font><font size="1"><font face="Verdana"><font color="#0000ff">GO              <br />CREATE</font><font color="#000000"> </font><font color="#0000ff">TABLE</font><font color="#000000"> Tab1</font><font color="#0000ff"> </font><font color="#808080">(</font><font color="#000000">Col1 </font><font color="#0000ff">INT</font><font color="#000000"> </font><font color="#808080">NOT</font><font color="#000000"> </font><font color="#808080">NULL,</font><font color="#000000"> </font><font color="#000000">Col2 </font><font color="#0000ff">CHAR</font><font color="#808080">(</font><font color="#000000">5</font><font color="#808080">)</font><font color="#000000"> </font><font color="#808080">NOT</font><font color="#000000"> </font><font color="#808080">NULL,</font><font color="#000000"> </font><font color="#000000">Col3 </font><font color="#0000ff">INT</font><font color="#000000"> </font><font color="#808080">NOT</font><font color="#000000"> </font><font color="#808080">NULL,</font><font color="#000000"> </font><font color="#000000">Col4 </font><font color="#0000ff">CHAR</font><font color="#808080">(</font><font color="#000000">7</font><font color="#808080">)</font><font color="#000000"> </font><font color="#808080">NOT</font><font color="#000000"> </font><font color="#808080">NULL</font></font></font><font color="#808080" size="1" face="Verdana">)</font><font color="#008000" size="1" face="Verdana">          <br /></font><font size="1"><font face="Verdana"><font color="#0000ff">GO              <br />CREATE</font><font color="#000000"> </font><font color="#0000ff">TABLE</font><font color="#000000"> Tab2</font><font color="#0000ff"> </font><font color="#808080">(</font><font color="#000000">Col1 </font><font color="#0000ff">INT</font><font color="#000000"> </font><font color="#808080">NOT</font><font color="#000000"> </font><font color="#808080">NULL, </font><font color="#000000">Col2 </font><font color="#0000ff">BIGINT</font><font color="#000000"> </font><font color="#808080">NOT</font><font color="#000000"> </font><font color="#808080">NULL, </font><font color="#000000">Col3 </font><font color="#0000ff">CHAR</font><font color="#808080">(</font><font color="#000000">200</font><font color="#808080">)</font><font color="#000000"> </font><font color="#808080">NOT</font><font color="#000000"> </font><font color="#808080">NULL, </font><font color="#000000">Col4 </font><font color="#0000ff">CHAR</font><font color="#808080">(</font><font color="#000000">1000</font><font color="#808080">)</font><font color="#000000"> </font><font color="#808080">NOT</font><font color="#000000"> </font><font color="#808080">NULL, </font><font color="#000000">Col5 </font><font color="#0000ff">CHAR</font><font color="#808080">(</font><font color="#000000">4000</font><font color="#808080">)</font><font color="#000000"> </font><font color="#808080">NOT</font><font color="#000000"> </font><font color="#808080">NULL</font></font></font><font color="#808080" size="1" face="Verdana">)</font><font size="1"><font face="Verdana"><font color="#008000">              <br /></font><font color="#0000ff">GO</font></font></font></p>   </blockquote>    <p align="justify"></p> </font>  <p align="justify"><font color="#000000" size="2" face="Arial">Now, we will take a Full Backup of both the databases and then a Log Backup each. After the Log Backup, we will examine the Log File Size and the Log Space Used for both the databases:</font></p> <font color="#0000ff" size="2" face="Courier New">   <blockquote>     <p align="justify"><font size="1" face="Verdana">USE </font><font face="Verdana"><font size="1"><font color="#0000ff">master              <br />GO               <br />BACKUP</font><font color="#000000"> </font><font color="#0000ff">DATABASE</font><font color="#000000"> FullRecoveryTest </font><font color="#0000ff">TO</font><font color="#000000"> </font><font color="#0000ff">DISK</font><font color="#000000"> </font><font color="#808080">=</font><font color="#000000"> </font></font></font><font color="#ff0000" size="1" face="Verdana">'C:\FullRecoveryTest_Full.bak'          <br /></font><font face="Verdana"><font size="1"><font color="#0000ff">GO              <br />BACKUP</font><font color="#000000"> </font><font color="#ff00ff">LOG</font><font color="#000000"> FullRecoveryTest </font><font color="#0000ff">TO</font><font color="#000000"> </font><font color="#0000ff">DISK</font><font color="#000000"> </font><font color="#808080">=</font><font color="#000000"> </font></font></font><font color="#ff0000" size="1" face="Verdana">'C:\FullRecoveryTest_Log_Before.trn'          <br /></font><font face="Verdana"><font size="1"><font color="#0000ff">GO              <br />BACKUP</font><font color="#000000"> </font><font color="#0000ff">DATABASE</font><font color="#000000"> BulkLoggedRecoveryTest </font><font color="#0000ff">TO</font><font color="#000000"> </font><font color="#0000ff">DISK</font><font color="#000000"> </font><font color="#808080">=</font><font color="#000000"> </font></font></font><font color="#ff0000" size="1" face="Verdana">'C:\BulkLoggedRecoveryTest_Full.bak'          <br /></font><font face="Verdana"><font size="1"><font color="#0000ff">GO              <br />BACKUP</font><font color="#000000"> </font><font color="#ff00ff">LOG</font><font color="#000000"> BulkLoggedRecoveryTest </font><font color="#0000ff">TO</font><font color="#000000"> </font><font color="#0000ff">DISK</font><font color="#000000"> </font><font color="#808080">=</font><font color="#000000"> </font></font></font><font face="Verdana"><font size="1"><font color="#ff0000">'C:\BulkLoggedRecoveryTest_Log_Before.trn'              <br /></font><font color="#0000ff">GO</font></font></font></p>   </blockquote>    <p align="justify"><font color="#000000" face="Arial">Here is the result that we get:</font></p> </font>  <div align="justify">   <table border="1" cellspacing="0" cellpadding="2" width="544"><tbody>       <tr>         <td valign="top" width="153"><font size="1" face="Verdana"><strong>Database Name</strong></font></td>          <td valign="top" width="107"><font size="1" face="Verdana"><strong>Log Size (MB)</strong></font></td>          <td valign="top" width="137"><font size="1" face="Verdana"><strong>Log Space Used (%)</strong></font></td>          <td valign="top" width="145"><font size="1" face="Verdana"><strong>Log Space Used (MB)</strong></font></td>       </tr>        <tr>         <td valign="top" width="153"><font size="1" face="Verdana">FullRecoveryTest</font></td>          <td valign="top" width="107"><font size="1" face="Verdana">0.5546875</font></td>          <td valign="top" width="137"><font size="1" face="Verdana">51.49648</font></td>          <td valign="top" width="145"><font size="1" face="Verdana">0.2856445375</font></td>       </tr>        <tr>         <td valign="top" width="153"><font size="1" face="Verdana">BulkLoggedRecoveryTest</font></td>          <td valign="top" width="107"><font size="1" face="Verdana">0.5546875</font></td>          <td valign="top" width="137"><font size="1" face="Verdana">51.84859</font></td>          <td valign="top" width="145"><font size="1" face="Verdana">0.2875976476</font></td>       </tr>     </tbody></table> </div>  <p align="justify"><font size="2" face="Arial">Ok. Now, we will BCP in rows into the tables in both the databases. We will BCP in the same data set in both the databases, so that after the BCP in, both the databases have identical data. I used the attached files and the following commands:</font></p>  <blockquote>   <p align="justify"><font color="#0000ff" size="1" face="Verdana">bcp FullRecoveryTest.dbo.Tab1 in D:\Tab1.txt -SSQLServerName -T -c -h &quot;TABLOCK&quot;        <br />bcp FullRecoveryTest.dbo.Tab2 in D:\Tab2.txt -SSQLServerName -T -c -h &quot;TABLOCK&quot;         <br />bcp BulkLoggedRecoveryTest.dbo.Tab1 in D:\Tab1.txt -SSQLServerName -T -c -h &quot;TABLOCK&quot;         <br />bcp BulkLoggedRecoveryTest.dbo.Tab2 in D:\Tab2.txt -SSQLServerName -T -c -h &quot;TABLOCK&quot;</font></p> </blockquote>  <div align="justify">   <div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: none; padding-top: 0px" id="scid:fb3a1972-4489-4e52-abe7-25a00bb07fdf:756f708e-08c1-4b4c-b392-7f556be41a23" class="wlWriterEditableSmartContent"><p>BCP In File For Table1 : <a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/21/25/metablogapi/8524.Tab2_3A95C306.txt" original-url="http://blogs.msdn.com/cfs-file.ashx/__key/CommunityServer-Blogs-Components-WeblogFiles/00-00-01-21-25-metablogapi/8524.Tab2_5F00_3A95C306.txt" target="_blank">BCPInFileForTab1</a><br />BCP In File For Table2 : <a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/21/25/metablogapi/7853.Tab1_6C5CF229.txt" original-url="http://blogs.msdn.com/cfs-file.ashx/__key/CommunityServer-Blogs-Components-WeblogFiles/00-00-01-21-25-metablogapi/7853.Tab1_5F00_6C5CF229.txt" target="_blank">BCPInFileForTab2</a></p></div> </div>  <p align="justify"><font size="2" face="Arial">Now, before we take another set of Log Backups, lets examine the Log Space Usage:</font></p>  <div align="justify">   <table border="1" cellspacing="0" cellpadding="2" width="543"><tbody>       <tr>         <td valign="top" width="152"><font size="1" face="Verdana"><strong>Database Name</strong></font></td>          <td valign="top" width="107"><font size="1" face="Verdana"><strong>Log Size (MB)</strong></font></td>          <td valign="top" width="140"><font size="1" face="Verdana"><strong>Log Space Used (%)</strong></font></td>          <td valign="top" width="142"><font size="1" face="Verdana"><strong>Log Space Used (MB)</strong></font></td>       </tr>        <tr>         <td valign="top" width="152"><font size="1" face="Verdana">FullRecoveryTest</font></td>          <td valign="top" width="107"><font size="1" face="Verdana">2.054688</font></td>          <td valign="top" width="140"><font size="1" face="Verdana">88.26046</font></td>          <td valign="top" width="142"><font size="1" face="Verdana">1.8134770803648</font></td>       </tr>        <tr>         <td valign="top" width="152"><font size="1" face="Verdana">BulkLoggedRecoveryTest</font></td>          <td valign="top" width="107"><font size="1" face="Verdana">0.5546875</font></td>          <td valign="top" width="140"><font size="1" face="Verdana">72.53521</font></td>          <td valign="top" width="142"><font size="1" face="Verdana">0.40234374296875</font></td>       </tr>     </tbody></table> </div>  <p align="justify"><font size="2" face="Arial">We can clearly see that under the Bulk Logged Recovery Model, only <strong>0.11474609536875 MB</strong> of Log Space has been used for the entire operation, where as under Full Recovery Model, <strong>1.5278325428648 MB</strong> of Log Space has been used (almost 13.5 times the Log Space in Bulk Logged Recovery Model).</font></p>  <p align="justify"><font size="2" face="Arial">Does this mean that the size of the Log Backup of the BulkLoggedRecoveryTest database will be smaller than that of the FullRecoveryTest database? Let’s see:</font></p> <font color="#0000ff" size="2" face="Courier New">   <blockquote>     <p align="justify"><font size="1" face="Verdana">USE </font><font face="Verdana"><font size="1"><font color="#0000ff">master              <br />GO               <br />BACKUP</font><font color="#000000"> </font><font color="#ff00ff">LOG</font><font color="#000000"> FullRecoveryTest </font><font color="#0000ff">TO</font><font color="#000000"> </font><font color="#0000ff">DISK</font><font color="#000000"> </font><font color="#808080">=</font><font color="#000000"> </font></font></font><font color="#ff0000" size="1" face="Verdana">'C:\FullRecoveryTest_Log_After.trn'          <br /></font><font face="Verdana"><font size="1"><font color="#0000ff">GO              <br />BACKUP</font><font color="#000000"> </font><font color="#ff00ff">LOG</font><font color="#000000"> BulkLoggedRecoveryTest </font><font color="#0000ff">TO</font><font color="#000000"> </font><font color="#0000ff">DISK</font><font color="#000000"> </font><font color="#808080">=</font><font color="#000000"> </font></font></font><font face="Verdana"><font size="1"><font color="#ff0000"><font color="#ff0000">'</font>C:\BulkLoggedRecoveryTest_Log_After.trn<font color="#ff0000">'</font>               <br /></font><font color="#0000ff">GO</font></font></font></p>   </blockquote>    <blockquote>     <p align="justify"><font color="#000000">FullRecoveryTest_Log_After.trn&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; : 1,667,584 bytes          <br />BulkLoggedRecoveryTest_Log_After.trn&#160;&#160;&#160;&#160;&#160;&#160;&#160; : 1,856,000 bytes</font></p>   </blockquote> </font>  <p align="justify"><font color="#000000" size="2" face="Arial">Strange, isn’t it? The answer is again in the Books Online (<a title="http://msdn.microsoft.com/en-us/library/ms190692(v=SQL.90).aspx" href="http://msdn.microsoft.com/en-us/library/ms190692(v=SQL.90).aspx">http://msdn.microsoft.com/en-us/library/ms190692(v=SQL.90).aspx</a>):</font></p>  <p align="justify"><font color="#666666" size="1" face="Verdana">Under the bulk-logged recovery model, if a log backup covers any bulk operations, the <strong><font color="#ff0000">log backup contains both log records and the data pages that were changed by bulk operations</font></strong>. This is necessary to capture the results of the bulk-logged operations. The incorporated data extents can make a log backup very large. Additionally, backing up the log requires access to the data files that contain the bulk-logged transactions. If any affected database file is inaccessible, the transaction log cannot be backed up and all operations committed in that log are lost.</font></p>  <p align="justify"><font color="#666666" size="1" face="Verdana">To track the data pages, a log backup operation relies on a bulk-changes bitmap page that contains a bit for every extent. For each extent updated by a bulk-logged operation since the last log backup, the bit is set to 1 in the bitmap. The data extents are copied into the log followed by the log data. The following illustration shows how a log backup is constructed.</font></p>  <p align="justify"><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/21/25/metablogapi/1462.LogBackupBulkLogged_3E92F114.jpg" original-url="http://blogs.msdn.com/cfs-file.ashx/__key/CommunityServer-Blogs-Components-WeblogFiles/00-00-01-21-25-metablogapi/1462.LogBackupBulkLogged_5F00_3E92F114.jpg"><img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="LogBackupBulkLogged" border="0" alt="LogBackupBulkLogged" src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/21/25/metablogapi/4572.LogBackupBulkLogged_thumb_2EA3AC50.jpg" original-url="http://blogs.msdn.com/cfs-file.ashx/__key/CommunityServer-Blogs-Components-WeblogFiles/00-00-01-21-25-metablogapi/4572.LogBackupBulkLogged_5F00_thumb_5F00_2EA3AC50.jpg" width="338" height="241" /></a></p>  <p align="justify"><font size="2" face="Arial">The proof of the above theory is in the output for the BACKUP LOG commands. For the FullRecoveryTest database, the output of the BACKUP LOG command is:</font></p>  <blockquote>   <p><font size="2" face="Courier New">Processed 191 pages for database 'FullRecoveryTest', file 'FullRecoveryTest_log' on file 1.       <br />BACKUP LOG successfully processed 191 pages in 0.941 seconds (1.579 MB/sec).</font></p> </blockquote>  <p><font size="2" face="Arial">However, for the BulkLoggedRecoveryTest database, the output of the command is:</font></p>  <blockquote>   <p><font size="2"><font face="Courier New"><font style="background-color: #ffff00">Processed 208 pages for database 'BulkLoggedRecoveryTest', file 'BulkLoggedRecoveryTest' on file 1.</font>          <br />Processed 15 pages for database 'BulkLoggedRecoveryTest', file 'BulkLoggedRecoveryTest_log' on file 1.          <br />BACKUP LOG successfully processed 223 pages in 0.830 seconds (2.095 MB/sec).</font></font></p> </blockquote>  <p><font size="2" face="Arial"><strong>Notice that 208 pages from the DataFile of the BulkLoggedRecoveryTest database has been backed up in the Transaction Log Backup.</strong></font></p>  <p><font size="2" face="Arial"><strong>Lesson learnt:</strong> We cannot expect the Transaction Log Backup Size to be smaller even if the Database is in Bulk Logged Recovery Model. This recovery model is for better <strong>Transaction Log Management</strong>, not for better Transaction Log Backup Management. Those who have Log Shipping configured, and hope to benefit from Bulk Logged Recovery model while performing maintenance tasks – this is <strong>not</strong> good news for you.</font></p>  <p align="justify"><strong><font size="2"><font face="Arial"><font color="#808080"><u>Disclaimer</u>: All information provided here is my personal opinion and is neither verified nor approved by Microsoft before it is published. All information, and code samples, if any, is provided &quot;AS IS&quot; with no warranties and confers no rights.</font></font></font></strong></p>]]></content:encoded>
    </item>
    <item>
      <title>Advanced Performance Tuning – 1 :: Importance of data-types</title>
      <link>https://docs.microsoft.com/archive/blogs/suhde/advanced-performance-tuning-1-importance-of-data-types</link>
      <pubDate>Sun, 12 Sep 2010 03:10:00 GMT</pubDate>
      <dc:creator><![CDATA[Suhas De]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/suhde/2010/09/12/advanced-performance-tuning-1-importance-of-data-types/</guid>
      <description><![CDATA[Why did we develop DBMS systems? Not just for storing data &ndash; but also to be able to search for...]]></description>
      <content:encoded><![CDATA[<p align="justify"><span style="font-family: Arial; font-size: x-small;">Why did we develop DBMS systems? </span><span style="font-family: Arial; font-size: x-small;">Not just for storing data &ndash; but also to be able to search for and retrieve stored data, and we define what our search criteria should be.</span></p>
<p align="justify"><span style="font-family: Arial; font-size: x-small;">This was probably true a couple of decades back, but we have come a long way now. With the huge advancement of technology and the computing powers being doubled probably a few hundred times, we are now not satisfied with just being able to retrieve stored data; we want to retrieve data faster, we want our operations to complete in the least amount of time possible.</span></p>
<p align="justify"><span style="font-family: Arial; font-size: x-small;">All of us are aware of the fact that queries must be tuned to achieve optimal performance, some of the </span><span style="font-family: Arial; font-size: x-small;">common ways of tuning database systems include creating appropriate indexes, updating statistics, rebuilding indexes to reduce fragmentation. However, sometimes we tend to forget that each database system is just another piece of code running on a bunch of dumb hardware; unless we design our databases properly, unless we write efficient code, we will never be able to achieve the speeds we desire. In this and subsequent posts, I will try to explain the most common mistakes that I have experienced, and how these can affect system performance.</span></p>
<p align="justify"><span style="font-family: Arial; font-size: x-small;">In this post, I will try to explain the importance of appropriate data-types. To start off, let&rsquo;s create a new table and populate it with data:</span></p>
<div>
<pre id="codeSnippet" style="text-align: left; line-height: 12pt; background-color: #f4f4f4; margin: 0em; width: 100%; font-family: 'Courier New', courier, monospace; direction: ltr; color: black; font-size: 8pt; overflow: visible; border-style: none; padding: 0px;"><span style="color: #0000ff">CREATE</span> <span style="color: #0000ff">TABLE</span> [dbo].[Employee](<br />       [EmpID] [<span style="color: #0000ff">int</span>] <span style="color: #0000ff">IDENTITY</span>(1,1) <span style="color: #0000ff">NOT</span> <span style="color: #0000ff">NULL</span>,<br />       [EmpFName] [<span style="color: #0000ff">varchar</span>](50) <span style="color: #0000ff">NOT</span> <span style="color: #0000ff">NULL</span>,<br />       [EmpMName] [<span style="color: #0000ff">char</span>](2) <span style="color: #0000ff">NULL</span>,<br />       [EmpLName] [<span style="color: #0000ff">varchar</span>](50) <span style="color: #0000ff">NULL</span>,<br />       [EmpSal] [<span style="color: #0000ff">numeric</span>](18, 2) <span style="color: #0000ff">NOT</span> <span style="color: #0000ff">NULL</span>,<br />       [EmpAddress] [<span style="color: #0000ff">varchar</span>](250) <span style="color: #0000ff">NULL</span>,<br />       [EmpCountry] [<span style="color: #0000ff">char</span>](2) <span style="color: #0000ff">NOT</span> <span style="color: #0000ff">NULL</span>,<br /><span style="color: #0000ff">CONSTRAINT</span> [PK_Employee] <span style="color: #0000ff">PRIMARY</span> <span style="color: #0000ff">KEY</span> <span style="color: #0000ff">CLUSTERED</span> <br />([EmpID] <span style="color: #0000ff">ASC</span>))<br />GO</pre>
</div>
<p><span style="font-family: Arial; font-size: x-small;">We now, insert 1,000,000 rows of data into the table. </span><span style="font-family: Arial; font-size: x-small;">We have the following Stored Procedure:</span></p>
<div id="codeSnippetWrapper">
<pre id="codeSnippet" style="text-align: left; line-height: 12pt; background-color: #f4f4f4; margin: 0em; width: 100%; font-family: 'Courier New', courier, monospace; direction: ltr; color: black; font-size: 8pt; overflow: visible; border-style: none; padding: 0px;"><span style="color: #0000ff">CREATE</span> <span style="color: #0000ff">PROCEDURE</span> sp_GetAvgSal<br />    @Country NCHAR(2)<br /><span style="color: #0000ff">AS</span><br />    <span style="color: #0000ff">BEGIN</span><br />        <span style="color: #0000ff">SELECT</span> <span style="color: #0000ff">AVG</span>(EmpSal) <span style="color: #0000ff">FROM</span> Employee <span style="color: #0000ff">WHERE</span> EmpCountry = @Country<br />    END</pre>
</div>
<p><span style="font-family: Arial; font-size: x-small;">We will execute this Stored Procedure and see how much time it takes:</span></p>
<div>
<pre id="codeSnippet" style="text-align: left; line-height: 12pt; background-color: #f4f4f4; margin: 0em; width: 100%; font-family: 'Courier New', courier, monospace; direction: ltr; color: black; font-size: 8pt; overflow: visible; border-style: none; padding: 0px;"><span style="color: #0000ff">SET</span> <span style="color: #0000ff">STATISTICS IO </span><span style="color: #0000ff">ON</span><br /><span style="color: #0000ff">SET</span> <span style="color: #0000ff">STATISTICS</span> <span style="color: #0000ff">TIME</span> <span style="color: #0000ff">ON</span><br /><span style="color: #0000ff">SET</span> <span style="color: #0000ff">STATISTICS PROFILE </span><span style="color: #0000ff">ON</span><br /><span style="color: #0000ff">GO</span><br /><span style="color: #0000ff">EXEC</span> dbo.sp_GetAvgSal @Country = <span style="color: #ff0000">'IN'</span><br />GO</pre>
</div>
<p><span style="font-family: Arial; font-size: x-small;">It takes ages for the query to return back the results. Finally, when it does give us back the results, here is what we get:</span></p>
<div id="codeSnippetWrapper">
<pre id="codeSnippet" style="text-align: left; line-height: 12pt; background-color: #f4f4f4; margin: 0em; width: 100%; font-family: 'Courier New', courier, monospace; direction: ltr; color: black; font-size: 8pt; overflow: visible; border-style: none; padding: 0px;"><p>SQL Server Execution Times:<br />   CPU time = 10093 ms,  elapsed time = 282344 ms.</p></pre>
</div>
<p align="justify"><span style="font-family: Arial; font-size: x-small;">We can clearly see that there are lots of Reads, and we know why &ndash; there is no Index on the EmpCountry column. Hence, the Clustered Index is being Scanned for getting us back the results. Great! Since we know the source of the problem, let&rsquo;s create the index to speed things up&hellip;</span></p>
<div>
<pre id="codeSnippet" style="text-align: left; line-height: 12pt; background-color: #f4f4f4; margin: 0em; width: 100%; font-family: 'Courier New', courier, monospace; direction: ltr; color: black; font-size: 8pt; overflow: visible; border-style: none; padding: 0px;"><p><span style="color: #0000ff">CREATE</span> <span style="color: #0000ff">NONCLUSTERED</span> <span style="color: #0000ff">INDEX</span> [IX_Employee_Country] <span style="color: #0000ff">ON</span> [dbo].[Employee]<br />   ([EmpCountry] <span style="color: #0000ff">ASC</span>) <span style="color: #0000ff">INCLUDE</span> ([EmpSal])</p></pre>
</div>
<p align="justify"><span style="font-family: Arial; font-size: x-small;">We execute the same Stored Procedure with the same parameters again. We get better results now; but not quite to our expectations.</span></p>
<div>
<pre id="codeSnippet" style="text-align: left; line-height: 12pt; background-color: #f4f4f4; margin: 0em; width: 100%; font-family: 'Courier New', courier, monospace; direction: ltr; color: black; font-size: 8pt; overflow: visible; border-style: none; padding: 0px;">SQL Server Execution Times:<br />   CPU time = 202 ms,  elapsed time = 216 ms<span style="color: #ff0000">.</span></pre>
</div>
<p align="justify"><span style="font-family: Arial; font-size: x-small;">What&rsquo;s going on? </span><span style="font-family: Arial; font-size: x-small;">We have a proper Index, still we are not able to achieve the performance we deserve. Let&rsquo;s have a look at the execution plan. A section of the plan is:</span></p>
<div>
<pre id="codeSnippet" style="text-align: left; line-height: 12pt; background-color: #f4f4f4; margin: 0em; width: 100%; font-family: 'Courier New', courier, monospace; direction: ltr; color: black; font-size: 8pt; overflow: visible; border-style: none; padding: 0px;">StmtText                                                                                <br />----------------------------------------------------------------------------------------<br />SELECT AVG([EmpSal]) FROM [Employee] WHERE [EmpCountry]=@1                              <br />  |--Compute Scalar(DEFINE:([Expr1003]=CASE WHEN [Expr1005]=(0) THEN NULL ELSE [Expr1006<br />       |--Stream Aggregate(DEFINE:([Expr1005]=Count(*), [Expr1006]=SUM([Test].[dbo].[Emp<br />            |--Index Scan(OBJECT:([Test].[dbo].[Employee].[IX_Employee_Country])</pre>
</div>
<p align="justify"><span style="font-family: Arial; font-size: x-small;">An Index Scan? Why is the Index [IX_Employee_Country] being scanned? Shouldn&rsquo;t we see an Index Seek? The answer lies in the same execution plan. Let&rsquo;s have a look at that branch:</span></p>
<div>
<pre id="codeSnippet" style="text-align: left; line-height: 12pt; background-color: #f4f4f4; margin: 0em; width: 100%; font-family: 'Courier New', courier, monospace; direction: ltr; color: black; font-size: 8pt; overflow: visible; border-style: none; padding: 0px;">Index Scan(OBJECT:([Test].[dbo].[Employee].[IX_Employee_Country]),<br />&nbsp;&nbsp; WHERE:(CONVERT_IMPLICIT(nchar(2),[Test].[dbo].[Employee].[EmpCountry],0)=[@1])) </pre>
</div>
<p align="justify"><span style="font-family: Arial; font-size: x-small;">The EmpCountry column in the Employee table is CHAR(2) which cannot be directly compared with NCHAR(2); hence, SQL Server is forced to CONVERT the EmpCountry column values to NCHAR(2) before it can perform the comparison. Again, SQL Server does not know what the column values will look like after converting the Non-Unicode character values to Unicode. Hence, it converts each column value to Unicode and then compares the result with the value passed. Hence, the Index Scan.</span></p>
<p align="justify"><span style="font-family: Arial; font-size: x-small;">How can we eliminate the Index Scan? Easy, isn&rsquo;t it? Rewrite the Stored Procedure so that the @Country parameter data-type matches the data-type of the underlying table. We did this, and do you know what the result was?</span></p>
<div>
<pre id="codeSnippet" style="text-align: left; line-height: 12pt; background-color: #f4f4f4; margin: 0em; width: 100%; font-family: 'Courier New', courier, monospace; direction: ltr; color: black; font-size: 8pt; overflow: visible; border-style: none; padding: 0px;"><p>SQL Server Execution Times:<br />   CPU time = 0 ms,  elapsed time = 1 ms.</p><p>StmtText                                                                                <br />----------------------------------------------------------------------------------------<br />SELECT AVG([EmpSal]) FROM [Employee] WHERE [EmpCountry]=@1                              <br />  |--Compute Scalar(DEFINE:([Expr1003]=CASE WHEN [Expr1004]=(0) THEN NULL ELSE [Expr1005<br />       |--Stream Aggregate(DEFINE:([Expr1004]=Count(*), [Expr1005]=SUM([Test].[dbo].[Emp<br />            |--Index Seek(OBJECT:([Test].[dbo].[Employee].[IX_Employee_Country])</p></pre>
</div>
<p align="justify"><span style="font-family: Arial; font-size: x-small;"><strong>So what did we learn?</strong> While doing comparison, you must make sure that the data-types used in the underlying tables match the data-types of the data we are comparing to. This will make sure we eliminate the IMPLICIT CONVERSIONS and thus, we can utilize the Indexes in a much better way.</span></p>
<p align="justify"><strong><span style="font-size: x-small;"><span style="font-family: Arial;"><span style="color: #808080;"><span style="text-decoration: underline;">Disclaimer</span>: All information provided here is my personal opinion and is neither verified nor approved by Microsoft before it is published. All information, and code samples, if any, is provided "AS IS" with no warranties and confers no rights.</span></span></span></strong></p>]]></content:encoded>
    </item>
    <item>
      <title>INF: How to re-setup Database Mirroring In SQL Server</title>
      <link>https://docs.microsoft.com/archive/blogs/suhde/inf-how-to-re-setup-database-mirroring-in-sql-server</link>
      <pubDate>Wed, 25 Aug 2010 10:08:04 GMT</pubDate>
      <dc:creator><![CDATA[Suhas De]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/suhde/2010/08/25/inf-how-to-re-setup-database-mirroring-in-sql-server/</guid>
      <description><![CDATA[One of my friends was asking this to me offline the other day:   &quot;I have Database in SQL...]]></description>
      <content:encoded><![CDATA[<p><font face="Arial" size="2">One of my friends was asking this to me offline the other day: </font></p>  <p align="justify"><font face="Arial" size="2">&quot;I have Database in SQL Server, which is both Mirrored as well as Log Shipped. Log backups happen every 15 minutes; Mirroring happens in Synchronous mode and does not involve a witness. I want to break Mirroring (Remove Mirroring) and then want to re-setup the Mirroring configuration after about 30 minutes; what is the easiest way to achieve this?&quot;</font></p>  <p align="justify"><font face="Arial" size="2">I constructed a small diagram for better understanding:     <br /></font><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/21/25/metablogapi/2671.Blog_6F5F9EAB.png" original-url="http://blogs.msdn.com/cfs-file.ashx/__key/CommunityServer-Blogs-Components-WeblogFiles/00-00-01-21-25-metablogapi/2671.Blog_5F00_6F5F9EAB.png"><font face="Arial" color="#333333" size="2"><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/21/25/metablogapi/4857.Blog_4125A2F1.png" original-url="http://blogs.msdn.com/cfs-file.ashx/__key/CommunityServer-Blogs-Components-WeblogFiles/00-00-01-21-25-metablogapi/4857.Blog_5F00_4125A2F1.png"><img title="Blog" style="border-right: 0px; border-top: 0px; display: inline; margin-left: 0px; border-left: 0px; margin-right: 0px; border-bottom: 0px" height="276" alt="Blog" src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/21/25/metablogapi/7462.Blog_thumb_57984178.png" original-url="http://blogs.msdn.com/cfs-file.ashx/__key/CommunityServer-Blogs-Components-WeblogFiles/00-00-01-21-25-metablogapi/7462.Blog_5F00_thumb_5F00_57984178.png" width="427" border="0" /></a></font>      <br /></a><font face="Arial" size="2">I said, &quot;What is the size of the database?&quot; The answer that came was, &quot;More than 1 TB. Don't tell me you want me to start with a Full Backup; it's not feasible. It takes more than 8 hours to backup the database; and more than 6 hours to restore it, even if we use Instant File Initialization.&quot;</font></p>  <p align="justify"><font face="Arial" size="2">&quot;Well&quot;, I said, &quot;try restoring the first Transaction Log Backup (with NoRecovery) that was taken after the Mirroring was removed. If the Principal and Mirror were completely synchronized when Mirroring was removed (i.e., Unsent Log = 0 KB), it will get restored successfully. If they were not synchronized (Unsent Log &gt; 0 KB), you will be prompted to restore an earlier backup. Once you get the starting point, restore all Transaction Log Backups taken in sequence (all with NoRecovery). Now reconfigure Database Mirroring again.&quot;</font></p>  <p align="justify"><font face="Arial" size="2">My friend did not believe this can be done.</font></p>  <p align="justify"><font face="Arial" size="2"><strong>Question to my readers: Do you believe this is possible? Well, give it a try…</strong></font></p>  <p><strong><font face="ari" size="2"><font color="#808080"><u>Disclaimer</u>: All information provided here is my personal opinion and is neither verified nor approved by Microsoft before it is published. All information, and code samples, if any, is provided &quot;AS IS&quot; with no warranties and confers no rights.</font></font></strong></p>]]></content:encoded>
    </item>
    <item>
      <title>INF: Automatic Identity Range Management with Merge Replication in Microsoft SQL Server 2005 / 2008</title>
      <link>https://docs.microsoft.com/archive/blogs/suhde/inf-automatic-identity-range-management-with-merge-replication-in-microsoft-sql-server-2005-2008</link>
      <pubDate>Sun, 21 Mar 2010 23:41:37 GMT</pubDate>
      <dc:creator><![CDATA[Suhas De]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/suhde/2010/03/21/inf-automatic-identity-range-management-with-merge-replication-in-microsoft-sql-server-2005-2008/</guid>
      <description><![CDATA[I have been working with SQL Server Replication for over 4 years now; and till now each case that I...]]></description>
      <content:encoded><![CDATA[<p align="justify"><font face="Arial" size="2">I have been working with SQL Server Replication for over 4 years now; and till now each case that I work on gives me something new to learn. At the same time, every case amazes me by the intricate design that has gone into implementation of SQL Server Replication. With the wonderful GUI design, replication seems so easy to configure and monitor; however, when I start troubleshooting an issue and I look into the metadata to understand the cause of the issue, I am faced with new challenges every day.</font></p>  <p align="justify"><font face="Arial" size="2">One such technology is the Automatic Identity Range Management. Have you ever imagined how SQL Server Replication manages the identity values of user tables? If your user table is a part of Transactional Replication with Updatable Subscriptions or the table is replicated using Merge Replication, how does Replication ensure that different values are inserted at the Publisher and the Subscribers, so that conflicts arising off using same identity values can be avoided? Let’s see...</font></p>  <p align="justify"><font face="Arial" size="2">Let’s create a table that has an identity column:</font></p>  <div id="codeSnippetWrapper">   <pre id="codeSnippet" style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; direction: ltr; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: &#39;Courier New&#39;, courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; text-align: left; border-bottom-style: none"><span style="color: #0000ff">CREATE</span> <span style="color: #0000ff">TABLE</span> [dbo].[Orders]<br />(<br />    OrderID         <span style="color: #0000ff">INT</span>             <span style="color: #0000ff">IDENTITY</span>(1, 1)   <font color="#808080"><span style="color: #0000ff">NOT</span> <span style="color: #0000ff">NULL</span></font>,<br />    OrderDate       <font color="#0000ff">DATETIME</font>                         <span style="color: #0000ff">NOT</span> <span style="color: #0000ff">NULL</span>,<br />    CustomerName    <span style="color: #0000ff">VARCHAR</span>(100)                     <span style="color: #0000ff">NOT</span> <span style="color: #0000ff">NULL</span>,<br />    ProductName     <span style="color: #0000ff">VARCHAR</span>(100)                     <span style="color: #0000ff">NOT</span> <span style="color: #0000ff">NULL</span>,<br />    OrderQuantity   <span style="color: #0000ff">INT</span>                              <span style="color: #0000ff">NOT</span> <span style="color: #0000ff">NULL</span>,<br />    UnitPrice       <span style="color: #0000ff">DECIMAL</span>(10, 2)                   <span style="color: #0000ff">NOT</span> <span style="color: #0000ff">NULL</span><br />)</pre>

  <br /></div>

<p align="justify"><font face="Arial" size="2">We will now publish this table using Merge Replication. We set the Publisher Identity Range as 10000 and the Subscriber Range as 1000. We will also create a subscription to this publication. For demonstration purposes, our publication will have just this one article. Also, at this point, the table is completely empty. After the initial sync, we ran the following query against both the Published Database and the Subscribed Database:</font></p>

<div id="codeSnippetWrapper">
  <pre id="codeSnippet" style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; direction: ltr; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: &#39;Courier New&#39;, courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; text-align: left; border-bottom-style: none"><span style="color: #0000ff">SELECT</span> <font color="#ff00ff">IDENT_CURRENT</font> (<span style="color: #ff0000">'dbo.Orders'</span>)</pre>
</div>

<div>&#160;</div>

<div align="justify"><font face="Arial" size="2">Against the Published Database, we got <strong>1</strong> as the output, and against the Subscribed Database we got <strong>2002</strong> as the output. This means that the next record inserted at the Publisher will have the identity value <strong>1</strong>, while that inserted at the Subscriber will have a identity value of <strong>2002</strong>. This leads us to the following questions: 

    <br />a) On what basis is the identity value at the Publisher and the Subscriber decided? 

    <br />b) How does replication implement the identity values? 

    <br />c) What will happen if the current identity value at the Publisher exceeds 2001? 

    <br />d) How does replication know what identity values have been assigned?</font></div>

<div align="justify"><font face="Arial" size="2"></font></div>

<div align="justify"><font face="Arial" size="2"></font></div>

<p align="justify"><strong><font face="Arial" size="2">Question: On what basis is the identity value at the Publisher and the Subscriber decided?</font></strong></p>

<p align="justify"><font face="Arial" size="2">When we configure replication, we have to set the Publisher Identity Range and the Subscriber Identity Range. These range values are by default set to 10000 and 1000 respectively. This means that the Publisher will be assigned an identity range of 10000 (primary) + 10000 (secondary) for distribution. Of these, the Publisher will keep 1000 (primary) + 1000 (secondary) identity values for inserts on the Publisher. Among the rest, 1000 (primary) + 1000 (secondary) identity values will be assigned to the subscriber for inserts at the subscriber. The next subscriber will be assigned the next 1000 (primary) + 1000 (secondary) identity values.</font></p>

<p align="justify"><strong><font face="Arial" size="2">Question: How does replication implement the identity values?</font></strong></p>

<p align="justify"><font face="Arial" size="2">SQL Server Replication implements the identity values in the form of constraints that are applied on the corresponding tables. If we examine the Orders table on the Publisher, we will find a constraint by the name of <strong><em>repl_identity_range_&lt;GUID&gt;</em></strong> created. The GUID is based on the value of the <strong>artid</strong> column for the article in the <strong>sysmergearticles</strong> system table. The same constraint is created on the Subscriber as well. The expression for this constraint on the Publisher and the Subscriber are as follows:</font></p>

<p align="justify"><font size="2"><font face="Arial"><strong>Publisher:</strong> 

      <br />([OrderID]&gt;=(1) AND [OrderID]&lt;=(1001) OR [OrderID]&gt;(1001) AND [OrderID]&lt;=(2001)) 

      <br /></font></font><font size="2"><font face="Arial"><strong>Subscriber: 
        <br /></strong>([OrderID]&gt;(2001) AND [OrderID]&lt;=(3001) OR [OrderID]&gt;(3001) AND [OrderID]&lt;=(4001))</font></font></p>

<p align="justify"><strong><font face="Arial" size="2">Question: What will happen if the current identity value at the Publisher exceeds 2001?</font></strong></p>

<p align="justify"><font face="Arial" size="2">Once rows are entered into the Publisher Database and the Primary and the Secondary identity ranges are completely utilized, 2 new ranges are assigned to the Publisher. Hence, when the identity value on the Publisher reaches 2001, and we try to insert the 2002th value, the constraint is modified on the Publisher. The expression now becomes: 
    <br />([OrderID]&gt;(4001) AND [OrderID]&lt;=(5001) OR [OrderID]&gt;(5001) AND [OrderID]&lt;=(6001))</font></p>

<p align="justify"><font face="Arial" size="2">However, the behavior on the subscriber is different. On the subscriber, if both the assigned identity ranges are completely utilized, further inserts will fail, unless synchronized with the Publisher. When the subscriber synchronizes, new identity ranges are assigned to the subscriber. If the primary range is completely utilized, during synchronization, the secondary range is made the primary range, and a new identity range is assigned, which now becomes the secondary identity range.</font></p>

<p align="justify"><font face="Arial" size="2">For SQL Server CE subscribers or subscribers running SQL Server 2000 or an earlier version, only one identity range can be assigned. Here the <strong>threshold</strong> parameter comes into play. If the identity range usage is above the <strong>threshold (default value 80%)</strong>, a new identity range is assigned. Thus holes in the identity values can be created. For example, on the SQL CE subscriber, if we have the identity range set to ([OrderID]&gt;(4001) AND [OrderID]&lt;=(5001)), and we are currently at 4850, it means that we have utilized more than the threshold value; while synchronization, a new identity range is assigned. If now, the identity range is like ([OrderID]&gt;(5001) AND [OrderID]&lt;=(6001)), it means that the next identity value that will be assigned to the next new row is 5002; thus identity values between 4851 and 5001 are skipped, leaving holes in the identity values. Higher the <strong>threshold</strong>, smaller is the hole, the less fault-tolerant the system becomes.</font></p>

<p align="justify"><font face="Arial" size="2"><strong>Question: How does replication know what identity values have been assigned?</strong></font></p>

<p align="justify"><font face="Arial" size="2">Starting SQL Server 2005, </font><font face="Arial" size="2">the <strong>MSmerge_identity_range</strong> system table on the Published Database has information about the current identity ranges in use. It has one row per article for the Publisher Identity Range, one row per article for the Identity Range being used by the Publisher, and one row per article for each Subscriber. The following is a screenshot of the contents of this table on the publisher:</font></p>

<p align="justify"><a href="https://msdnshared.blob.core.windows.net/media/TNBlogsFS/BlogFileStorage/blogs_msdn/suhde/WindowsLiveWriter/INFAutomaticIdentityRangeManagementwithM_12C02/Publisher_MSmerge_identity_range_2.png" original-url="http://blogs.msdn.com/blogfiles/suhde/WindowsLiveWriter/INFAutomaticIdentityRangeManagementwithM_12C02/Publisher_MSmerge_identity_range_2.png"><font face="Arial" color="#000000" size="2"><img title="Publisher_MSmerge_identity_range" style="border-top-width: 0px; display: inline; border-left-width: 0px; border-bottom-width: 0px; border-right-width: 0px" height="72" alt="Publisher_MSmerge_identity_range" src="https://msdnshared.blob.core.windows.net/media/TNBlogsFS/BlogFileStorage/blogs_msdn/suhde/WindowsLiveWriter/INFAutomaticIdentityRangeManagementwithM_12C02/Publisher_MSmerge_identity_range_thumb.png" original-url="http://blogs.msdn.com/blogfiles/suhde/WindowsLiveWriter/INFAutomaticIdentityRangeManagementwithM_12C02/Publisher_MSmerge_identity_range_thumb.png" width="530" border="0" /></font></a><font face="Arial" size="2"> </font></p>

<p align="justify"><font face="Arial" size="2">The <strong>MSmerge_identity_range</strong> system table on the Subscribed Database, however, has one per row per article and has information about the identity range used by itself for that article.</font></p>

<p align="justify"><font face="Arial" size="2">Similar to previous versions of SQL Server, we also keep the history of all the identity range allocations in the <strong>MSmerge_identity_range_allocations</strong> system table in the distribution database. The following is a screenshot of the contents of this table:</font></p>

<p align="justify"><a href="https://msdnshared.blob.core.windows.net/media/TNBlogsFS/BlogFileStorage/blogs_msdn/suhde/WindowsLiveWriter/INFAutomaticIdentityRangeManagementwithM_12C02/MSmerge_identity_range_allocations_2.png" original-url="http://blogs.msdn.com/blogfiles/suhde/WindowsLiveWriter/INFAutomaticIdentityRangeManagementwithM_12C02/MSmerge_identity_range_allocations_2.png"><img title="MSmerge_identity_range_allocations" style="border-top-width: 0px; display: inline; border-left-width: 0px; border-bottom-width: 0px; border-right-width: 0px" height="33" alt="MSmerge_identity_range_allocations" src="https://msdnshared.blob.core.windows.net/media/TNBlogsFS/BlogFileStorage/blogs_msdn/suhde/WindowsLiveWriter/INFAutomaticIdentityRangeManagementwithM_12C02/MSmerge_identity_range_allocations_thumb.png" original-url="http://blogs.msdn.com/blogfiles/suhde/WindowsLiveWriter/INFAutomaticIdentityRangeManagementwithM_12C02/MSmerge_identity_range_allocations_thumb.png" width="534" border="0" /></a>&#160; <br /><font face="Courier New" color="#ff0000" size="1">(Click on the image to enlarge)</font></p>

<p><strong><font face="Arial" size="2">Distributing Identity Ranges to Multiple Subscribers:</font></strong></p>

<p align="justify"><font face="Arial" size="2">Now that we know how identity rages are assigned and kept track of, lets see how the identity ranges will look like when we have multiple subscribers. Let’s assume that database <strong><em>PubDB</em></strong> of the SQL Server Instance <strong><em>Publisher</em></strong> is published and is subscribed by <strong><em>Subscriber1</em></strong>, <strong><em>Subscriber2</em></strong>, <strong><em>Subscriber3</em></strong> and <strong><em>Subscriber4</em></strong>. The identity range allocations are as follows:</font></p>

<table cellspacing="0" cellpadding="2" width="530" border="1"><tbody>
    <tr>
      <td valign="top" width="112"><strong>Server Name</strong></td>

      <td valign="top" width="163"><strong>Primary Range</strong></td>

      <td valign="top" width="131"><strong>Secondary Range</strong></td>

      <td valign="top" width="122"><strong>Comments</strong></td>
    </tr>

    <tr>
      <td valign="top" width="112"><strong>Publisher</strong></td>

      <td valign="top" width="163"><strong>1 to (max value *)</strong></td>

      <td valign="top" width="131"><strong>-</strong></td>

      <td valign="top" width="122"><strong>Publisher Range</strong></td>
    </tr>

    <tr>
      <td valign="top" width="112">Publisher</td>

      <td valign="top" width="163">1 to 1001</td>

      <td valign="top" width="131">1001 to 2001</td>

      <td valign="top" width="122">Subscriber Range</td>
    </tr>

    <tr>
      <td valign="top" width="112">Subscriber1</td>

      <td valign="top" width="163">2001 to 3001</td>

      <td valign="top" width="131">3001 to 4001</td>

      <td valign="top" width="122">Subscriber Range</td>
    </tr>

    <tr>
      <td valign="top" width="112">Subscriber2</td>

      <td valign="top" width="163">4001 to 5001</td>

      <td valign="top" width="131">5001 to 6001</td>

      <td valign="top" width="122">Subscriber Range</td>
    </tr>

    <tr>
      <td valign="top" width="112">Subscriber3</td>

      <td valign="top" width="163">6001 to 7001</td>

      <td valign="top" width="131">7001 to 8001</td>

      <td valign="top" width="122">Subscriber Range</td>
    </tr>

    <tr>
      <td valign="top" width="112">Subscriber4</td>

      <td valign="top" width="163">8001 to 9001</td>

      <td valign="top" width="131">9001 to 10001</td>

      <td valign="top" width="122">Subscriber Range</td>
    </tr>
  </tbody></table>

<blockquote>
  <p align="justify"><strong><font size="2"><font face="Arial"><font color="#800000">* max value for INT datatype is <a href="http://msdn.microsoft.com/en-us/library/ms187745.aspx" target="_blank">&quot;2147483647&quot;</a><strong>.</strong></font></font></font></strong></p>
</blockquote>

<p><strong><font face="Arial" size="2">Distributing Identity Ranges in a Re-Publishing Scenario:</font></strong></p>

<p align="justify"><font face="Arial" size="2">In a republishing scenario, the re-publisher will also be assigned a Publisher Range, which will be used by it for distributing identity ranges to its subscribers. Lets consider the following scenario: <strong><em>Publisher</em> </strong>is the main publisher, with a Publisher Identity Range of 10000 and Subscriber Identity Range of 1000, and its subscribers are <strong><em>Subscriber1</em></strong>, <strong><em>Subscriber2</em></strong>, <strong><em>Subscriber3</em></strong>, and <strong><em>RePublisher</em></strong>. The database on <strong><em>RePublisher</em></strong> is published again with a Publisher Identity Range of 7500 and Subscriber Identity Range of 500. The subscribers of <strong><em>RePublisher</em></strong> are <strong><em>SubSubscriber1</em></strong> and <strong><em>SubSubscriber2</em></strong>.</font></p>

<p align="justify"><font face="Arial" size="2">The chart of identity range assignments will be as follows:</font></p>

<table cellspacing="0" cellpadding="2" width="530" border="1"><tbody>
    <tr>
      <td valign="top" width="112"><strong>Server Name</strong></td>

      <td valign="top" width="163"><strong>Primary Range</strong></td>

      <td valign="top" width="131"><strong>Secondary Range</strong></td>

      <td valign="top" width="122"><strong>Comments</strong></td>
    </tr>

    <tr>
      <td valign="top" width="112"><strong>Publisher</strong></td>

      <td valign="top" width="163"><strong>1 to (<strong>max value *</strong>)</strong></td>

      <td valign="top" width="131"><strong>-</strong></td>

      <td valign="top" width="122"><strong>Publisher Range</strong></td>
    </tr>

    <tr>
      <td valign="top" width="112">Publisher</td>

      <td valign="top" width="163">1 to 1001</td>

      <td valign="top" width="131">1001 to 2001</td>

      <td valign="top" width="122">Subscriber Range</td>
    </tr>

    <tr>
      <td valign="top" width="112">Subscriber1</td>

      <td valign="top" width="163">2001 to 3001</td>

      <td valign="top" width="131">3001 to 4001</td>

      <td valign="top" width="122">Subscriber Range</td>
    </tr>

    <tr>
      <td valign="top" width="112">Subscriber2</td>

      <td valign="top" width="163">4001 to 5001</td>

      <td valign="top" width="131">5001 to 6001</td>

      <td valign="top" width="122">Subscriber Range</td>
    </tr>

    <tr>
      <td valign="top" width="112">Subscriber3</td>

      <td valign="top" width="163">6001 to 7001</td>

      <td valign="top" width="131">7001 to 8001</td>

      <td valign="top" width="122">Subscriber Range</td>
    </tr>

    <tr>
      <td valign="top" width="112">RePublisher</td>

      <td valign="top" width="163">8001 to 9001</td>

      <td valign="top" width="131">9001 to 10001</td>

      <td valign="top" width="122">Subscriber Range</td>
    </tr>

    <tr>
      <td valign="top" width="112"><strong>RePublisher</strong></td>

      <td valign="top" width="163"><strong>10001 to 17501</strong></td>

      <td valign="top" width="131"><strong>17501 to 25001</strong></td>

      <td valign="top" width="122"><strong>Publisher Range</strong></td>
    </tr>

    <tr>
      <td valign="top" width="112">SubSubscriber1</td>

      <td valign="top" width="163">10001 to 10501</td>

      <td valign="top" width="131">10501 to 11001</td>

      <td valign="top" width="122">Subscriber Range</td>
    </tr>

    <tr>
      <td valign="top" width="112">SubSubscriber2</td>

      <td valign="top" width="163">11001 to 11501</td>

      <td valign="top" width="131">11501 to 12001</td>

      <td valign="top" width="122">Subscriber Range</td>
    </tr>
  </tbody></table>

<blockquote>
  <p align="justify"><strong><font size="2"><font face="Arial"><font color="#800000">* max value for INT datatype is <a href="http://msdn.microsoft.com/en-us/library/ms187745.aspx" target="_blank">&quot;2147483647&quot;</a><strong>.</strong></font></font></font></strong></p>
</blockquote>

<p align="justify"><font face="Arial" size="2">So, we can clearly see that <strong><em>RePublisher</em></strong> is assigned two Publisher Identity Ranges (primary range and secondary range) and all ranges assigned to its subscribers are from these ranges only.</font></p>

<p align="justify"><font face="Arial" size="2"><strong>What if we reinitialize the Subscriptions?</strong></font></p>

<p align="justify"><font face="Arial" size="2">If we reinitialize the subscriptions, each time we reinitialize, new identity ranges are assigned to the corresponding subscribers. This is irrespective of the fact whether the identity ranges already assigned to the subscribers are depleted. This has the potential to create holes in the identity values in the table and can even cause the assignable identity ranges to get depleted. Let’s see using an example.</font></p>

<p align="justify"><font face="Arial" size="2">I have a database that has the Orders Table, similar to the one defined earlier, with a small change. My identity column is now <strong>smallint</strong> instead of <strong>int</strong>. I have created the publication using 5000 as the Publisher Range and 2000 as the Subscriber Range. Once I create the subscriptions, I have the following identity values assigned:</font></p>

<table cellspacing="0" cellpadding="2" width="530" border="1"><tbody>
    <tr>
      <td valign="top" width="132"><strong>Server Name</strong></td>

      <td valign="top" width="132"><strong>Primary Range</strong></td>

      <td valign="top" width="132"><strong>Secondary Range</strong></td>

      <td valign="top" width="132"><strong>Comments</strong></td>
    </tr>

    <tr>
      <td valign="top" width="132"><strong>Publisher</strong></td>

      <td valign="top" width="132"><strong>1 to 32767</strong></td>

      <td valign="top" width="132"><strong>-</strong></td>

      <td valign="top" width="132"><strong>Publisher Range</strong></td>
    </tr>

    <tr>
      <td valign="top" width="132">Publisher</td>

      <td valign="top" width="132">1 to 2001</td>

      <td valign="top" width="132">2001 to 4001</td>

      <td valign="top" width="132">Subscriber Range</td>
    </tr>

    <tr>
      <td valign="top" width="132">Subscriber1</td>

      <td valign="top" width="132">4001 to 6001</td>

      <td valign="top" width="132">6001 to 8001</td>

      <td valign="top" width="132">Subscriber Range</td>
    </tr>

    <tr>
      <td valign="top" width="132">Subscriber2</td>

      <td valign="top" width="132">8001 to 10001</td>

      <td valign="top" width="132">10001 to 12001</td>

      <td valign="top" width="132">Subscriber Range</td>
    </tr>

    <tr>
      <td valign="top" width="132">Subscriber3</td>

      <td valign="top" width="132">12001 to 14001</td>

      <td valign="top" width="132">14001 to 16001</td>

      <td valign="top" width="132">Subscriber Range</td>
    </tr>

    <tr>
      <td valign="top" width="132">Subscriber4</td>

      <td valign="top" width="132">16001 to 18001</td>

      <td valign="top" width="132">18001 to 20001</td>

      <td valign="top" width="132">Subscriber Range</td>
    </tr>
  </tbody></table>

<p align="justify"><font face="Arial" size="2">Now, I reinitialize all the subscriptions. In this case, the Publisher Identity Range (1 to 2001 &amp; 2001 to 4001) is not changed, however, each subscriber is assigned 2 new ranges. This is irrespective of the fact that I have not inserted any row in any of the databases. So, the Merge Agent starts off with the assignment:</font></p>

<p align="justify"><font face="Arial" size="2">Subscriber1: 20001 to 22001 &amp; 22001 to 24001 
    <br />Subscriber2: 24001 to 26001 &amp; 26001 to 28001 

    <br />Subscriber3: 28001 to 30001 &amp; 30001 to 32001 

    <br /><font color="#ff0000">Subscriber4: 32001 to 34001… Oops!!!</font></font></p>

<p align="justify"><font face="Arial" size="2">The max value for <strong>smallint</strong> is 32767, and the Merge Agent has tried to assign an identity range that is above the max value of <strong>smallint</strong>. This allocation will now fail, and the Merge Agent will fail with the following message:</font></p>

<div id="codeSnippetWrapper">
  <pre id="codeSnippet" style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; direction: ltr; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: &#39;Courier New&#39;, courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; text-align: left; border-bottom-style: none"><p><span style="color: #ff0000">The Publisher failed to allocate a new set of identity ranges for the <br /></span><span style="color: #ff0000">subscription. This can occur when a Publisher or a republishing Subscriber<br />has run out of identity ranges to allocate to its own Subscribers or when<br />an identity column data type does not support an additional identity range<br />allocation. If a republishing Subscriber has run out of identity ranges,<br />synchronize the republishing Subscriber to obtain more identity ranges<br />before restarting the synchronization. If a Publisher runs out of identit<br />(Source: MSSQL_REPL, Error number: MSSQL_REPL-2147199417)<br />Get help: <a href="http://help/MSSQL_REPL-2147199417">http://help/MSSQL_REPL-2147199417</a></span></p></pre>

  <br /></div>

<p></p>

<p><font face="Arial" size="2"><strong>What can I do to resolve this error?</strong></font></p>

<p align="justify"><font face="ari" size="2">To resolve this error, you can try to drop the Subscriptions and Publication, and create them once again. If the last inserted row has a low value in the identity column, this recreating everything will give you relief for some time.</font></p>

<p align="justify"><font face="ari" size="2">However, if the last inserted identity row value is high, and close to the max value for <strong>smallint</strong>, recreating the Publication and the Subscriptions will be of no good. The only way out in such cases is to change the datatype of the Identity Column to a larger datatype (<strong>int</strong> or <strong>bigint</strong>).</font></p>

<p align="justify"><font face="ari" size="2">Hope this helps my readers to understand the concepts of Automatic Identity Range management in Merge Replication using Microsoft SQL Server 2005 / 2008.</font></p>

<p align="justify"><strong><font color="#808080"><font size="2"><font face="ari"><u>Disclaimer</u>: All information provided here is my personal opinion and is neither verified nor approved by Microsoft before it is published. All information, and code samples, if any, is provided &quot;AS IS&quot; with no warranties and confers no rights.</font></font></font></strong></p>]]></content:encoded>
    </item>
  </channel>
</rss>