<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>&#8235;Madeira &#187; Shay Attiya&#8236;</title>	<atom:link href="http://www.madeira.co.il/author/shay/feed/" rel="self" type="application/rss+xml" />
	<link>http://www.madeira.co.il</link>
	<description>&#8235;SQL Server Services&#8236;</description>	<lastBuildDate>Sat, 19 May 2012 09:04:40 +0000</lastBuildDate>
	<generator>http://wordpress.org/?v=2.9.2</generator>
	<language>he</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
			<item>
		<title>&#8235;Foreign keys impact on inserts&#8236;</title>		<link>http://www.madeira.co.il/foreign-keys-impact-on-inserts/</link>
		<comments>http://www.madeira.co.il/foreign-keys-impact-on-inserts/#comments</comments>
		<pubDate>Thu, 08 Mar 2012 18:57:23 +0000</pubDate>
		<dc:creator>&#8235;Shay Attiya&#8236;</dc:creator>				<category><![CDATA[בלוגים]]></category>
		<category><![CDATA[כללי]]></category>
		<category><![CDATA[מקודמות]]></category>
		<category><![CDATA[Foreign key]]></category>
		<category><![CDATA[Insert]]></category>
		<category><![CDATA[sql]]></category>

		<guid isPermaLink="false">http://www.madeira.co.il/?p=4570</guid>
		<description><![CDATA[&#8235;Foreign keys have a significant impact on inserts. Why is that, and how can we go around it ?&#8236;]]></description>			<content:encoded><![CDATA[<div dir="rtl"><p><img src='http://www.madeira.co.il/wp-content/plugins/simple-post-thumbnails/timthumb.php?src=/wp-content/thumbnails/4570.png&amp;w=214&amp;h=129&amp;zc=1&amp;ft=jpg' alt='post thumbnail' /></p>
<p dir="ltr">In my previous <a href="http://www.madeira.co.il/foreign-keys-and-how-they-help-the-optimizer/">post</a>, I've demonstrated how Foreign keys can help the optimizer make better decisions when creating an execution plan. However, I've also mentioned that FKs make inserts and updates run slower. In this short post I'll show the actual impact the FK has on these actions.</p>
<p dir="ltr"><strong>Why does the insert run slower ?</strong></p>
<p dir="ltr">A foreign key is basically a constraint. It limits the data in the referencing column to a list of values found in the referenced table. This makes SQL Server check the data before inserting it, to make sure the constraint is not violated. This check takes time, but how much time ?</p>
<p dir="ltr">To demonstrate I've created 2 tables: NameList (populated with 100 Ids and names) and DataTable that will reference the NameList table.</p>
<p dir="ltr">At first I've created the DataTable without the FK.</p>
<div style="text-align: left;line-height: 12pt;background-color: #f4f4f4;margin: 20px 0px 10px;width: 97.5%;font-family: 'Courier New', courier, monospace;direction: ltr;font-size: 8pt;overflow: auto;cursor: text;border: silver 1px solid;padding: 4px">
<pre 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: #008000">-- Create the list table</span>
<span style="color: #0000ff">CREATE</span> <span style="color: #0000ff">TABLE</span>
    dbo.NameList
(
    Id        <span style="color: #0000ff">INT</span>            <span style="color: #0000ff">NOT</span> <span style="color: #0000ff">NULL</span> ,
    Name    <span style="color: #0000ff">VARCHAR</span>(10)    <span style="color: #0000ff">NOT</span> <span style="color: #0000ff">NULL</span> ,

    <span style="color: #0000ff">CONSTRAINT</span> pk_NameId_c <span style="color: #0000ff">PRIMARY</span> <span style="color: #0000ff">KEY</span> <span style="color: #0000ff">CLUSTERED</span> (Id)
);

INSERT <span style="color: #0000ff">INTO</span>
    dbo.NameList(Id, Name)
<span style="color: #0000ff">SELECT</span> <span style="color: #0000ff">TOP</span> 100
    ROW_NUMBER()<span style="color: #0000ff">OVER</span>(<span style="color: #0000ff">ORDER</span> <span style="color: #0000ff">BY</span> T1.name) ,
    <span style="color: #006080">'Name_'</span>+<span style="color: #0000ff">CAST</span>(ROW_NUMBER()<span style="color: #0000ff">OVER</span>(<span style="color: #0000ff">ORDER</span> <span style="color: #0000ff">BY</span> T1.name) <span style="color: #0000ff">AS</span> <span style="color: #0000ff">VARCHAR</span>(4))
<span style="color: #0000ff">FROM</span>
    sys.all_columns <span style="color: #0000ff">AS</span> T1
<span style="color: #0000ff">CROSS</span> <span style="color: #0000ff">JOIN</span>
    sys.all_columns <span style="color: #0000ff">AS</span> T2;
<span style="color: #0000ff">GO</span>

<span style="color: #008000">-- Create data table</span>
<span style="color: #0000ff">CREATE</span> <span style="color: #0000ff">TABLE</span>
    dbo.DataTable
(
    NameId <span style="color: #0000ff">INT</span> <span style="color: #0000ff">NOT</span> <span style="color: #0000ff">NULL</span>
);
<span style="color: #0000ff">GO</span></pre>
<pre style="text-align: left;line-height: 12pt;background-color: #f4f4f4;width: 100%;font-family: 'Courier New', courier, monospace;direction: ltr;font-size: 8pt;border-style: none;padding: 0px;margin: 0em"><span style="color: #0000ff">
</span></pre>
</div>
<p dir="ltr">I've also created a third table (RandomData) from which data will be selected into the DataTable. This table contains 1,000,000 records that match the values in the list table.</p>
<div style="text-align: left;line-height: 12pt;background-color: #f4f4f4;margin: 20px 0px 10px;width: 97.5%;font-family: 'Courier New', courier, monospace;direction: ltr;font-size: 8pt;overflow: auto;cursor: text;border: silver 1px solid;padding: 4px">
<pre 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: #008000">-- Create random data</span>
<span style="color: #0000ff">SELECT</span> <span style="color: #0000ff">TOP</span> 1000000
    ABS(CHECKSUM(NEWID())%100)+1 <span style="color: #0000ff">AS</span> RandId
<span style="color: #0000ff">INTO</span>
    dbo.RandomData
<span style="color: #0000ff">FROM</span>
    sys.all_columns <span style="color: #0000ff">AS</span> T1
<span style="color: #0000ff">CROSS</span> <span style="color: #0000ff">JOIN</span>
    sys.all_columns <span style="color: #0000ff">AS</span> T2;
<span style="color: #0000ff">GO</span></pre>
</div>
<p dir="ltr"><strong>Inserting data without a FK:</strong></p>
<p dir="ltr">First lets see how long it takes to insert 1,000,000 records into a table without a FK. To test this, I've run an insert statement 10 times and measured the AVG run time.</p>
<div style="text-align: left;line-height: 12pt;background-color: #f4f4f4;margin: 20px 0px 10px;width: 97.5%;font-family: 'Courier New', courier, monospace;direction: ltr;font-size: 8pt;overflow: auto;cursor: text;border: silver 1px solid;padding: 4px">
<pre 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: #008000">-- Insert with out a FK and check time</span>
<span style="color: #0000ff">IF</span> <span style="color: #0000ff">EXISTS</span> (<span style="color: #0000ff">SELECT</span> * <span style="color: #0000ff">FROM</span> sys.foreign_keys
            <span style="color: #0000ff">WHERE</span> object_id = OBJECT_ID(N<span style="color: #006080">'dbo.fk_DataList'</span>)
            <span style="color: #0000ff">AND</span> parent_object_id = OBJECT_ID(N<span style="color: #006080">'dbo.DataTable'</span>))
<span style="color: #0000ff">BEGIN</span>
    <span style="color: #0000ff">ALTER</span> <span style="color: #0000ff">TABLE</span> dbo.DataTable
    <span style="color: #0000ff">DROP</span> <span style="color: #0000ff">CONSTRAINT</span> fk_DataList;
<span style="color: #0000ff">END</span>;
<span style="color: #0000ff">GO</span>

<span style="color: #0000ff">DECLARE</span>
    @StartTime    DATETIME2 ,
    @EndTime    DATETIME2 ,
    @i            <span style="color: #0000ff">INT</span> = 1,
    @n            <span style="color: #0000ff">INT</span> = 10;

<span style="color: #0000ff">TRUNCATE</span> <span style="color: #0000ff">TABLE</span> dbo.DataTable;

<span style="color: #0000ff">SET</span> @StartTime  = SYSDATETIME();

<span style="color: #0000ff">WHILE</span> @i &lt;= @n
<span style="color: #0000ff">BEGIN</span>
    INSERT <span style="color: #0000ff">INTO</span> dbo.DataTable (NameId)
    <span style="color: #0000ff">SELECT</span> RandId <span style="color: #0000ff">FROM</span> dbo.RandomData;

    <span style="color: #0000ff">SET</span> @i+=1;
<span style="color: #0000ff">END</span>;

<span style="color: #0000ff">SET</span> @EndTime = SYSDATETIME();

<span style="color: #0000ff">SELECT</span> Avg_Insert_Time = <span style="color: #0000ff">CAST</span>(<span style="color: #0000ff">CAST</span>(DATEDIFF(MS,@StartTime,@EndTime) <span style="color: #0000ff">AS</span> <span style="color: #0000ff">DECIMAL</span>)/1000/@n <span style="color: #0000ff">AS</span> <span style="color: #0000ff">DECIMAL</span>(7,3));
<span style="color: #0000ff">GO</span></pre>
</div>
<p dir="ltr">I run this test 12 time, and eliminated the best and worst results. The average run time was 4.68 sec.</p>
<p dir="ltr">The execution plan for the insert was very simple.</p>
<p dir="ltr"><a href="http://www.madeira.co.il/wp-content/uploads/2012/03/No_FK1.jpg" rel="wp-prettyPhoto[g4570]"><img class="alignleft size-full wp-image-4573" src="http://www.madeira.co.il/wp-content/uploads/2012/03/No_FK1.jpg" alt="" width="584" height="160" /></a></p>
<p dir="ltr">
<p dir="ltr">
<p dir="ltr">
<p dir="ltr">
<p dir="ltr">
<p dir="ltr"><strong>Inserting with a FK:</strong></p>
<p dir="ltr">I run the same test, only this time I've created a FK before.</p>
<div style="text-align: left;line-height: 12pt;background-color: #f4f4f4;margin: 20px 0px 10px;width: 97.5%;font-family: 'Courier New', courier, monospace;direction: ltr;font-size: 8pt;overflow: auto;cursor: text;border: silver 1px solid;padding: 4px">
<pre 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: #008000">-- Add the FK</span>
<span style="color: #0000ff">TRUNCATE</span> <span style="color: #0000ff">TABLE</span> dbo.DataTable;

<span style="color: #0000ff">ALTER</span> <span style="color: #0000ff">TABLE</span> dbo.DataTable
<span style="color: #0000ff">ADD</span> <span style="color: #0000ff">CONSTRAINT</span> fk_DataList <span style="color: #0000ff">FOREIGN</span> <span style="color: #0000ff">KEY</span> (NameId)
<span style="color: #0000ff">REFERENCES</span> dbo.NameList (Id);
<span style="color: #0000ff">GO</span>

<span style="color: #008000">-- Now insert with a FK and check time</span>
<span style="color: #0000ff">DECLARE</span>
    @StartTime    DATETIME2 ,
    @EndTime    DATETIME2 ,
    @i            <span style="color: #0000ff">INT</span> = 1,
    @n            <span style="color: #0000ff">INT</span> = 10;

<span style="color: #0000ff">TRUNCATE</span> <span style="color: #0000ff">TABLE</span> dbo.DataTable;

<span style="color: #0000ff">SET</span> @StartTime  = SYSDATETIME();

<span style="color: #0000ff">WHILE</span> @i &lt;= @n
<span style="color: #0000ff">BEGIN</span>
    INSERT <span style="color: #0000ff">INTO</span> dbo.DataTable (NameId)
    <span style="color: #0000ff">SELECT</span> RandId <span style="color: #0000ff">FROM</span> dbo.RandomData;

    <span style="color: #0000ff">SET</span> @i+=1;
<span style="color: #0000ff">END</span>;

<span style="color: #0000ff">SET</span> @EndTime = SYSDATETIME();

<span style="color: #0000ff">SELECT</span> Avg_Insert_Time = <span style="color: #0000ff">CAST</span>(<span style="color: #0000ff">CAST</span>(DATEDIFF(MS,@StartTime,@EndTime) <span style="color: #0000ff">AS</span> <span style="color: #0000ff">DECIMAL</span>)/1000/@n <span style="color: #0000ff">AS</span> <span style="color: #0000ff">DECIMAL</span>(7,3));
<span style="color: #0000ff">GO</span></pre>
</div>
<p dir="ltr">This time the average run time was 6.30sec. Almost 35% more. I must say I didn't think the impact is that significant.</p>
<p dir="ltr">The execution plan this time was very different. The data is sorted, and the list table is accessed as well.</p>
<p dir="ltr"><a href="http://www.madeira.co.il/wp-content/uploads/2012/03/With_FK.jpg" rel="wp-prettyPhoto[g4570]"><img class="alignleft size-full wp-image-4574" src="http://www.madeira.co.il/wp-content/uploads/2012/03/With_FK.jpg" alt="" width="740" height="152" /></a></p>
<p dir="ltr">
<p dir="ltr">
<p dir="ltr">
<p dir="ltr">
<p dir="ltr">
<p dir="ltr"><strong>Enjoying both worlds:</strong></p>
<p dir="ltr">As we just saw, a FK can have significant impact on performance during inserts. Sometimes it's a price we are willing to pay, but sometimes our system is overloaded as it is. What can we do to minimize the impact on inserts while still keeping the FK ?</p>
<p dir="ltr">One solution is to drop the FK right before the insert and then rebuild it. This way the insert itself is performed as fast as possible while we still keep our data consistent.</p>
<div style="text-align: left;line-height: 12pt;background-color: #f4f4f4;margin: 20px 0px 10px;width: 97.5%;font-family: 'Courier New', courier, monospace;direction: ltr;font-size: 8pt;overflow: auto;cursor: text;border: silver 1px solid;padding: 4px">
<pre 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: #008000">-- Dropping the FK before the insert and recrating it after it's finished</span>
<span style="color: #0000ff">DECLARE</span>
    @StartTime    DATETIME2 ,
    @EndTime    DATETIME2 ,
    @i            <span style="color: #0000ff">INT</span> = 1,
    @n            <span style="color: #0000ff">INT</span> = 10;

<span style="color: #0000ff">TRUNCATE</span> <span style="color: #0000ff">TABLE</span> dbo.DataTable;

<span style="color: #0000ff">SET</span> @StartTime  = SYSDATETIME();

<span style="color: #0000ff">WHILE</span> @i &lt;= @n
<span style="color: #0000ff">BEGIN</span>
    <span style="color: #0000ff">ALTER</span> <span style="color: #0000ff">TABLE</span> dbo.DataTable
    <span style="color: #0000ff">DROP</span> <span style="color: #0000ff">CONSTRAINT</span> fk_DataList;

    INSERT <span style="color: #0000ff">INTO</span> dbo.DataTable (NameId)
    <span style="color: #0000ff">SELECT</span> RandId <span style="color: #0000ff">FROM</span> dbo.RandomData;

    <span style="color: #0000ff">ALTER</span> <span style="color: #0000ff">TABLE</span> dbo.DataTable
    <span style="color: #0000ff">ADD</span> <span style="color: #0000ff">CONSTRAINT</span> fk_DataList <span style="color: #0000ff">FOREIGN</span> <span style="color: #0000ff">KEY</span> (NameId)
    <span style="color: #0000ff">REFERENCES</span> dbo.NameList (Id);

    <span style="color: #0000ff">SET</span> @i+=1;
<span style="color: #0000ff">END</span>;

<span style="color: #0000ff">SET</span> @EndTime = SYSDATETIME();

<span style="color: #0000ff">SELECT</span> Avg_Insert_Time = <span style="color: #0000ff">CAST</span>(<span style="color: #0000ff">CAST</span>(DATEDIFF(MS,@StartTime,@EndTime) <span style="color: #0000ff">AS</span> <span style="color: #0000ff">DECIMAL</span>)/1000/@n <span style="color: #0000ff">AS</span> <span style="color: #0000ff">DECIMAL</span>(7,3));
<span style="color: #0000ff">GO</span></pre>
</div>
<p dir="ltr">This time, the average run time was 5.03 sec, only 2.4% more than without using a FK. The only &quot;danger&quot; is this way there is a possibility that data that violates the FK can be inserted and we won't be able to rebuild the FK. In order to avoid this we could simply use a try-catch and rollback in case to FK cannot be rebuilt.</p>
<p dir="ltr">
</div>]]></content:encoded>			<wfw:commentRss>http://www.madeira.co.il/foreign-keys-impact-on-inserts/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>&#8235;Foreign keys and how they help the optimizer&#8236;</title>		<link>http://www.madeira.co.il/foreign-keys-and-how-they-help-the-optimizer/</link>
		<comments>http://www.madeira.co.il/foreign-keys-and-how-they-help-the-optimizer/#comments</comments>
		<pubDate>Fri, 17 Feb 2012 14:26:31 +0000</pubDate>
		<dc:creator>&#8235;Shay Attiya&#8236;</dc:creator>				<category><![CDATA[בלוגים]]></category>
		<category><![CDATA[כללי]]></category>
		<category><![CDATA[מקודמות]]></category>
		<category><![CDATA[Foreign keys]]></category>
		<category><![CDATA[Optimizer]]></category>
		<category><![CDATA[sql]]></category>

		<guid isPermaLink="false">http://www.madeira.co.il/?p=4295</guid>
		<description><![CDATA[&#8235;Foreign keys just make perfect sense&#8236;]]></description>			<content:encoded><![CDATA[<div dir="rtl"><p><img src='http://www.madeira.co.il/wp-content/plugins/simple-post-thumbnails/timthumb.php?src=/wp-content/thumbnails/4295.gif&amp;w=214&amp;h=129&amp;zc=1&amp;ft=jpg' alt='post thumbnail' /></p>
<p dir="ltr">
<p dir="ltr">I love foreign keys. In my opinion they just make perfect sense.</p>
<p dir="ltr">They keep my data “clean” and consistent. Whenever two tables need to correspond with each other, I like to make sure that they do. For me as a DBA, the easiest and most intuitive way to do that is to create a FK constraint. Some of you (maybe even most of you) will say that FKs can be replaced by the application. Let it do all of the “dirty work” of making sure that the data in our DB makes sense business wise. But if there’s something I hate, it’s cleaning up after some programmer’s bug. Data fixes are a pain in the ***.</p>
<p dir="ltr">However, there is another reason why FKs are important. They can help the optimizer make better and more efficient decisions when creating an execution plan. To explain exactly how and why, let first see what a FK is.</p>
<p dir="ltr"><strong>What’s a Foreign Key ?</strong></p>
<p dir="ltr">A foreign key represents a relation between two tables in a database. The relation is determined by matching the value in a column (or combination of columns) in the referencing table to the value\s in the column\s of the referenced table. The value\s in the referenced table must be unique, either a primary key or a unique constraint.</p>
<p dir="ltr">These 2 conditions make sure 2 things will always be true:</p>
<p dir="ltr">1) For <strong>every</strong> value in the FK there is a <strong>matching record</strong> in the referenced table</p>
<p dir="ltr">2) There is <strong>only one match</strong></p>
<p dir="ltr"><strong>How does it help the optimizer ?</strong></p>
<p dir="ltr">Naturally, the optimizer knows how to take these 2 trues into consideration when creating the execution plan. To demonstrate I’ve created 2 tables with a FK relation between them.</p>
<p dir="ltr">The NameList table contains 100 names and Ids with the Id as a primary key for the table.</p>
<div style="text-align: left;line-height: 12pt;background-color: #f4f4f4;margin: 20px 0px 10px;width: 97.5%;font-family: 'Courier New', courier, monospace;direction: ltr;font-size: 8pt;overflow: auto;cursor: text;border: silver 1px solid;padding: 4px">
<pre 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: #008000">-- Create the list tables</span>
<span style="color: #0000ff">CREATE</span> <span style="color: #0000ff">TABLE</span>
    dbo.NameList
(
    Id    <span style="color: #0000ff">INT</span>            <span style="color: #0000ff">NOT</span> <span style="color: #0000ff">NULL</span> ,
    Name  <span style="color: #0000ff">VARCHAR</span>(10)    <span style="color: #0000ff">NOT</span> <span style="color: #0000ff">NULL</span> ,

    <span style="color: #0000ff">CONSTRAINT</span> pk_NameId_c <span style="color: #0000ff">PRIMARY</span> <span style="color: #0000ff">KEY</span> <span style="color: #0000ff">CLUSTERED</span> (Id)
);

INSERT <span style="color: #0000ff">INTO</span>
    dbo.NameList(Id, Name)
<span style="color: #0000ff">SELECT</span> <span style="color: #0000ff">TOP</span> 100
    ROW_NUMBER()<span style="color: #0000ff">OVER</span>(<span style="color: #0000ff">ORDER</span> <span style="color: #0000ff">BY</span> T1.name) ,
    <span style="color: #006080">'Name_'</span>+<span style="color: #0000ff">CAST</span>(ROW_NUMBER()<span style="color: #0000ff">OVER</span>(<span style="color: #0000ff">ORDER</span> <span style="color: #0000ff">BY</span> T1.name) <span style="color: #0000ff">AS</span> <span style="color: #0000ff">VARCHAR</span>(4))
<span style="color: #0000ff">FROM</span>
    sys.all_columns <span style="color: #0000ff">AS</span> T1
<span style="color: #0000ff">CROSS</span> <span style="color: #0000ff">JOIN</span>
    sys.all_columns <span style="color: #0000ff">AS</span> T2;
<span style="color: #0000ff">GO</span></pre>
</div>
<p dir="ltr">The RandomNames table contains 100,000 records with 4 columns. The Id is the PK for the table. The other 3 columns hold values between 1 and 100 and the values are exactly the same for all 3. The difference between them is that the first column is constrained by a FK, the second column isn’t, and the third also constrained by a FK but is nullable.</p>
<div style="text-align: left;line-height: 12pt;background-color: #f4f4f4;margin: 20px 0px 10px;width: 97.5%;font-family: 'Courier New', courier, monospace;direction: ltr;font-size: 8pt;overflow: auto;cursor: text;border: silver 1px solid;padding: 4px">
<pre 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: #008000">-- Create the data table</span>
<span style="color: #0000ff">CREATE</span> <span style="color: #0000ff">TABLE</span>
    dbo.RandomNames
(
    Id            <span style="color: #0000ff">INT</span>    <span style="color: #0000ff">NOT</span> <span style="color: #0000ff">NULL</span> <span style="color: #0000ff">IDENTITY</span>(1,1) ,
    NameId        <span style="color: #0000ff">INT</span>    <span style="color: #0000ff">NOT</span> <span style="color: #0000ff">NULL</span> ,
    NameId_NoFK   <span style="color: #0000ff">INT</span>    <span style="color: #0000ff">NOT</span> <span style="color: #0000ff">NULL</span> ,
    NameId_NULL   <span style="color: #0000ff">INT</span>    <span style="color: #0000ff">NULL</span> ,

    <span style="color: #0000ff">CONSTRAINT</span> pk_RandNameId_c <span style="color: #0000ff">PRIMARY</span> <span style="color: #0000ff">KEY</span> <span style="color: #0000ff">CLUSTERED</span> (Id) ,

    <span style="color: #0000ff">CONSTRAINT</span> fk_NameID <span style="color: #0000ff">FOREIGN</span> <span style="color: #0000ff">KEY</span> (NameId)
    <span style="color: #0000ff">REFERENCES</span> dbo.NameList (Id) ,

    <span style="color: #0000ff">CONSTRAINT</span> fk_NameID_NULL <span style="color: #0000ff">FOREIGN</span> <span style="color: #0000ff">KEY</span> (NameId_NULL)
    <span style="color: #0000ff">REFERENCES</span> dbo.NameList (Id)
);

INSERT <span style="color: #0000ff">INTO</span>
    dbo.RandomNames
(
    NameId ,
    NameId_NoFK ,
    NameId_NULL
)
<span style="color: #0000ff">SELECT</span>
    RandId ,
    RandId ,
    RandId
<span style="color: #0000ff">FROM</span>(
    <span style="color: #0000ff">SELECT</span> <span style="color: #0000ff">TOP</span> 100000
        ABS(CHECKSUM(NEWID())%100)+1 <span style="color: #0000ff">AS</span> RandId
    <span style="color: #0000ff">FROM</span>
        sys.all_columns <span style="color: #0000ff">AS</span> T1
    <span style="color: #0000ff">CROSS</span> <span style="color: #0000ff">JOIN</span>
        sys.all_columns <span style="color: #0000ff">AS</span> T2) <span style="color: #0000ff">AS</span> T;
<span style="color: #0000ff">GO</span></pre>
</div>
<p dir="ltr">Now, let’s join the 2 tables, every time using a different column (NameId, NameId_NoFK, NameId_NULL), and see how the execution plan differs for every query. Notice that I’m only selecting values from the RandomNames table.</p>
<div style="text-align: left;line-height: 12pt;background-color: #f4f4f4;margin: 20px 0px 10px;width: 97.5%;font-family: 'Courier New', courier, monospace;direction: ltr;font-size: 8pt;overflow: auto;cursor: text;border: silver 1px solid;padding: 4px">
<pre 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">SELECT</span> <span style="color: #0000ff">TOP</span> 100
    RN.Id
<span style="color: #0000ff">FROM</span>
    dbo.RandomNames <span style="color: #0000ff">AS</span> RN
<span style="color: #0000ff">INNER</span> <span style="color: #0000ff">JOIN</span>
    dbo.NameList <span style="color: #0000ff">AS</span> NL
    <span style="color: #0000ff">ON</span> RN.NameId = NL.Id;
<span style="color: #0000ff">GO</span>

<span style="color: #0000ff">SELECT</span> <span style="color: #0000ff">TOP</span> 100
    RN.Id
<span style="color: #0000ff">FROM</span>
    dbo.RandomNames <span style="color: #0000ff">AS</span> RN
<span style="color: #0000ff">INNER</span> <span style="color: #0000ff">JOIN</span>
    dbo.NameList <span style="color: #0000ff">AS</span> NL
    <span style="color: #0000ff">ON</span> RN.NameId_NoFK = NL.Id;
<span style="color: #0000ff">GO</span>

<span style="color: #0000ff">SELECT</span> <span style="color: #0000ff">TOP</span> 100
    RN.Id
<span style="color: #0000ff">FROM</span>
    dbo.RandomNames <span style="color: #0000ff">AS</span> RN
<span style="color: #0000ff">INNER</span> <span style="color: #0000ff">JOIN</span>
    dbo.NameList <span style="color: #0000ff">AS</span> NL
    <span style="color: #0000ff">ON</span> RN.NameId_NULL = NL.Id;
<span style="color: #0000ff">GO</span></pre>
</div>
<p style="text-align: center" dir="ltr"><a href="http://www.madeira.co.il/wp-content/uploads/2012/02/Exec_1.jpg" rel="wp-prettyPhoto[g4295]"><img class="size-medium wp-image-4297 aligncenter" src="http://www.madeira.co.il/wp-content/uploads/2012/02/Exec_1-300x251.jpg" alt="" width="300" height="251" /></a></p>
<p dir="ltr">When using the FK column the optimizer recognizes that the inner join in meaningless in this query. Because there must be a match and only one match, every record in the RandomNames must return and no duplicate can be created, so the optimizer simple scans the table. When using the NoFK column the optimizer has no way of knowing this and must perform the join. Notice that the nullable column behaves just like the NoFK column. This is because the optimizer can’t say every row as a match so the column is treated like it doesn’t have a FK (at least when it comes to executing the query).</p>
<p dir="ltr">Now you might say a better query would eliminate the need for the join. If you know your schema well enough you can just SELECT TOP 100 Id FROM dbo.RandomNames; and get the same result (that’s what the optimizer did), but a lot of times the person writing the query doesn’t know the schema very well. Other time the query is already written for us in a form of a view.</p>
<div style="text-align: left;line-height: 12pt;background-color: #f4f4f4;margin: 20px 0px 10px;width: 97.5%;font-family: 'Courier New', courier, monospace;direction: ltr;font-size: 8pt;overflow: auto;cursor: text;border: silver 1px solid;padding: 4px">
<pre 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">VIEW</span>
    dbo.VW_RandomNames
<span style="color: #0000ff">AS</span>
    <span style="color: #0000ff">SELECT</span>
        RN.Id ,
        NL.Name
    <span style="color: #0000ff">FROM</span>
        dbo.RandomNames <span style="color: #0000ff">AS</span> RN
    <span style="color: #0000ff">INNER</span> <span style="color: #0000ff">JOIN</span>
        dbo.NameList <span style="color: #0000ff">AS</span> NL
        <span style="color: #0000ff">ON</span> RN.NameId = NL.Id;
<span style="color: #0000ff">GO</span>

<span style="color: #0000ff">CREATE</span> <span style="color: #0000ff">VIEW</span>
    dbo.VW_RandomNames_NoFK
<span style="color: #0000ff">AS</span>
    <span style="color: #0000ff">SELECT</span>
        RN.Id ,
        NL.Name
    <span style="color: #0000ff">FROM</span>
        dbo.RandomNames <span style="color: #0000ff">AS</span> RN
    <span style="color: #0000ff">INNER</span> <span style="color: #0000ff">JOIN</span>
        dbo.NameList <span style="color: #0000ff">AS</span> NL
        <span style="color: #0000ff">ON</span> RN.NameId_NoFK = NL.Id;
<span style="color: #0000ff">GO</span>

<span style="color: #0000ff">CREATE</span> <span style="color: #0000ff">VIEW</span>
    dbo.VW_RandomNames_NULL
<span style="color: #0000ff">AS</span>
    <span style="color: #0000ff">SELECT</span>
        RN.Id ,
        NL.Name
    <span style="color: #0000ff">FROM</span>
        dbo.RandomNames <span style="color: #0000ff">AS</span> RN
    <span style="color: #0000ff">INNER</span> <span style="color: #0000ff">JOIN</span>
        dbo.NameList <span style="color: #0000ff">AS</span> NL
        <span style="color: #0000ff">ON</span> RN.NameId_NULL = NL.Id;
<span style="color: #0000ff">GO</span></pre>
</div>
<p dir="ltr">Now when we use the view, there is no way of rewriting the query.</p>
<div style="text-align: left;line-height: 12pt;background-color: #f4f4f4;margin: 20px 0px 10px;width: 97.5%;font-family: 'Courier New', courier, monospace;direction: ltr;font-size: 8pt;overflow: auto;cursor: text;border: silver 1px solid;padding: 4px">
<pre 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">SELECT</span> <span style="color: #0000ff">TOP</span> 100 Id
<span style="color: #0000ff">FROM</span> dbo.VW_RandomNames;
<span style="color: #0000ff">GO</span>

<span style="color: #0000ff">SELECT</span> <span style="color: #0000ff">TOP</span> 100 Id
<span style="color: #0000ff">FROM</span> dbo.VW_RandomNames_NoFK;
<span style="color: #0000ff">GO</span>

<span style="color: #0000ff">SELECT</span> <span style="color: #0000ff">TOP</span> 100 Id
<span style="color: #0000ff">FROM</span> dbo.VW_RandomNames_NULL;
<span style="color: #0000ff">GO</span></pre>
</div>
<p dir="ltr">
<p dir="ltr">Good thing the optimizer’s there to help us out. The execution plans are just like before.</p>
<p style="text-align: center" dir="ltr"><a href="http://www.madeira.co.il/wp-content/uploads/2012/02/Exec_View.jpg" rel="wp-prettyPhoto[g4295]"><img class="size-medium wp-image-4298   aligncenter" src="http://www.madeira.co.il/wp-content/uploads/2012/02/Exec_View-300x255.jpg" alt="" width="300" height="255" /></a></p>
<p dir="ltr">
<p dir="ltr">
<p dir="ltr">
<p dir="ltr">
<p dir="ltr">
<p dir="ltr">
<p dir="ltr">
<p dir="ltr">
<p dir="ltr">
<p dir="ltr">
<p dir="ltr">
<p dir="ltr">
<p dir="ltr">
<p dir="ltr">
<p dir="ltr">
<p dir="ltr">
<p dir="ltr">
<p dir="ltr">
<p dir="ltr">
<p dir="ltr">
<p dir="ltr">The FK is also helpful when we simply want to filter out records that don’t have a match.</p>
<div style="text-align: left;line-height: 12pt;background-color: #f4f4f4;margin: 20px 0px 10px;width: 97.5%;font-family: 'Courier New', courier, monospace;direction: ltr;font-size: 8pt;overflow: auto;cursor: text;border: silver 1px solid;padding: 4px">
<pre 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">SELECT</span> <span style="color: #0000ff">TOP</span> 100 Id
<span style="color: #0000ff">FROM</span> dbo.RandomNames
<span style="color: #0000ff">WHERE</span> NameId <span style="color: #0000ff">IN</span> (<span style="color: #0000ff">SELECT</span> Id <span style="color: #0000ff">FROM</span> dbo.NameList);
<span style="color: #0000ff">GO</span>

<span style="color: #0000ff">SELECT</span> <span style="color: #0000ff">TOP</span> 100 Id
<span style="color: #0000ff">FROM</span> dbo.RandomNames
<span style="color: #0000ff">WHERE</span> NameId_NoFK <span style="color: #0000ff">IN</span> (<span style="color: #0000ff">SELECT</span> Id <span style="color: #0000ff">FROM</span> dbo.NameList);
<span style="color: #0000ff">GO</span>

<span style="color: #0000ff">SELECT</span> <span style="color: #0000ff">TOP</span> 100 Id
<span style="color: #0000ff">FROM</span> dbo.RandomNames
<span style="color: #0000ff">WHERE</span> NameId_NULL <span style="color: #0000ff">IN</span> (<span style="color: #0000ff">SELECT</span> Id <span style="color: #0000ff">FROM</span> dbo.NameList);
<span style="color: #0000ff">GO</span></pre>
</div>
<p style="text-align: center" dir="ltr"><a href="http://www.madeira.co.il/wp-content/uploads/2012/02/Exec_Filter.jpg" rel="wp-prettyPhoto[g4295]"><img class="size-medium wp-image-4299  aligncenter" src="http://www.madeira.co.il/wp-content/uploads/2012/02/Exec_Filter-300x259.jpg" alt="" width="300" height="259" /></a></p>
<p dir="ltr">
<p dir="ltr">
<p dir="ltr">
<p dir="ltr">
<p dir="ltr">
<p dir="ltr">
<p dir="ltr">
<p dir="ltr">
<p dir="ltr">
<p dir="ltr">
<p dir="ltr">
<p dir="ltr">
<p dir="ltr">
<p dir="ltr">
<p dir="ltr">
<p dir="ltr">
<p dir="ltr">
<p dir="ltr">
<p dir="ltr">
<p dir="ltr">
<p dir="ltr">
<p dir="ltr">
<p dir="ltr">
<p dir="ltr">
<p dir="ltr">
<p dir="ltr">
<p dir="ltr">Again, the optimizer knows there is no need to check the NameList table when a FK is present (and it’s not nullable).</p>
<p dir="ltr">However, an odd thing happened when I tried to run these queries with a little change. I used NOT IN instead of IN to get only the records that don’t have a match.</p>
<div style="text-align: left;line-height: 12pt;background-color: #f4f4f4;margin: 20px 0px 10px;width: 97.5%;font-family: 'Courier New', courier, monospace;direction: ltr;font-size: 8pt;overflow: auto;cursor: text;border: silver 1px solid;padding: 4px">
<pre 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">SELECT</span> <span style="color: #0000ff">TOP</span> 100 Id
<span style="color: #0000ff">FROM</span> dbo.RandomNames
<span style="color: #0000ff">WHERE</span> NameId <span style="color: #0000ff">NOT</span> <span style="color: #0000ff">IN</span> (<span style="color: #0000ff">SELECT</span> Id <span style="color: #0000ff">FROM</span> dbo.NameList);
<span style="color: #0000ff">GO</span>

<span style="color: #0000ff">SELECT</span> <span style="color: #0000ff">TOP</span> 100 Id
<span style="color: #0000ff">FROM</span> dbo.RandomNames
<span style="color: #0000ff">WHERE</span> NameId_NoFK <span style="color: #0000ff">NOT</span> <span style="color: #0000ff">IN</span> (<span style="color: #0000ff">SELECT</span> Id <span style="color: #0000ff">FROM</span> dbo.NameList);
<span style="color: #0000ff">GO</span>

<span style="color: #0000ff">SELECT</span> <span style="color: #0000ff">TOP</span> 100 Id
<span style="color: #0000ff">FROM</span> dbo.RandomNames
<span style="color: #0000ff">WHERE</span> NameId_NULL <span style="color: #0000ff">NOT</span> <span style="color: #0000ff">IN</span> (<span style="color: #0000ff">SELECT</span> Id <span style="color: #0000ff">FROM</span> dbo.NameList);
<span style="color: #0000ff">GO</span></pre>
</div>
<p dir="ltr">I thought that the optimizer would immediately recognized that when a FK is present this is not a possible scenario and would automatically return an empty record set without scanning any table. I was wrong…</p>
<p style="text-align: center" dir="ltr"><a href="http://www.madeira.co.il/wp-content/uploads/2012/02/Exec_Filter2.jpg" rel="wp-prettyPhoto[g4295]"><img class="size-medium wp-image-4300  aligncenter" src="http://www.madeira.co.il/wp-content/uploads/2012/02/Exec_Filter2-300x207.jpg" alt="" width="300" height="207" /></a></p>
<p dir="ltr">
<p dir="ltr">
<p dir="ltr">
<p dir="ltr">
<p dir="ltr">
<p dir="ltr">
<p dir="ltr">
<p dir="ltr">The optimizer still goes over both tables and there is no difference between the FK column and the NoFK column. It gets even worse in the nullable FK column. It’s clear that the optimizer made a wrong decision, and I must say I couldn’t figure out why. My only guess is that the optimizer is not very effective when NULL values might be involved.</p>
<p dir="ltr"><strong>Bottom line</strong></p>
<p dir="ltr">FK can help the optimizer. They make queries run faster and prevent unnecessary reads (less shared locks). This is true as long as no NULL values are involved. You can avoid that by using an Id that is not used instead of using NULL (-1 for example). That will eliminate the need for a nullable FK column.</p>
<p dir="ltr">It’s true that FKs make inserts and updates run slower, so the overall benefits of FKs are not that great performance wise. But we are still left with my main reason for using FKs. Consistent, “Clean” data.</p>
<p dir="ltr">As I’ve said before… “Data fixes are a pain in the ***”.</p>
</div>]]></content:encoded>			<wfw:commentRss>http://www.madeira.co.il/foreign-keys-and-how-they-help-the-optimizer/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>&#8235;Log Shipping – As easy as 1, 2, 3&#8236;</title>		<link>http://www.madeira.co.il/log-shipping-%e2%80%93-as-easy-as-1-2-3/</link>
		<comments>http://www.madeira.co.il/log-shipping-%e2%80%93-as-easy-as-1-2-3/#comments</comments>
		<pubDate>Thu, 02 Feb 2012 08:44:18 +0000</pubDate>
		<dc:creator>&#8235;Shay Attiya&#8236;</dc:creator>				<category><![CDATA[בלוגים]]></category>
		<category><![CDATA[כללי]]></category>
		<category><![CDATA[מקודמות]]></category>
		<category><![CDATA[High Availability]]></category>
		<category><![CDATA[Log Shipping]]></category>
		<category><![CDATA[SQL Server]]></category>
		<category><![CDATA[Standby Server]]></category>

		<guid isPermaLink="false">http://www.madeira.co.il/?p=4076</guid>
		<description><![CDATA[&#8235;Log Shipping is an easy way to implement a high availability solution. As easy as 1, 2, 3&#8236;]]></description>			<content:encoded><![CDATA[<div dir="rtl"><p><img src='http://www.madeira.co.il/wp-content/plugins/simple-post-thumbnails/timthumb.php?src=/wp-content/thumbnails/4076.png&amp;w=214&amp;h=129&amp;zc=1&amp;ft=jpg' alt='post thumbnail' /></p>
<p dir="ltr">Today I want to talk about Log Shipping.</p>
<p dir="ltr">Log Shipping is an easy way to implement a high availability solution. It uses SQL Server’s transaction log to synchronize a stand by server. The configuration of the log shipping is really easy to set up as I’m about to show.</p>
<p dir="ltr"><strong>Basic requirements:</strong></p>
<p dir="ltr"><strong> </strong>1) The Primary Server must be in “Full” or “Bulk-Logged” recovery model</p>
<p dir="ltr">2) The Secondary Server must be from the same version as the primary (or later) and can be either in “Standby” or “No Recovery” mode. When using the Standby mode, the server can be used for reporting purposes, but it won’t be accessible during the log shipping process.</p>
<p dir="ltr">3) A third monitor server can be used but this is <span style="text-decoration: underline">optional</span>.</p>
<p dir="ltr"><strong>Step 1 – Configuring the Primary Server:</strong></p>
<p dir="ltr">Right click on the DB you wish to log ship and go to “Tasts -&gt; Ship Transaction Logs…”. Click on “Enable this as a primary database in a log shipping configuration” check box and press on the “Backup Settings…” button.</p>
<p dir="ltr"><a href="http://www.madeira.co.il/wp-content/uploads/2012/02/Database-Properties-1.jpg" rel="wp-prettyPhoto[g4076]"><img class="alignnone size-medium wp-image-4079" src="http://www.madeira.co.il/wp-content/uploads/2012/02/Database-Properties-1-300x143.jpg" alt="" width="300" height="143" /></a></p>
<p dir="ltr">The “Transaction Log Backup Settings” window will open. In it you will have to specify the path to the folder where the transaction log backups will be saved. Here you can also determine the frequency of transaction log backups (the default is every 15 minutes).</p>
<p dir="ltr"><a href="http://www.madeira.co.il/wp-content/uploads/2012/02/Transaction-Log-Backup-Settings.jpg" rel="wp-prettyPhoto[g4076]"><img class="alignnone size-medium wp-image-4080" src="http://www.madeira.co.il/wp-content/uploads/2012/02/Transaction-Log-Backup-Settings-291x300.jpg" alt="" width="291" height="300" /></a></p>
<p dir="ltr"><strong>Step 2 – Configuring the Secondary Server:</strong></p>
<p dir="ltr">After the primary server is configured, it’s time to configure the secondary server by pressing the “Add” button. <span style="text-decoration: underline">More than one</span> secondary server can be defined to create multiple standby servers.</p>
<p dir="ltr"><a href="http://www.madeira.co.il/wp-content/uploads/2012/02/Database-Properties-2.jpg" rel="wp-prettyPhoto[g4076]"><img class="alignnone size-medium wp-image-4081" src="http://www.madeira.co.il/wp-content/uploads/2012/02/Database-Properties-2-300x144.jpg" alt="" width="300" height="144" /></a></p>
<p dir="ltr">First, you’ll have to connect to the secondary server.</p>
<p dir="ltr"><a href="http://www.madeira.co.il/wp-content/uploads/2012/02/Secondary-Database-Settings1.jpg" rel="wp-prettyPhoto[g4076]"><img class="alignnone size-medium wp-image-4083" src="http://www.madeira.co.il/wp-content/uploads/2012/02/Secondary-Database-Settings1-300x261.jpg" alt="" width="300" height="261" /></a></p>
<p dir="ltr">
<p dir="ltr">In the <strong>“Initialize Secondary Database”</strong> tab you can choose the way you wish to initialize the secondary database (this is pretty self-explanatory).</p>
<p dir="ltr">In the <strong>“Copy Files”</strong> tab you’ll need to specify the folder where you wish to save the log backups shipped from the primary server, and the frequency of copying between the servers.</p>
<p dir="ltr"><a href="http://www.madeira.co.il/wp-content/uploads/2012/02/Copy-Files.jpg" rel="wp-prettyPhoto[g4076]"><img class="alignnone size-medium wp-image-4084" src="http://www.madeira.co.il/wp-content/uploads/2012/02/Copy-Files-300x260.jpg" alt="" width="300" height="260" /></a></p>
<p dir="ltr">In the <strong>“Restore Transaction Log”</strong> tab you can select the mode you want the secondary server to be in, and determine how often the transaction log will be restored.</p>
<p dir="ltr">As explained in the beginning, choosing the “Standby” mode will allow users to access the secondary DB. This may prevent the log from being restored and fail the log shipping process. When choosing this mode, it’s highly recommended to enable the “Disconnect users” option.</p>
<p dir="ltr"><a href="http://www.madeira.co.il/wp-content/uploads/2012/02/Restore-Transaction-Log.jpg" rel="wp-prettyPhoto[g4076]"><img class="alignnone size-medium wp-image-4085" src="http://www.madeira.co.il/wp-content/uploads/2012/02/Restore-Transaction-Log-300x262.jpg" alt="" width="300" height="262" /></a></p>
<p dir="ltr"><strong>Step 3 – Monitor Server (Optional):</strong></p>
<p dir="ltr">After configuring both servers, you can configure a third, monitor server.</p>
<p dir="ltr">The monitor server keeps track of when the transaction log on the primary database was last backed up, when the secondary servers last copied and restored the backup files, and information about any backup failure alerts.</p>
<p dir="ltr">A single monitor server can monitor multiple log shipping configurations.</p>
<p dir="ltr"><a href="http://www.madeira.co.il/wp-content/uploads/2012/02/Monitor.jpg" rel="wp-prettyPhoto[g4076]"><img class="alignnone size-medium wp-image-4086" src="http://www.madeira.co.il/wp-content/uploads/2012/02/Monitor-300x142.jpg" alt="" width="300" height="142" /></a></p>
<p dir="ltr">You can also generate a script for all of the configurations made to reuse on other servers.</p>
<p dir="ltr"><strong>That’s it!!</strong></p>
<p dir="ltr">You’re all done. After pressing “OK” you should see the following window, and the log shipping is up and running.</p>
<p dir="ltr"><a href="http://www.madeira.co.il/wp-content/uploads/2012/02/DONE.jpg" rel="wp-prettyPhoto[g4076]"><img class="alignnone size-medium wp-image-4087" src="http://www.madeira.co.il/wp-content/uploads/2012/02/DONE-300x155.jpg" alt="" width="300" height="155" /></a></p>
<p dir="ltr"><strong>Created objects:</strong></p>
<p dir="ltr">You might have guessed that the log shipping solution is actually implemented using a series of jobs.</p>
<p dir="ltr">On the primary server you’ll find a job responsible for backing up the log file as specified in the first step.</p>
<p dir="ltr">On the secondary server you’ll find two jobs. The first is responsible for copying the file from the primary to the secondary server, and the second is responsible for restoring the log file as defined in the second step.</p>
<p dir="ltr">On the monitor server you’ll find a job responsible for alerting when no restore has occurred.</p>
<p dir="ltr">You should also find the original log backups on the folder you specified on the primary server, and the copied ones on the secondary.</p>
<p dir="ltr"><strong>Fail Over:</strong></p>
<p dir="ltr">There’s <strong><span style="text-decoration: underline">no automatic fail over</span></strong> when using log shipping.</p>
<p dir="ltr">Performing the manual fail over involves two simple steps</p>
<p dir="ltr">1) Restoring the secondary server with recovery</p>
<p dir="ltr">2) Disable all the related jobs</p>
<p dir="ltr"><strong>Pros and Cons:</strong></p>
<p dir="ltr">I'll start with the good stuff. It’s easy! The default configuration shouldn’t take more than a few minutes to set up. In addition, the standby server can be used for reporting purposes and can be configured on the same instance.</p>
<p dir="ltr">The bad news is the frequent backups add overhead to SQL Server’s operation, and the copying of the backup files takes its toll on the disk subsystem. Another drawback is that fail over must be done manually. If an automatic fail over is important, mirroring might be a more suitable solution.</p>
<p dir="ltr">
</div>]]></content:encoded>			<wfw:commentRss>http://www.madeira.co.il/log-shipping-%e2%80%93-as-easy-as-1-2-3/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>&#8235;How are VLFs created, truncated and deleted?&#8236;</title>		<link>http://www.madeira.co.il/how-are-vlfs-created-truncated-and-deleted/</link>
		<comments>http://www.madeira.co.il/how-are-vlfs-created-truncated-and-deleted/#comments</comments>
		<pubDate>Mon, 16 Jan 2012 18:14:28 +0000</pubDate>
		<dc:creator>&#8235;Shay Attiya&#8236;</dc:creator>				<category><![CDATA[בלוגים]]></category>
		<category><![CDATA[מקודמות]]></category>
		<category><![CDATA[auto truncate]]></category>
		<category><![CDATA[Log file]]></category>
		<category><![CDATA[VLF]]></category>

		<guid isPermaLink="false">http://www.madeira.co.il/?p=3644</guid>
		<description><![CDATA[&#8235; Virtual Log Files or VLFs in short are the physical files that implement the log file. Here’s how they are created, truncated and deleted, and how we as DBAs can control it.&#8236;]]></description>			<content:encoded><![CDATA[<div dir="rtl"><p><img src='http://www.madeira.co.il/wp-content/plugins/simple-post-thumbnails/timthumb.php?src=/wp-content/thumbnails/3644.jpg&amp;w=214&amp;h=129&amp;zc=1&amp;ft=jpg' alt='post thumbnail' /></p>
<p dir="ltr">Today I chose to talk about Virtual Log Files or VLF in short. VLFs are the physical files that implement the log file. The log file’s log records are stored sequentially in a set of these physical files.</p>
<p dir="ltr"><strong>Creating a VLF:</strong></p>
<p dir="ltr">Every time space is allocated for the log file (Initial creation or log growth) new VLFs are created behind the scenes. The number of new VLFs is determined by the amount of space allocated.</p>
<p dir="ltr">1) 0MB &gt; Space Added &gt;= 64MB ,  4 new VLFs</p>
<p dir="ltr">2) 64MB &gt; Space Added &gt;= 1GB ,  8 new VLFs</p>
<p dir="ltr">3) 1GB &gt; Space Added,                 16 new VLFs</p>
<p dir="ltr">To demonstrate, I’ll use this script to create a DB with a log file in various sizes and auto growths. The database’s data file sizes is irrelevant.</p>
<div style="text-align: left;line-height: 12pt;background-color: #f4f4f4;margin: 20px 0px 10px;width: 97.5%;font-family: 'Courier New', courier, monospace;direction: ltr;font-size: 8pt;overflow: auto;cursor: text;border: silver 1px solid;padding: 4px">
<pre 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">USE</span>
    master;
<span style="color: #0000ff">GO</span>

<span style="color: #0000ff">IF</span> DB_ID(<span style="color: #006080">'MyDB'</span>) <span style="color: #0000ff">IS</span> <span style="color: #0000ff">NOT</span> <span style="color: #0000ff">NULL</span>
<span style="color: #0000ff">BEGIN</span>
    <span style="color: #0000ff">DROP</span> <span style="color: #0000ff">DATABASE</span> MyDB;
<span style="color: #0000ff">END</span>;

<span style="color: #0000ff">CREATE</span> <span style="color: #0000ff">DATABASE</span> MyDB
<span style="color: #0000ff">ON</span> (
    NAME = MyDB,
    FILENAME = <span style="color: #006080">'C:\Program Files\Microsoft SQL Server\MSSQL10_50.SQLSERVERR2_SHAY\MSSQL\DATA\MyDB.mdf'</span>,
    <span style="color: #0000ff">SIZE</span> = 10MB,
    FILEGROWTH = 1MB)
LOG <span style="color: #0000ff">ON</span> (
    NAME = MyDB_Log,
    FILENAME = <span style="color: #006080">'C:\Program Files\Microsoft SQL Server\MSSQL10_50.SQLSERVERR2_SHAY\MSSQL\DATA\MyDB_Log.ldf'</span>,
    <span style="color: #0000ff">SIZE</span> = 10MB, <span style="color: #008000">-- 10MB, 250MB, 65MB, 1025MB</span>
    FILEGROWTH = 6MB);
<span style="color: #0000ff">GO</span></pre>
</div>
<p dir="ltr">To view the size and number of the VLFs created I’ll use sys.database_files and DBCC LOGINFO.</p>
<div style="text-align: left;line-height: 12pt;background-color: #f4f4f4;margin: 20px 0px 10px;width: 97.5%;font-family: 'Courier New', courier, monospace;direction: ltr;font-size: 8pt;overflow: auto;cursor: text;border: silver 1px solid;padding: 4px">
<pre 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">USE</span>
    MyDB;
<span style="color: #0000ff">GO</span>

<span style="color: #0000ff">SELECT</span>
    File_Name    = name ,
    <span style="color: #0000ff">Size</span>        = <span style="color: #0000ff">CAST</span>(<span style="color: #0000ff">size</span>*8/1024 <span style="color: #0000ff">AS</span> <span style="color: #0000ff">VARCHAR</span>(10))+<span style="color: #006080">'MB'</span> , <span style="color: #008000">-- size is in pages (8KB)</span>
    Auto_Growth    = <span style="color: #0000ff">CASE</span> is_percent_growth
                    <span style="color: #0000ff">WHEN</span> 1 <span style="color: #0000ff">THEN</span> <span style="color: #0000ff">CAST</span>(growth <span style="color: #0000ff">AS</span> <span style="color: #0000ff">VARCHAR</span>(10))+<span style="color: #006080">'%'</span>
                    <span style="color: #0000ff">ELSE</span> <span style="color: #0000ff">CAST</span>(growth*8/1024 <span style="color: #0000ff">AS</span> <span style="color: #0000ff">VARCHAR</span>(10))+<span style="color: #006080">'MB'</span>
                <span style="color: #0000ff">END</span>
<span style="color: #0000ff">FROM</span> sys.database_files
<span style="color: #0000ff">WHERE</span> type_desc = <span style="color: #006080">'LOG'</span>;

<span style="color: #0000ff">DBCC</span> LOGINFO;
<span style="color: #0000ff">GO</span></pre>
</div>
<p dir="ltr"><strong>10MB:</strong></p>
<p dir="ltr">Our log file is 10MB, and as expected, we have 4 VLF files. But if we sum the size of the files we’ll get to a total of 10,477,568 Bytes while 10MB are 10,485,760 Bytes. There are 8,192 Bytes (size of a page) missing. This space is used by the file header and it does not store log records. You can see it in the StartOffset for the first VLF. This is true for all log file sizes.</p>
<p dir="ltr"><a href="http://www.madeira.co.il/wp-content/uploads/2012/01/10MB.jpg" rel="wp-prettyPhoto[g3644]"><img class="alignnone size-medium wp-image-3648" src="http://www.madeira.co.il/wp-content/uploads/2012/01/10MB-300x144.jpg" alt="" width="300" height="144" /></a></p>
<p dir="ltr">Here are the results for bigger log files:</p>
<p dir="ltr"><strong>65MB:</strong></p>
<p dir="ltr"><strong><a href="http://www.madeira.co.il/wp-content/uploads/2012/01/65MB.jpg" rel="wp-prettyPhoto[g3644]"><img class="alignnone size-medium wp-image-3649" src="http://www.madeira.co.il/wp-content/uploads/2012/01/65MB-300x192.jpg" alt="" width="300" height="192" /></a></strong></p>
<p dir="ltr"><strong>250MB:</strong></p>
<p><strong> </strong></p>
<p dir="ltr"><strong> <a href="http://www.madeira.co.il/wp-content/uploads/2012/01/250MB.jpg" rel="wp-prettyPhoto[g3644]"><img class="alignnone size-medium wp-image-3650" src="http://www.madeira.co.il/wp-content/uploads/2012/01/250MB-300x186.jpg" alt="" width="300" height="186" /></a></strong></p>
<p dir="ltr"><strong>1025MB:</strong></p>
<p dir="ltr"><strong><a href="http://www.madeira.co.il/wp-content/uploads/2012/01/1025MB.jpg" rel="wp-prettyPhoto[g3644]"><img class="alignnone size-medium wp-image-3651" src="http://www.madeira.co.il/wp-content/uploads/2012/01/1025MB-297x300.jpg" alt="" width="297" height="300" /></a></strong></p>
<p><strong> </strong></p>
<p dir="ltr">Ok, so now we know how VLFs are created, but before we continue, here’s a short overview on DBCC LOGINFO.</p>
<p dir="ltr"><strong>DBCC LOGINFO:</strong></p>
<p dir="ltr">The fields that will be relevant to better understand the VLF behavior are:</p>
<p dir="ltr"><strong>FileId</strong> – If you have one log file, this number will be the same</p>
<p dir="ltr"><strong>FileSize</strong> – This number is in Bytes</p>
<p dir="ltr"><strong>StartOffset</strong> – This number is in Bytes</p>
<p dir="ltr"><strong>FSeqNo</strong> – The file sequence number. The VLF with the highest FSeqNo is the one where current log records are being written into. FSeqNo = 0 indicates that the file hasn’t been used yet.</p>
<p dir="ltr"><strong>Status</strong> – Two possible values: 0 – VLF is recyclable, 2 – VLF may be active (Highest <strong>FSeqNo</strong> must be active)</p>
<p dir="ltr"><strong>Parity</strong> &#8211; Two possible values: 64 and 128. Every time a VLF is reused, the parity value is switched.</p>
<p dir="ltr"><strong>CreateLSN</strong> – Indicates at what LNS (Log Sequence Number) the VLF was created. CreateLSN = 0 states that the VLF was created at the time the log was created. Files with the same CreateLSN were created at the same time.</p>
<p dir="ltr">To see how the VLFs are being written on, I’ll create some activity in the DB.</p>
<p dir="ltr">The DB is set for simple recovery mode for now.</p>
<div style="text-align: left;line-height: 12pt;background-color: #f4f4f4;margin: 20px 0px 10px;width: 97.5%;font-family: 'Courier New', courier, monospace;direction: ltr;font-size: 8pt;overflow: auto;cursor: text;border: silver 1px solid;padding: 4px">
<pre 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">USE</span>
    MyDB;
<span style="color: #0000ff">GO</span>

<span style="color: #0000ff">ALTER</span> <span style="color: #0000ff">DATABASE</span> MyDB
<span style="color: #0000ff">SET</span> RECOVERY SIMPLE;
<span style="color: #0000ff">GO</span>

<span style="color: #0000ff">CREATE</span> <span style="color: #0000ff">TABLE</span> dbo.MyTable (<span style="color: #0000ff">Value</span> SYSNAME);

INSERT <span style="color: #0000ff">INTO</span> dbo.MyTable
<span style="color: #0000ff">SELECT</span> <span style="color: #0000ff">TOP</span> 1000 T1.name
<span style="color: #0000ff">FROM</span> sys.columns <span style="color: #0000ff">AS</span> T1
<span style="color: #0000ff">CROSS</span> <span style="color: #0000ff">JOIN</span> sys.columns <span style="color: #0000ff">AS</span> T2;
<span style="color: #0000ff">GO</span> 20</pre>
</div>
<p dir="ltr">The empty VLF files will be filled one by one until they are all full.</p>
<p dir="ltr"><a href="http://www.madeira.co.il/wp-content/uploads/2012/01/FullLog.jpg" rel="wp-prettyPhoto[g3644]"><img class="alignnone size-medium wp-image-3656" src="http://www.madeira.co.il/wp-content/uploads/2012/01/FullLog-300x147.jpg" alt="" width="300" height="147" /></a></p>
<p dir="ltr">Notice that the first 2 VLF statuses are 0. This means that these VLFs are recyclable and can be written on.</p>
<p dir="ltr">After generating more activity the first VLF has been overwritten and its FSeqNo changed from 22 to 26. Now the 3 other VLFs are recyclable and have status = 0.</p>
<p dir="ltr"><a href="http://www.madeira.co.il/wp-content/uploads/2012/01/FullLog2.jpg" rel="wp-prettyPhoto[g3644]"><img class="alignnone size-medium wp-image-3661" src="http://www.madeira.co.il/wp-content/uploads/2012/01/FullLog2-300x148.jpg" alt="" width="300" height="148" /></a></p>
<p dir="ltr"><strong>Log Truncation:</strong></p>
<p dir="ltr">Truncation is when inactive VLFs are marked as recyclable. This is a logical operation. Truncation occurs in the following events:</p>
<p dir="ltr">1) When a checkpoint occurs (only in auto truncate mode)</p>
<p dir="ltr">2) When the log is backed up (not in COPY_ONLY)</p>
<p dir="ltr">3) When the recovery mode is set to SIMPLE</p>
<p dir="ltr"><strong>Auto Truncate Mode:</strong></p>
<p dir="ltr">In Auto Truncate VLFs are being overwritten once they become inactive. The log file is in this mode when the DB is in SIMPLE recovery mode, or in FULL but no backup has been made.</p>
<p dir="ltr"><strong>Adding new VLFs (Growing the Log):</strong></p>
<p dir="ltr">In order to make the log file grow and create more VLFs we must first get out if the Auto Truncate mode. To do that, we’ll set the DB to FULL recovery mode and back it up.</p>
<div style="text-align: left;line-height: 12pt;background-color: #f4f4f4;margin: 20px 0px 10px;width: 97.5%;font-family: 'Courier New', courier, monospace;direction: ltr;font-size: 8pt;overflow: auto;cursor: text;border: silver 1px solid;padding: 4px">
<pre 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">ALTER</span> <span style="color: #0000ff">DATABASE</span> MyDB
<span style="color: #0000ff">SET</span> RECOVERY <span style="color: #0000ff">FULL</span>;
<span style="color: #0000ff">GO</span>

<span style="color: #0000ff">BACKUP</span> <span style="color: #0000ff">DATABASE</span> MyDB
<span style="color: #0000ff">TO</span> <span style="color: #0000ff">DISK</span> = <span style="color: #006080">'C:\Users\Shay\Documents\BackUp\MyDB_BU.bak'</span>;
<span style="color: #0000ff">GO</span></pre>
</div>
<p dir="ltr">Once we generate some more activity, all of the VLFs are full and new ones are created.</p>
<p dir="ltr"><a href="http://www.madeira.co.il/wp-content/uploads/2012/01/MoreVLFs.jpg" rel="wp-prettyPhoto[g3644]"><img class="alignnone size-medium wp-image-3666" src="http://www.madeira.co.il/wp-content/uploads/2012/01/MoreVLFs-300x173.jpg" alt="" width="300" height="173" /></a></p>
<p dir="ltr">The log file grew by 6MB and 4 new VLFs are added to it. All of the files that have already been written on (FSeqNo &gt; 0) are marked as active (Status = 2) and cannot be truncated.</p>
<p dir="ltr"><strong>Forcing Truncation:</strong></p>
<p dir="ltr">In order to be able to recycle the VLFs, we’ll buck up the <strong>Log File</strong>.</p>
<div style="text-align: left;line-height: 12pt;background-color: #f4f4f4;margin: 20px 0px 10px;width: 97.5%;font-family: 'Courier New', courier, monospace;direction: ltr;font-size: 8pt;overflow: auto;cursor: text;border: silver 1px solid;padding: 4px">
<pre 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">BACKUP</span> LOG MyDB
<span style="color: #0000ff">TO</span> <span style="color: #0000ff">DISK</span> = <span style="color: #006080">'C:\Users\Shay\Documents\BackUp\MyDB_LOGBU.bak'</span>;
<span style="color: #0000ff">GO</span></pre>
</div>
<p dir="ltr">After it has been bucked up, all of the VLFs are marked as inactive (Status = 0) except for the VLF that is being used at the moment. Note that the log file hasn’t shrunk, and is still 16MB.</p>
<p dir="ltr"><a href="http://www.madeira.co.il/wp-content/uploads/2012/01/Truncated1.jpg" rel="wp-prettyPhoto[g3644]"><img class="alignnone size-medium wp-image-3667" src="http://www.madeira.co.il/wp-content/uploads/2012/01/Truncated1-300x177.jpg" alt="" width="300" height="177" /></a></p>
<p dir="ltr">Now new log records can be written on the truncated VLFs.</p>
<p dir="ltr"><a href="http://www.madeira.co.il/wp-content/uploads/2012/01/OverWrite.jpg" rel="wp-prettyPhoto[g3644]"><img class="alignnone size-medium wp-image-3669" src="http://www.madeira.co.il/wp-content/uploads/2012/01/OverWrite-300x180.jpg" alt="" width="300" height="180" /></a></p>
<p dir="ltr"><strong>Deleting VLFs (Shrinking the Log):</strong></p>
<p dir="ltr">If the log file is too big, it can be shrunk by removing inactive VLFs from it. To do that we’ll make the DB go into Auto Truncate mode by set the recovery mode back to SIMPLE. If we are already in SIMPLE recovery mode, we can force a truncation by using the CHECKPOINT command.</p>
<div style="text-align: left;line-height: 12pt;background-color: #f4f4f4;margin: 20px 0px 10px;width: 97.5%;font-family: 'Courier New', courier, monospace;direction: ltr;font-size: 8pt;overflow: auto;cursor: text;border: silver 1px solid;padding: 4px">
<pre 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">ALTER</span> <span style="color: #0000ff">DATABASE</span> MyDB
<span style="color: #0000ff">SET</span> RECOVERY SIMPLE;
<span style="color: #0000ff">GO</span>

<span style="color: #0000ff">CHECKPOINT</span>;
<span style="color: #0000ff">GO</span></pre>
</div>
<p dir="ltr"><a href="http://www.madeira.co.il/wp-content/uploads/2012/01/Truncated2.jpg" rel="wp-prettyPhoto[g3644]"><img class="alignnone size-medium wp-image-3668" src="http://www.madeira.co.il/wp-content/uploads/2012/01/Truncated2-300x170.jpg" alt="" width="300" height="170" /></a></p>
<p dir="ltr">Now we can shrink the log file</p>
<div style="text-align: left;line-height: 12pt;background-color: #f4f4f4;margin: 20px 0px 10px;width: 97.5%;font-family: 'Courier New', courier, monospace;direction: ltr;font-size: 8pt;overflow: auto;cursor: text;border: silver 1px solid;padding: 4px">
<pre 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">DBCC</span> SHRINKFILE(2,5); <span style="color: #008000">-- File Id, File Size in MB</span>
GO</pre>
</div>
<p dir="ltr"><a href="http://www.madeira.co.il/wp-content/uploads/2012/01/Shrunk.jpg" rel="wp-prettyPhoto[g3644]"><img class="alignnone size-medium wp-image-3670" src="http://www.madeira.co.il/wp-content/uploads/2012/01/Shrunk-300x114.jpg" alt="" width="300" height="114" /></a></p>
<p dir="ltr">The file has shrunk to 6MB instead of 5 like we wanted. The reason is not all of the inactive VLFs are being removed during the file shrinking. In addition, the size of a single VLF can never change.</p>
<p dir="ltr"><strong>Now What?</strong></p>
<p dir="ltr">Now after we have a better understanding of how VLFs are created, truncated and deleted, and have also learned how to control these events, we as DBAs can modify our database for better performance by controlling the number of VFLs in the log file.</p>
<p dir="ltr"><strong>But what do we want to achieve?</strong></p>
<p dir="ltr">First, let’s consider our options:</p>
<p dir="ltr"><strong>Many VLFs:</strong></p>
<p dir="ltr">Having a lot of relatively small VLFs means that the log file has more flexibility during normal activity, but may cause problems during recovery and restore, and also during CHECKPOINT when in Auto Truncate mode. All of the inactive VLFs are being inspected at these events, and having a lot of then may be time consuming.</p>
<p dir="ltr">In SQL Server 2012 an error log message will appear when going over 1,000 VLFs.</p>
<p dir="ltr"><strong>Few VLFs:</strong></p>
<p dir="ltr">Having only a few big VLFs could cause problems involving truncation. A small amount of log records can prevent the VLF from being truncated. Shrinking the log file might suffer the same problems.</p>
<p dir="ltr"><strong>Bottom Line:</strong></p>
<p dir="ltr">There is no conclusive best Practice when it comes to determining the desired number of VLF files in the log. DBAs who have tested the subject report the “Sweet Spot” is around 100 VLFs. Like every other aspect of DB design, you should test it on your own system and configure it for what suits you best.</p>
<p dir="ltr">If you have tested it already, I'd like it if you could share your insights.</p>
<p><strong> </strong></p>
</div>]]></content:encoded>			<wfw:commentRss>http://www.madeira.co.il/how-are-vlfs-created-truncated-and-deleted/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>&#8235;The table variable transaction catch&#8236;</title>		<link>http://www.madeira.co.il/the-table-variable-transaction-catch/</link>
		<comments>http://www.madeira.co.il/the-table-variable-transaction-catch/#comments</comments>
		<pubDate>Sun, 08 Jan 2012 08:43:09 +0000</pubDate>
		<dc:creator>&#8235;Shay Attiya&#8236;</dc:creator>				<category><![CDATA[בלוגים]]></category>
		<category><![CDATA[כללי]]></category>
		<category><![CDATA[מקודמות]]></category>
		<category><![CDATA[table variable]]></category>
		<category><![CDATA[Transaction]]></category>

		<guid isPermaLink="false">http://www.madeira.co.il/?p=3266</guid>
		<description><![CDATA[&#8235;There is a catch when using a table variable as a part of a transaction.
It behaves differently than a regular or a temporary table.
In what way and why ?&#8236;]]></description>			<content:encoded><![CDATA[<div dir="rtl"><p><img src='http://www.madeira.co.il/wp-content/plugins/simple-post-thumbnails/timthumb.php?src=/wp-content/thumbnails/3266.jpg&amp;w=214&amp;h=129&amp;zc=1&amp;ft=jpg' alt='post thumbnail' /></p>
<p dir="ltr">As I’ve mentioned in my previous <a href="http://www.madeira.co.il/staging-temp-and-variable-tables-performance-recompilation-and-what%E2%80%99s-in-between-4/" target="_blank">post</a>, there is a catch when using a table variable as a part of a transaction. It behaves differently than a regular or a temporary table. To see this behavior, just run the attached script, once with @Rollback = 0 and once with @Rollback = 1.</p>
<div style="text-align: left;line-height: 12pt;background-color: #f4f4f4;margin: 20px 0px 10px;width: 97.5%;font-family: 'Courier New', courier, monospace;direction: ltr;font-size: 8pt;overflow: auto;cursor: text;border: silver 1px solid;padding: 4px">
<pre 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> NOCOUNT <span style="color: #0000ff">ON</span>;

<span style="color: #008000">-- RollBack or not ?</span>
<span style="color: #0000ff">DECLARE</span> @<span style="color: #0000ff">RollBack</span> <span style="color: #0000ff">BIT</span> = 0; <span style="color: #008000">-- 1</span>

<span style="color: #0000ff">DECLARE</span> @<span style="color: #0000ff">Table</span> <span style="color: #0000ff">TABLE</span>
    (
        Id        <span style="color: #0000ff">INT</span> ,
        <span style="color: #0000ff">Value</span>    <span style="color: #0000ff">VARCHAR</span>(15)
    );

INSERT <span style="color: #0000ff">INTO</span>
    @<span style="color: #0000ff">Table</span>
<span style="color: #0000ff">VALUES</span>
    (1,<span style="color: #006080">'Update Me'</span>) ,
    (2,<span style="color: #006080">'Delete Me'</span>);

<span style="color: #0000ff">SELECT</span> *
<span style="color: #0000ff">INTO</span> #<span style="color: #0000ff">Table</span>
<span style="color: #0000ff">FROM</span> @<span style="color: #0000ff">Table</span>;

<span style="color: #0000ff">SELECT</span>
    TmpTable = T.<span style="color: #0000ff">Value</span> ,
    VarTable = V.<span style="color: #0000ff">Value</span>
<span style="color: #0000ff">FROM</span>
    #<span style="color: #0000ff">Table</span> <span style="color: #0000ff">AS</span> T
<span style="color: #0000ff">FULL</span> <span style="color: #0000ff">OUTER</span> <span style="color: #0000ff">JOIN</span>
    @<span style="color: #0000ff">Table</span> <span style="color: #0000ff">AS</span> V
    <span style="color: #0000ff">ON</span> T.Id = V.Id;

<span style="color: #0000ff">SELECT</span> <span style="color: #0000ff">Action</span> = <span style="color: #006080">'Begin Tran'</span>;

<span style="color: #0000ff">BEGIN</span> <span style="color: #0000ff">TRAN</span>

<span style="color: #008000">-- Temp table DML</span>
INSERT <span style="color: #0000ff">INTO</span>
    #<span style="color: #0000ff">Table</span>
<span style="color: #0000ff">VALUES</span> (3,<span style="color: #006080">'I was inserted'</span>);

<span style="color: #0000ff">UPDATE</span> #<span style="color: #0000ff">Table</span>
<span style="color: #0000ff">SET</span> <span style="color: #0000ff">Value</span> = <span style="color: #006080">'I was updated'</span>
<span style="color: #0000ff">WHERE</span> <span style="color: #0000ff">Value</span> = <span style="color: #006080">'Update Me'</span>;

<span style="color: #0000ff">DELETE</span> #<span style="color: #0000ff">Table</span>
<span style="color: #0000ff">WHERE</span> <span style="color: #0000ff">Value</span> = <span style="color: #006080">'Delete Me'</span>;

<span style="color: #008000">-- Table variable DML</span>
INSERT <span style="color: #0000ff">INTO</span>
    @<span style="color: #0000ff">Table</span>
<span style="color: #0000ff">VALUES</span> (3,<span style="color: #006080">'I was inserted'</span>);

<span style="color: #0000ff">UPDATE</span> @<span style="color: #0000ff">Table</span>
<span style="color: #0000ff">SET</span> <span style="color: #0000ff">Value</span> = <span style="color: #006080">'I was updated'</span>
<span style="color: #0000ff">WHERE</span> <span style="color: #0000ff">Value</span> = <span style="color: #006080">'Update Me'</span>;

<span style="color: #0000ff">DELETE</span> @<span style="color: #0000ff">Table</span>
<span style="color: #0000ff">WHERE</span> <span style="color: #0000ff">Value</span> = <span style="color: #006080">'Delete Me'</span>;

<span style="color: #0000ff">SELECT</span>
    TmpTable = T.<span style="color: #0000ff">Value</span> ,
    VarTable = V.<span style="color: #0000ff">Value</span>
<span style="color: #0000ff">FROM</span>
    #<span style="color: #0000ff">Table</span> <span style="color: #0000ff">AS</span> T
<span style="color: #0000ff">FULL</span> <span style="color: #0000ff">OUTER</span> <span style="color: #0000ff">JOIN</span>
    @<span style="color: #0000ff">Table</span> <span style="color: #0000ff">AS</span> V
    <span style="color: #0000ff">ON</span> T.Id = V.Id;

<span style="color: #008000">-- RollBack ??</span>
<span style="color: #0000ff">IF</span> @<span style="color: #0000ff">RollBack</span> = 1
<span style="color: #0000ff">BEGIN</span>
    <span style="color: #0000ff">SELECT</span> <span style="color: #0000ff">Action</span> = <span style="color: #006080">'Rollback Tran'</span>;
    <span style="color: #0000ff">ROLLBACK</span> <span style="color: #0000ff">TRAN</span>;
<span style="color: #0000ff">END</span>;
<span style="color: #0000ff">ELSE</span>
<span style="color: #0000ff">BEGIN</span>
    <span style="color: #0000ff">SELECT</span> <span style="color: #0000ff">Action</span> = <span style="color: #006080">'Commit Tran'</span>;
    <span style="color: #0000ff">COMMIT</span> <span style="color: #0000ff">TRAN</span>;
<span style="color: #0000ff">END</span>;

<span style="color: #0000ff">SELECT</span>
    TmpTable = T.<span style="color: #0000ff">Value</span> ,
    VarTable = V.<span style="color: #0000ff">Value</span>
<span style="color: #0000ff">FROM</span>
    #<span style="color: #0000ff">Table</span> <span style="color: #0000ff">AS</span> T
<span style="color: #0000ff">FULL</span> <span style="color: #0000ff">OUTER</span> <span style="color: #0000ff">JOIN</span>
    @<span style="color: #0000ff">Table</span> <span style="color: #0000ff">AS</span> V
    <span style="color: #0000ff">ON</span> T.Id = V.Id;

<span style="color: #0000ff">DROP</span> <span style="color: #0000ff">TABLE</span> #<span style="color: #0000ff">Table</span>;

<span style="color: #0000ff">GO</span></pre>
</div>
<p dir="ltr">The script is pretty simple:</p>
<p dir="ltr">1. Inserts identical data to a temp table and to a table variable</p>
<p dir="ltr">2. Opens a transaction</p>
<p dir="ltr">3. Perform identical DML commands (insert, update, delete) on both tables</p>
<p dir="ltr">4. Commit / Rollback the transaction</p>
<p dir="ltr">You should get the following results:</p>
<p dir="ltr"><a href="http://www.madeira.co.il/wp-content/uploads/2012/01/Both.jpg" rel="wp-prettyPhoto[g3266]"><img class="alignnone size-medium wp-image-3269" src="http://www.madeira.co.il/wp-content/uploads/2012/01/Both-300x274.jpg" alt="" width="300" height="274" /></a></p>
<p dir="ltr">
<p dir="ltr">
<p dir="ltr">When we commit the transaction, both tables behave as expected. But when we rollback the transaction, the temp table rolls back while the table variable doesn’t.</p>
<p dir="ltr"><strong>Why is that??</strong></p>
<p dir="ltr">The answer lies in the transaction log. Well… Actually it doesn’t, and that’s the whole point.</p>
<p dir="ltr">Every time an object in the DB changes schema or data is being modified, the change is logged in the transactions log. But a table variable is <strong>not an object</strong>, it’s a variable, and the record set it holds is <strong>not data</strong>, it’s a value. Being as such, <strong>any change to a table variable is never logged in the transaction log.</strong> In other words, SQL Server never keeps any record on table variable history, and if no history records exist, no rollback can be performed.</p>
<p dir="ltr">Be sure to keep that in mind every time you use a table variable, and there is a chance a rollback might happen.</p>
</div>]]></content:encoded>			<wfw:commentRss>http://www.madeira.co.il/the-table-variable-transaction-catch/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>&#8235;Staging, Temp and Variable tables &#8211; Performance, Recompilation and what’s in between&#8236;</title>		<link>http://www.madeira.co.il/staging-temp-and-variable-tables-performance-recompilation-and-what%e2%80%99s-in-between-4/</link>
		<comments>http://www.madeira.co.il/staging-temp-and-variable-tables-performance-recompilation-and-what%e2%80%99s-in-between-4/#comments</comments>
		<pubDate>Mon, 02 Jan 2012 15:02:16 +0000</pubDate>
		<dc:creator>&#8235;Shay Attiya&#8236;</dc:creator>				<category><![CDATA[בלוגים]]></category>
		<category><![CDATA[כללי]]></category>
		<category><![CDATA[מקודמות]]></category>
		<category><![CDATA[performance]]></category>
		<category><![CDATA[recompilation]]></category>
		<category><![CDATA[sql]]></category>
		<category><![CDATA[table variable]]></category>
		<category><![CDATA[temp table]]></category>

		<guid isPermaLink="false">http://www.madeira.co.il/?p=2981</guid>
		<description><![CDATA[&#8235;In most cases I choose to use a temporary table in order to perform all kinds of calculations, but I must admit that I’ve never given it much thought in terms of performance. It always seemed like the natural thing to do, but is it the best thing to do?&#8236;]]></description>			<content:encoded><![CDATA[<div dir="rtl"><p><img src='http://www.madeira.co.il/wp-content/plugins/simple-post-thumbnails/timthumb.php?src=/wp-content/thumbnails/2981.jpg&amp;w=214&amp;h=129&amp;zc=1&amp;ft=jpg' alt='post thumbnail' /></p>
<p dir="ltr">A lot of times, when creating a procedure, we need to use some kind of an empty table in order to perform all kinds of calculations. The table is only needed for the calculation process itself, and has no other functionality otherwise. In most cases I choose to use a temporary table in order to do this, but I must admit that I’ve never given it much thought in terms of performance. It always seemed like the natural thing to do, but is it the best thing to do?</p>
<p dir="ltr">First, let consider our options:</p>
<p dir="ltr">1. Staging table (dbo.MyTable)</p>
<p dir="ltr">2.Temp table (#MyTable)</p>
<p dir="ltr">3.Table variable (@MyTable)</p>
<p dir="ltr">In order to find out which one is the best choice, I ran a little test. I used all 3 types of tables in order to execute the same kind of procedure and measured the time it took to execute. I did this for 2 kinds of procedures (simple and complex calculation) and for various record sets from 10 records to 1,000,000 records. I’ve also cleaned the cache in order to make the procedures recompile and see how it affected performance for every type of table.</p>
<p dir="ltr"><strong>The test:</strong></p>
<p dir="ltr">I first created some test data to be used throughout the test. The tables held 1,000,000 records of random values from 1 to 10,000. The table was indexed using a clustered primary key.</p>
<div style="text-align: left;line-height: 12pt;background-color: #f4f4f4;margin: 20px 0px 10px;width: 97.5%;font-family: 'Courier New', courier, monospace;direction: ltr;font-size: 8pt;overflow: auto;cursor: text;border: silver 1px solid;padding: 4px">
<pre 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: #008000">-- Test data table</span>
<span style="color: #0000ff">CREATE</span> <span style="color: #0000ff">TABLE</span>
    dbo.DataTable
(
    Id    <span style="color: #0000ff">INT</span>    <span style="color: #0000ff">NOT</span> <span style="color: #0000ff">NULL</span> <span style="color: #0000ff">IDENTITY</span> (1,1) ,
    <span style="color: #0000ff">Value</span> <span style="color: #0000ff">INT</span>    <span style="color: #0000ff">NOT</span> <span style="color: #0000ff">NULL</span> ,

    <span style="color: #0000ff">CONSTRAINT</span> pk_DataTable <span style="color: #0000ff">PRIMARY</span> <span style="color: #0000ff">KEY</span> <span style="color: #0000ff">CLUSTERED</span> (Id)
);

<span style="color: #008000">-- 1,000,000 recordes of test data</span>
INSERT <span style="color: #0000ff">INTO</span>
    dbo.DataTable
(
    <span style="color: #0000ff">Value</span>
)
<span style="color: #0000ff">SELECT</span> <span style="color: #0000ff">TOP</span> 1000000
    ABS(CHECKSUM(NEWID())%10000) + 1
<span style="color: #0000ff">FROM</span>
    sys.all_columns <span style="color: #0000ff">AS</span> T1
<span style="color: #0000ff">CROSS</span> <span style="color: #0000ff">JOIN</span>
    sys.all_columns <span style="color: #0000ff">AS</span> T2;
<span style="color: #0000ff">GO</span></pre>
</div>
<p dir="ltr">I then created the Staging table. One column, no indexes, no constraints.</p>
<div style="text-align: left;line-height: 12pt;background-color: #f4f4f4;margin: 20px 0px 10px;width: 97.5%;font-family: 'Courier New', courier, monospace;direction: ltr;font-size: 8pt;overflow: auto;cursor: text;border: silver 1px solid;padding: 4px">
<pre 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: #008000">-- The Staging table</span>
<span style="color: #0000ff">CREATE</span> <span style="color: #0000ff">TABLE</span>
    dbo.StgTable
(
    <span style="color: #0000ff">Value</span>    <span style="color: #0000ff">INT</span>    <span style="color: #0000ff">NOT</span> <span style="color: #0000ff">NULL</span>
);
<span style="color: #0000ff">GO</span></pre>
</div>
<p dir="ltr">For the test itself I used three procedures, all doing exactly the same:</p>
<p dir="ltr">1. Populating the Staging / Temp / Variable table with a certain amount of records.</p>
<p dir="ltr">2. For the simple procedure – Count all of the values that are divided by 10.</p>
<p dir="ltr">3. For the complex procedure – Join with the DataTable and then count the data values that are divided by 10.</p>
<p dir="ltr"><strong>Staging Table:</strong></p>
<div style="text-align: left;line-height: 12pt;background-color: #f4f4f4;margin: 20px 0px 10px;width: 97.5%;font-family: 'Courier New', courier, monospace;direction: ltr;font-size: 8pt;overflow: auto;cursor: text;border: silver 1px solid;padding: 4px">
<pre 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>
    dbo.usp_StgTable
        @<span style="color: #0000ff">Top</span>        <span style="color: #0000ff">INT</span> ,
        @Complex    <span style="color: #0000ff">BIT</span>
<span style="color: #0000ff">AS</span>
<span style="color: #0000ff">BEGIN</span>
    <span style="color: #008000">-- Insert X amount of records into the table</span>
    INSERT <span style="color: #0000ff">INTO</span> dbo.StgTable
    (
        <span style="color: #0000ff">Value</span>
    )
    <span style="color: #0000ff">SELECT</span> <span style="color: #0000ff">Value</span>
    <span style="color: #0000ff">FROM</span> dbo.DataTable
    <span style="color: #0000ff">WHERE</span> Id &lt;= @<span style="color: #0000ff">Top</span>;

    <span style="color: #0000ff">IF</span> @Complex = 0 <span style="color: #008000">-- Simple</span>
    <span style="color: #0000ff">BEGIN</span>
        <span style="color: #0000ff">SELECT</span> <span style="color: #0000ff">COUNT</span>(*)
        <span style="color: #0000ff">FROM</span> dbo.StgTable
        <span style="color: #0000ff">WHERE</span> <span style="color: #0000ff">Value</span>%10 = 0;
    <span style="color: #0000ff">END</span>;
    <span style="color: #0000ff">ELSE</span> <span style="color: #008000">-- Complex</span>
    <span style="color: #0000ff">BEGIN</span>
        <span style="color: #0000ff">SELECT</span> <span style="color: #0000ff">COUNT</span>(*)
        <span style="color: #0000ff">FROM</span> dbo.StgTable <span style="color: #0000ff">AS</span> Stg
        <span style="color: #0000ff">INNER</span> <span style="color: #0000ff">JOIN</span> dbo.DataTable <span style="color: #0000ff">AS</span> Tbl
        <span style="color: #0000ff">ON</span> Stg.<span style="color: #0000ff">Value</span> = Tbl.Id
        <span style="color: #0000ff">WHERE</span> Tbl.<span style="color: #0000ff">Value</span>%10 = 0;
    <span style="color: #0000ff">END</span>;

<span style="color: #0000ff">END</span>;
<span style="color: #0000ff">GO</span></pre>
</div>
<p dir="ltr"><strong>Temp Table:</strong></p>
<div style="text-align: left;line-height: 12pt;background-color: #f4f4f4;margin: 20px 0px 10px;width: 97.5%;font-family: 'Courier New', courier, monospace;direction: ltr;font-size: 8pt;overflow: auto;cursor: text;border: silver 1px solid;padding: 4px">
<pre 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>
    dbo.usp_TmpTable
        @<span style="color: #0000ff">Top</span>        <span style="color: #0000ff">INT</span> ,
        @Complex    <span style="color: #0000ff">BIT</span>

<span style="color: #0000ff">AS</span>
<span style="color: #0000ff">BEGIN</span>
    <span style="color: #008000">-- Insert X amount of records into the table</span>
    <span style="color: #0000ff">SELECT</span> <span style="color: #0000ff">Value</span>
    <span style="color: #0000ff">INTO</span> #<span style="color: #0000ff">Table</span>
    <span style="color: #0000ff">FROM</span> dbo.DataTable
    <span style="color: #0000ff">WHERE</span> Id &lt;= @<span style="color: #0000ff">Top</span>;

    <span style="color: #0000ff">IF</span> @Complex = 0 <span style="color: #008000">-- Simple</span>
    <span style="color: #0000ff">BEGIN</span>
        <span style="color: #0000ff">SELECT</span> <span style="color: #0000ff">COUNT</span>(*)
        <span style="color: #0000ff">FROM</span> #<span style="color: #0000ff">Table</span>
        <span style="color: #0000ff">WHERE</span> <span style="color: #0000ff">Value</span>%10 = 0;
    <span style="color: #0000ff">END</span>;
    <span style="color: #0000ff">ELSE</span> <span style="color: #008000">-- Complex</span>
    <span style="color: #0000ff">BEGIN</span>
        <span style="color: #0000ff">SELECT</span> <span style="color: #0000ff">COUNT</span>(*)
        <span style="color: #0000ff">FROM</span> #<span style="color: #0000ff">Table</span> <span style="color: #0000ff">AS</span> Tmp
        <span style="color: #0000ff">INNER</span> <span style="color: #0000ff">JOIN</span> dbo.DataTable <span style="color: #0000ff">AS</span> Tbl
        <span style="color: #0000ff">ON</span> Tmp.<span style="color: #0000ff">Value</span> = Tbl.Id
        <span style="color: #0000ff">WHERE</span> Tbl.<span style="color: #0000ff">Value</span>%10 = 0;
    <span style="color: #0000ff">END</span>;

    <span style="color: #0000ff">DROP</span> <span style="color: #0000ff">TABLE</span> #<span style="color: #0000ff">Table</span>;

<span style="color: #0000ff">END</span>;
<span style="color: #0000ff">GO</span></pre>
</div>
<p dir="ltr"><strong>Table Variable:</strong></p>
<div style="text-align: left;line-height: 12pt;background-color: #f4f4f4;margin: 20px 0px 10px;width: 97.5%;font-family: 'Courier New', courier, monospace;direction: ltr;font-size: 8pt;overflow: auto;cursor: text;border: silver 1px solid;padding: 4px">
<pre 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>
    dbo.usp_VarTable
        @<span style="color: #0000ff">Top</span>        <span style="color: #0000ff">INT</span> ,
        @Complex    <span style="color: #0000ff">BIT</span>
<span style="color: #0000ff">AS</span>
<span style="color: #0000ff">BEGIN</span>
    <span style="color: #008000">-- Insert X amount of records into the table</span>
    <span style="color: #0000ff">DECLARE</span> @<span style="color: #0000ff">Table</span> <span style="color: #0000ff">TABLE</span>
    (
        <span style="color: #0000ff">Value</span>    <span style="color: #0000ff">INT</span>    <span style="color: #0000ff">NOT</span> <span style="color: #0000ff">NULL</span>
    );

    INSERT <span style="color: #0000ff">INTO</span> @<span style="color: #0000ff">Table</span>
    (
        <span style="color: #0000ff">Value</span>
    )
    <span style="color: #0000ff">SELECT</span> <span style="color: #0000ff">Value</span>
    <span style="color: #0000ff">FROM</span> dbo.DataTable
    <span style="color: #0000ff">WHERE</span> Id &lt;= @<span style="color: #0000ff">Top</span>;

    <span style="color: #0000ff">IF</span> @Complex = 0 <span style="color: #008000">-- Simple</span>
    <span style="color: #0000ff">BEGIN</span>
        <span style="color: #0000ff">SELECT</span> <span style="color: #0000ff">COUNT</span>(*)
        <span style="color: #0000ff">FROM</span> @<span style="color: #0000ff">Table</span>
        <span style="color: #0000ff">WHERE</span> <span style="color: #0000ff">Value</span>%10 = 0;
    <span style="color: #0000ff">END</span>;
    <span style="color: #0000ff">ELSE</span> <span style="color: #008000">-- Complex</span>
    <span style="color: #0000ff">BEGIN</span>
        <span style="color: #0000ff">SELECT</span> <span style="color: #0000ff">COUNT</span>(*)
        <span style="color: #0000ff">FROM</span> @<span style="color: #0000ff">Table</span> <span style="color: #0000ff">AS</span> Var
        <span style="color: #0000ff">INNER</span> <span style="color: #0000ff">JOIN</span> dbo.DataTable <span style="color: #0000ff">AS</span> Tbl
        <span style="color: #0000ff">ON</span> Var.<span style="color: #0000ff">Value</span> = Tbl.Id
        <span style="color: #0000ff">WHERE</span> Tbl.<span style="color: #0000ff">Value</span>%10 = 0;
    <span style="color: #0000ff">END</span>;

<span style="color: #0000ff">END</span>;
<span style="color: #0000ff">GO</span></pre>
</div>
<p dir="ltr">I used the following scripts to run the test and to create the table where the results were saved.</p>
<div style="text-align: left;line-height: 12pt;background-color: #f4f4f4;margin: 20px 0px 10px;width: 97.5%;font-family: 'Courier New', courier, monospace;direction: ltr;font-size: 8pt;overflow: auto;cursor: text;border: silver 1px solid;padding: 4px">
<pre 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.TableTypeTest
(
    TableType     <span style="color: #0000ff">VARCHAR</span>(10) <span style="color: #0000ff">NOT</span> <span style="color: #0000ff">NULL</span> ,
    Complex       <span style="color: #0000ff">BIT</span>         <span style="color: #0000ff">NOT</span> <span style="color: #0000ff">NULL</span> ,
    RowCnt        <span style="color: #0000ff">INT</span>         <span style="color: #0000ff">NOT</span> <span style="color: #0000ff">NULL</span> ,
    UnCompiledRun <span style="color: #0000ff">INT</span>         <span style="color: #0000ff">NOT</span> <span style="color: #0000ff">NULL</span> ,
    CompiledRun   <span style="color: #0000ff">INT</span>         <span style="color: #0000ff">NOT</span> <span style="color: #0000ff">NULL</span>
);
<span style="color: #0000ff">GO</span></pre>
</div>
<p dir="ltr">The script was run twice for every table type, once for simple and the other for complex (six times in all). The script calculates the average time it took for the procedure to execute (average over 20 runs per procedure). This was done twice every cycle, once when the cache is freed before every run to make the procedure recompile every time, and the other when the execution plan is in cache.</p>
<p dir="ltr">The number of records populating the table was multiplied by 10 after every cycle (10, 100, 1000,…, 1000000) .</p>
<div style="text-align: left;line-height: 12pt;background-color: #f4f4f4;margin: 20px 0px 10px;width: 97.5%;font-family: 'Courier New', courier, monospace;direction: ltr;font-size: 8pt;overflow: auto;cursor: text;border: silver 1px solid;padding: 4px">
<pre 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">DBCC</span> FREEPROCCACHE;

<span style="color: #0000ff">DECLARE</span>
    @<span style="color: #0000ff">Top</span>        <span style="color: #0000ff">INT</span> = 10 ,
    @Complex    <span style="color: #0000ff">BIT</span> = 0 , <span style="color: #008000">-- 1</span>
    @Type       <span style="color: #0000ff">VARCHAR</span>(10) = <span style="color: #006080">'Stg'</span> , <span style="color: #008000">-- 'Tmp', 'Var'</span>
    @<span style="color: #0000ff">SQL</span>        NVARCHAR(<span style="color: #0000ff">MAX</span>);

<span style="color: #0000ff">WHILE</span> @<span style="color: #0000ff">Top</span> &lt;= 1000000
<span style="color: #0000ff">BEGIN</span>

    <span style="color: #0000ff">SET</span> @<span style="color: #0000ff">SQL</span> = <span style="color: #006080">'
    DECLARE
    @i        INT ,
    @n        INT = 20 ,
    @Time0    DATETIME2 ,
    @Time1    DATETIME2 ,
    @UnCompTime  INT = 0 ,
    @CompTime    INT = 0;

    SET @i = 1;

    WHILE @i &lt;= @n
    BEGIN
        TRUNCATE TABLE dbo.StgTable;
        DBCC FREEPROCCACHE;
        SET @Time0 = SYSDATETIME();
        EXECUTE dbo.usp_'</span>+@Type+<span style="color: #006080">'Table
            '</span>+<span style="color: #0000ff">CAST</span>(@<span style="color: #0000ff">Top</span> <span style="color: #0000ff">AS</span> <span style="color: #0000ff">VARCHAR</span>(10))+<span style="color: #006080">' ,
            '</span>+<span style="color: #0000ff">CAST</span>(@Complex <span style="color: #0000ff">AS</span> <span style="color: #0000ff">VARCHAR</span>(10))+<span style="color: #006080">';
        SET @Time1 = SYSDATETIME();
        SET @UnCompTime += DATEDIFF(ms,@Time0,@Time1)
        SET @i += 1;
    END;

    SET @i = 1;

    WHILE @i &lt;= @n
    BEGIN
        TRUNCATE TABLE dbo.StgTable;
        SET @Time0 = SYSDATETIME();
        EXECUTE dbo.usp_'</span>+@Type+<span style="color: #006080">'Table
            '</span>+<span style="color: #0000ff">CAST</span>(@<span style="color: #0000ff">Top</span> <span style="color: #0000ff">AS</span> <span style="color: #0000ff">VARCHAR</span>(10))+<span style="color: #006080">' ,
            '</span>+<span style="color: #0000ff">CAST</span>(@Complex <span style="color: #0000ff">AS</span> <span style="color: #0000ff">VARCHAR</span>(10))+<span style="color: #006080">';
        SET @Time1 = SYSDATETIME();
        SET @CompTime += DATEDIFF(ms,@Time0,@Time1)
        SET @i += 1;
    END;

    INSERT INTO
        dbo.TableTypeTest
    (
        TableType ,
        Complex ,
        RowCnt ,
        UnCompiledRun ,
        PreCompiledRun
    )
    SELECT
        TableType      = '</span><span style="color: #006080">''</span>+@Type+<span style="color: #006080">''</span><span style="color: #006080">' ,
        Complex        = '</span>+<span style="color: #0000ff">CAST</span>(@Complex <span style="color: #0000ff">AS</span> <span style="color: #0000ff">VARCHAR</span>(10))+<span style="color: #006080">' ,
        RowCnt         = '</span>+<span style="color: #0000ff">CAST</span>(@<span style="color: #0000ff">Top</span> <span style="color: #0000ff">AS</span> <span style="color: #0000ff">VARCHAR</span>(10))+<span style="color: #006080">' ,
        UnCompiledRun  = @UnCompTime/@n ,
        PreCompiledRun = @CompTime/@n;'</span>

    <span style="color: #0000ff">EXECUTE</span> sp_executesql @<span style="color: #0000ff">SQL</span>;

    <span style="color: #0000ff">SET</span> @<span style="color: #0000ff">Top</span> *= 10;

<span style="color: #0000ff">END</span>;
<span style="color: #0000ff">GO</span></pre>
</div>
<p dir="ltr"><strong>The Results:</strong></p>
<p dir="ltr">The “<strong>simple</strong>” procedure produced the following results (run time is shown in ms):</p>
<p dir="ltr"><a href="http://www.madeira.co.il/wp-content/uploads/2012/01/SimpleTable.jpg" rel="wp-prettyPhoto[g2981]"><img class="alignnone size-medium wp-image-3020" src="http://www.madeira.co.il/wp-content/uploads/2012/01/SimpleTable-284x300.jpg" alt="" width="284" height="300" /></a></p>
<p dir="ltr">It’s clear that the variable table is the “winner” for the UnCompiled runs. It’s about twice to three times as fast as the Staging and Temp table. For the PreCompiled runs the Var table has a slight advantage over the Temp table, but it’s still about twice to three times as fast as the Staging table.</p>
<p dir="ltr">The “<strong>complex</strong>” procedure produced the following results:</p>
<p dir="ltr"><a href="http://www.madeira.co.il/wp-content/uploads/2012/01/ComplexTable.jpg" rel="wp-prettyPhoto[g2981]"><img class="alignnone size-medium wp-image-3021" src="http://www.madeira.co.il/wp-content/uploads/2012/01/ComplexTable-283x300.jpg" alt="" width="283" height="300" /></a></p>
<p dir="ltr">Now the winner depends on the type of procedure (simple or complex).</p>
<p dir="ltr">For the UnCompiled runs the Var table is still 1.5-2 times faster than the Staging and Temp tables. But for the PreCompiled runs the Temp table takes the lead and is about 3 times as fast as the Var and Staging tables.</p>
<p dir="ltr"><strong> </strong> <span style="font-weight: bold">So what’s going on?</span></p>
<p dir="ltr">Let’s have a closer look at the results and try to find out.</p>
<p dir="ltr">We’ll start with the <strong>simple</strong> procedure.</p>
<p dir="ltr">In this procedure we count how many values are divided by 10. The execution plan for this kind of query would be the same (Table scan) no matter how many records the table holds.</p>
<p style="text-align: left" dir="rtl"><a href="http://www.madeira.co.il/wp-content/uploads/2012/01/TableScan.jpg" rel="wp-prettyPhoto[g2981]"><img class="alignnone size-medium wp-image-3010" src="http://www.madeira.co.il/wp-content/uploads/2012/01/TableScan-300x75.jpg" alt="" width="300" height="75" /></a>﻿﻿</p>
<p dir="ltr">Since the plan is always the same, statistics have no meaning in this case and updating it would be a useless overhead.</p>
<p dir="ltr">This is where the table variable has an advantage over other kinds of tables. It has no statistics, and the overhead for this kind of execution is kept minimal. In addition, SQL Server cannot make an estimated plan to the Temp table procedure, but is able to do so for the Var table. It doesn’t have to “wait” for the statistics to be updated for the Var table (there will never be any), but it needs to do so for the Temp table. When a Temp table is part of the procedure, the plan is recompiled every time the procedure is executed. Again, a useless overhead.</p>
<p dir="ltr">Now for the more <strong>complex</strong> procedure.</p>
<p dir="ltr">Here we join our table with another big table (1,000,000 records), before the count is performed. Now statistics have a huge influence on the execution plan. For a small amount of records, SQL Server chooses to perform the join using the “Nested Loops” method. However, for a large record count it chooses the “Hash Match” method.</p>
<p dir="ltr">When there are no statistics (Var table) SQL Server is “guessing” when it comes to choosing the right execution plan. For lower record counts, it guesses correctly having expecting only 1 record to return. For higher record counts its guess is becoming more and more incorrect.</p>
<p dir="ltr">This is why the Temp table procedure is more or less the same as the Var table procedure up to 100,000 records, but is much more efficient for higher record counts.</p>
<p dir="ltr">Here are the execution plans for 100,000 records:</p>
<p dir="ltr"><strong>Temp table:</strong></p>
<p dir="ltr"><strong><a href="http://www.madeira.co.il/wp-content/uploads/2012/01/HashMatch-TmpTable.jpg" rel="wp-prettyPhoto[g2981]"><img class="alignnone size-medium wp-image-3013" src="http://www.madeira.co.il/wp-content/uploads/2012/01/HashMatch-TmpTable-300x68.jpg" alt="" width="300" height="68" /></a></strong></p>
<p dir="ltr"><strong> </strong></p>
<p dir="ltr"><strong>Variable table:</strong></p>
<p dir="ltr"><strong><a href="http://www.madeira.co.il/wp-content/uploads/2012/01/NestedLoop-VarTable.jpg" rel="wp-prettyPhoto[g2981]"><img class="alignnone size-medium wp-image-3014" src="http://www.madeira.co.il/wp-content/uploads/2012/01/NestedLoop-VarTable-300x68.jpg" alt="" width="300" height="68" /></a></strong></p>
<p dir="ltr"><strong> </strong></p>
<p dir="ltr">This is true for both UnCompiled and PreCompiled runs, but the overhead of recompiling is still great for the Staging and Temp tables.</p>
<p><strong> </strong></p>
<p dir="ltr"><strong>Conclusion</strong></p>
<p dir="ltr">So… which is best?</p>
<p dir="ltr">The Staging table is clearly not the best choice. Having to keep statistics updated all the time is too big of an overhead for every kind of record count and every kind of procedure. In addition, there is the cost of truncating the table before (or after) every calculation. This was not measured in my little test to make the procedure as identical as possible. Naturally, this will make using a Staging table even more inefficient.</p>
<p dir="ltr">The Var table seems to be the best choice for simple calculation when there is only one way to execute the procedure. It is also the best choice when for some reason, there is no execution plan in the cache, but this is a rare event for most procedures.</p>
<p dir="ltr">The Temp table is the best choice when the procedure can be executed in more than one way. Here, updating the statistics is an overhead worth paying.</p>
<p dir="ltr"><strong>Bottom Line</strong></p>
<p dir="ltr">As a “rule of thumb” I would use the Temp table. Usually we already have the plan in cache, so we don’t have to worry about recompilation. The advantage the Var table has over the Temp table when it comes to simple calculations is minor compared to the huge advantage the Temp table has for complex ones.</p>
<p dir="ltr">Only if you are sure SQL will choose the right execution plan, use the Var table. In any other case a Temp table is preferable.</p>
<p dir="ltr"><strong>The “Catch”</strong></p>
<p dir="ltr">When using a table variable inside a transaction, its behavior is different than a regular or a temporary table.</p>
<p dir="ltr">Curious??</p>
<p dir="ltr">Wait for my next post…</p>
</div>]]></content:encoded>			<wfw:commentRss>http://www.madeira.co.il/staging-temp-and-variable-tables-performance-recompilation-and-what%e2%80%99s-in-between-4/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
	</channel>
</rss>

