<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">

  <title><![CDATA[Ramblings of a SQL Hillbilly]]></title>
  <link href="http://sqlhillbilly.com/atom.xml" rel="self"/>
  <link href="http://sqlhillbilly.com/"/>
  <updated>2015-04-13T21:45:38-05:00</updated>
  <id>http://sqlhillbilly.com/</id>
  <author>
    <name><![CDATA[JK Wood]]></name>
    
  </author>
  <generator uri="http://octopress.org/">Octopress</generator>

  
  <entry>
    <title type="html"><![CDATA[Temporary Query Items: Table Variables]]></title>
    <link href="http://sqlhillbilly.com/blog/2014/08/22/temporary-query-items-table-variables/"/>
    <updated>2014-08-22T15:53:46-05:00</updated>
    <id>http://sqlhillbilly.com/blog/2014/08/22/temporary-query-items-table-variables</id>
    <content type="html"><![CDATA[<p>Note: This is part of a series on <a href="http://sqlhillbilly.com/tqi/" title="Temporary Query Items">Temporary Query Items</a>.</p>

<p><a href="http://sqlhillbilly.com/tqi/" title="TQI home">Previously</a> we&rsquo;ve talked about Temporary Query Items, what they are, why they matter, and the various factors that might cause you to choose one over another. We&rsquo;ve also already talked about our first TQI, <a href="http://sqlhillbilly.com/blog/2014/08/22/temporary-query-items-temporary-tables/" title="Temp Tables">temporary tables</a>. Without further ado, it&rsquo;s time for our next one &ndash; the Table Variable.</p>

<!-- more -->


<h3>A Little History</h3>

<p>Table variables have been present in SQL Server since at least SQL Server 2000. You should be able to make use of them everywhere.</p>

<h3>Location, Location, Location</h3>

<p><a href="https://www.flickr.com/photos/mrjennings/62669688/in/photolist-6xcvS-4joL8n-eb4GVE-aFKTug-6xcup-bRhAsR-74Yikz-77iPKx-eC4qHe-53Yd6H-99m8mH-76ofz6-dSnr4A-5V16oA-81Xg5i-7jNzz2-8vsu7z-6DU4sA-7jDRMX-7awngW-77Hc4T-7a2c3U-7eTS9k-7jTvay-5BvFPv-8UP5AH-7jTtPL-5V16fb-8USa2W-8iRxoF-dyjsbA-73ae4b-5k1dXj-senyf-7xE1wK-4TcReb-74Yj9X-6XkzGA-DhBFz-eC7Bcq-eC4rJa-iFzfw3-4qJbkx-cLQPFs-aov8re-LT8c1-LT8cb-LT8bQ-LT8bJ-LT8bW" title="Pretty sure Aeschylus started it."><img class="left" src="http://sqlhillbilly.com/images/aeschylus.jpg"></a></p>

<p>Table variables live in TempDB. There&rsquo;s an ancient myth that says table variables only live in memory &ndash; that&rsquo;s not entirely true. Like any other data set in use, a table variable may be cached in memory, but its actual home address is TempDB. As with temporary tables, this means that table variables can more easily cause TempDB contention than other TQIs.</p>

<p>Unlike temporary tables, table variables are local in scope. You can only use them in the originating connection.</p>

<h3>How Long Does It Live?</h3>

<p>Table variables don&rsquo;t last very long relative to temporary tables, but they do have a fair amount of longevity. When you create a table variable, the data contained within will exist until the completion of the transaction. You can refer to it in multiple queries within one transaction, but it will disappear at the end of transaction.</p>

<h3>Indexing</h3>

<p>Table variables do support clustered indexes, but ONLY when the index is defined in the variable declaration. This actually improves somewhat in SQL Server 2014, where we have the ability to define non-clustered indexes directly in our table create statements &ndash; and therefore, we are able to define non-clustered indexes on table variables there.</p>

<p>One thing that table variables do NOT support is column statistics. This can be a pain point due to the fact that the query optimizer can&rsquo;t determine how many rows to expect out of a table variable, so it assumes that the number will always be 1. 1 is a terrible default, but it&rsquo;s better than all the other potential defaults. Large result sets stored in table variables can lead to performance problems depending on the execution plan that is chosen.</p>

<h3>Copy That</h3>

<p>Like temporary tables, when you create a table variable based on an existing data set, it is created as a separate physical copy of the data. Modifications to the table variable do not affect the original data source. This can be either a strength or a weakness , depending on your purposes for using a TQI.</p>

<h3>But Does It Blend?</h3>

<p>The data in a table variable is fully modifiable. However, modifications to the table variable result set do not automatically translate back to the original data used to populate it. This can definitely be either a point for or against your choice of TQI.</p>

<h3>Show Me The Code!</h3>

<h4>Test Data Setup</h4>

<p>I&rsquo;ll be using a test table to do some demonstrations. You can create/populate it here:</p>

<figure class='code'><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
</pre></td><td class='code'><pre><code class='sql'><span class='line'><span class="k">create</span> <span class="k">table</span> <span class="n">dbo</span><span class="p">.</span><span class="n">JK_Temp_Stuff_Test_Table</span> <span class="p">(</span>
</span><span class='line'>       <span class="n">myIntKey</span> <span class="nb">int</span> <span class="k">PRIMARY</span> <span class="k">KEY</span> <span class="n">CLUSTERED</span><span class="p">,</span>
</span><span class='line'>       <span class="n">myVarchar</span> <span class="nb">varchar</span><span class="p">(</span><span class="mi">10</span><span class="p">)</span> <span class="k">not</span> <span class="k">null</span>
</span><span class='line'><span class="p">);</span>
</span><span class='line'>
</span><span class='line'><span class="k">insert</span> <span class="k">into</span> <span class="n">dbo</span><span class="p">.</span><span class="n">JK_Temp_Stuff_Test_Table</span> <span class="p">(</span><span class="n">myIntKey</span><span class="p">,</span> <span class="n">myVarchar</span><span class="p">)</span>
</span><span class='line'><span class="k">values</span> <span class="p">(</span><span class="mi">5</span><span class="p">,</span> <span class="s1">&#39;Wheeee&#39;</span><span class="p">);</span>
</span><span class='line'>
</span><span class='line'><span class="k">create</span> <span class="k">table</span> <span class="n">dbo</span><span class="p">.</span><span class="n">JK_Temp_Stuff_Test_Table_2</span> <span class="p">(</span>
</span><span class='line'>       <span class="n">myIntKey</span> <span class="nb">int</span> <span class="k">PRIMARY</span> <span class="k">KEY</span> <span class="n">CLUSTERED</span><span class="p">,</span>
</span><span class='line'>       <span class="n">myOtherVarchar</span> <span class="nb">varchar</span><span class="p">(</span><span class="mi">10</span><span class="p">)</span> <span class="k">not</span> <span class="k">null</span>
</span><span class='line'><span class="p">);</span>
</span><span class='line'>
</span><span class='line'><span class="k">insert</span> <span class="k">into</span> <span class="n">dbo</span><span class="p">.</span><span class="n">JK_Temp_Stuff_Test_Table_2</span> <span class="p">(</span><span class="n">myIntKey</span><span class="p">,</span> <span class="n">myOtherVarchar</span><span class="p">)</span>
</span><span class='line'><span class="k">values</span> <span class="p">(</span><span class="mi">5</span><span class="p">,</span> <span class="s1">&#39;Waffle&#39;</span><span class="p">);</span>
</span></code></pre></td></tr></table></div></figure>


<h4>Creation</h4>

<p>The creation syntax for a table variable is a hybrid between a CREATE TABLE statement and a DECLARE statement &ndash; which makes sense, as a table variable is a table that happens to be a variable.</p>

<figure class='code'><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
</pre></td><td class='code'><pre><code class='sql'><span class='line'><span class="k">declare</span> <span class="o">@</span><span class="n">JK_Test_Table_Variable</span> <span class="k">TABLE</span> <span class="p">(</span>
</span><span class='line'>       <span class="n">myIntKey</span> <span class="nb">int</span> <span class="k">PRIMARY</span> <span class="k">KEY</span> <span class="n">CLUSTERED</span><span class="p">,</span>
</span><span class='line'>       <span class="n">myVarchar</span> <span class="nb">varchar</span><span class="p">(</span><span class="mi">10</span><span class="p">)</span> <span class="k">not</span> <span class="k">null</span>
</span><span class='line'><span class="p">);</span>
</span></code></pre></td></tr></table></div></figure>


<p>Note that you CANNOT do a SELECT INTO with a table variable.</p>

<h4>Modification</h4>

<p>Inserting into a table variable is just like inserting into a regular table. However, note that you have to do the INSERT in the same transaction as the CREATE.</p>

<figure class='code'><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
</pre></td><td class='code'><pre><code class='sql'><span class='line'><span class="k">declare</span> <span class="o">@</span><span class="n">JK_Test_Table_Variable</span> <span class="k">TABLE</span> <span class="p">(</span>
</span><span class='line'>       <span class="n">myIntKey</span> <span class="nb">int</span> <span class="k">PRIMARY</span> <span class="k">KEY</span> <span class="n">CLUSTERED</span><span class="p">,</span>
</span><span class='line'>       <span class="n">myVarchar</span> <span class="nb">varchar</span><span class="p">(</span><span class="mi">10</span><span class="p">)</span> <span class="k">not</span> <span class="k">null</span>
</span><span class='line'><span class="p">);</span>
</span><span class='line'>
</span><span class='line'><span class="k">insert</span> <span class="k">into</span> <span class="o">##</span><span class="n">JK_Test_Temp_Table</span> <span class="p">(</span><span class="n">myIntKey</span><span class="p">,</span> <span class="n">myVarchar</span><span class="p">)</span>
</span><span class='line'><span class="k">values</span> <span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="s1">&#39;A&#39;</span><span class="p">),</span>
</span><span class='line'>       <span class="p">(</span><span class="mi">2</span><span class="p">,</span> <span class="s1">&#39;B&#39;</span><span class="p">),</span>
</span><span class='line'>       <span class="p">(</span><span class="mi">3</span><span class="p">,</span> <span class="s1">&#39;A&#39;</span><span class="p">),</span>
</span><span class='line'>       <span class="p">(</span><span class="mi">4</span><span class="p">,</span> <span class="s1">&#39;C&#39;</span><span class="p">);</span>
</span></code></pre></td></tr></table></div></figure>


<p>Table variables are also updateable:</p>

<figure class='code'><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
<span class='line-number'>16</span>
<span class='line-number'>17</span>
<span class='line-number'>18</span>
</pre></td><td class='code'><pre><code class='sql'><span class='line'><span class="k">declare</span> <span class="o">@</span><span class="n">JK_Test_Table_Variable</span> <span class="k">TABLE</span> <span class="p">(</span>
</span><span class='line'>       <span class="n">myIntKey</span> <span class="nb">int</span> <span class="k">PRIMARY</span> <span class="k">KEY</span> <span class="n">CLUSTERED</span><span class="p">,</span>
</span><span class='line'>       <span class="n">myVarchar</span> <span class="nb">varchar</span><span class="p">(</span><span class="mi">10</span><span class="p">)</span> <span class="k">not</span> <span class="k">null</span>
</span><span class='line'><span class="p">);</span>
</span><span class='line'>
</span><span class='line'><span class="k">insert</span> <span class="k">into</span> <span class="o">@</span><span class="n">JK_Test_Table_Variable</span> <span class="p">(</span><span class="n">myIntKey</span><span class="p">,</span> <span class="n">myVarchar</span><span class="p">)</span>
</span><span class='line'><span class="k">values</span> <span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="s1">&#39;A&#39;</span><span class="p">),</span>
</span><span class='line'>       <span class="p">(</span><span class="mi">2</span><span class="p">,</span> <span class="s1">&#39;B&#39;</span><span class="p">),</span>
</span><span class='line'>       <span class="p">(</span><span class="mi">3</span><span class="p">,</span> <span class="s1">&#39;A&#39;</span><span class="p">),</span>
</span><span class='line'>       <span class="p">(</span><span class="mi">4</span><span class="p">,</span> <span class="s1">&#39;C&#39;</span><span class="p">);</span>
</span><span class='line'>
</span><span class='line'><span class="k">update</span> <span class="n">tgt</span>
</span><span class='line'>   <span class="k">set</span> <span class="n">MyVarchar</span> <span class="o">=</span> <span class="s1">&#39;D&#39;</span>
</span><span class='line'> <span class="k">from</span> <span class="o">@</span><span class="n">JK_Test_Table_Variable</span> <span class="n">tgt</span>
</span><span class='line'><span class="k">where</span> <span class="n">myIntKey</span> <span class="o">=</span> <span class="mi">4</span><span class="p">;</span>
</span><span class='line'>
</span><span class='line'><span class="k">select</span> <span class="o">*</span>
</span><span class='line'>  <span class="k">from</span> <span class="o">@</span><span class="n">JK_Test_Table_Variable</span><span class="p">;</span>
</span></code></pre></td></tr></table></div></figure>


<h4>Indexing</h4>

<p>Unfortunately, you cannot index a table variable after creation. This means that prior to SQL Server 2014, you cannot add nonclustered indexes to table variables. You can, however, declare a table variable with a clustered or unique index on it:</p>

<figure class='code'><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
</pre></td><td class='code'><pre><code class='sql'><span class='line'><span class="k">declare</span> <span class="o">@</span><span class="n">JK_Test_Table_Variable</span> <span class="k">TABLE</span> <span class="p">(</span>
</span><span class='line'>       <span class="n">myIntKey</span> <span class="nb">int</span> <span class="k">PRIMARY</span> <span class="k">KEY</span> <span class="n">CLUSTERED</span><span class="p">,</span>
</span><span class='line'>       <span class="n">myVarchar</span> <span class="nb">varchar</span><span class="p">(</span><span class="mi">10</span><span class="p">)</span> <span class="k">not</span> <span class="k">null</span>
</span><span class='line'><span class="p">);</span>
</span><span class='line'>
</span><span class='line'><span class="c1">-- Fails</span>
</span><span class='line'><span class="k">create</span> <span class="n">nonclustered</span> <span class="k">index</span> <span class="n">JK_Test_Table_Variable_SIDX_1</span> <span class="k">on</span> <span class="o">@</span><span class="n">JK_Test_Table_Variable</span> <span class="p">(</span>
</span><span class='line'>       <span class="n">myVarchar</span> <span class="k">asc</span>
</span><span class='line'><span class="p">);</span>
</span></code></pre></td></tr></table></div></figure>


<p><img class="right" src="http://sqlhillbilly.com/images/tablevariablestats.png" title="&#34;Two for One Sale, Sunday only!&#34;" alt="&#34;Two for One Sale, Sunday only!&#34;"></p>

<p>It is also not possible to create statistics on a table variable. Therefore, the query optimizer will always estimate that there is 1 row in the table variable, and therefore it may make some very bad plan choices. You&rsquo;ll find that a lot of people insist that table variables are bad for performance &ndash; this is usually the reason. A table variable with millions of rows is almost always going to be slower than about any other option.</p>

<h4>Global vs Local</h4>

<p>Table Variables are only local. They&rsquo;re so local, actually, that their use is limited to the transaction they&rsquo;re created in.</p>

<h4>Longevity</h4>

<p>No really, only the creating transaction. The insert will fail:</p>

<figure class='code'><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
</pre></td><td class='code'><pre><code class='sql'><span class='line'><span class="k">declare</span> <span class="o">@@</span><span class="n">JK_Test_Table_Variable</span> <span class="k">TABLE</span> <span class="p">(</span>
</span><span class='line'>       <span class="n">myIntKey</span> <span class="nb">int</span> <span class="k">PRIMARY</span> <span class="k">KEY</span> <span class="n">CLUSTERED</span><span class="p">,</span>
</span><span class='line'>       <span class="n">myVarchar</span> <span class="nb">varchar</span><span class="p">(</span><span class="mi">10</span><span class="p">)</span> <span class="k">not</span> <span class="k">null</span>
</span><span class='line'><span class="p">);</span>
</span><span class='line'><span class="k">go</span>
</span><span class='line'>
</span><span class='line'><span class="k">insert</span> <span class="k">into</span> <span class="o">@@</span><span class="n">JK_Test_Table_Variable</span> <span class="p">(</span><span class="n">myIntKey</span><span class="p">,</span> <span class="n">myVarchar</span><span class="p">)</span>
</span><span class='line'><span class="k">values</span> <span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="s1">&#39;A&#39;</span><span class="p">),</span>
</span><span class='line'>       <span class="p">(</span><span class="mi">2</span><span class="p">,</span> <span class="s1">&#39;B&#39;</span><span class="p">),</span>
</span><span class='line'>       <span class="p">(</span><span class="mi">3</span><span class="p">,</span> <span class="s1">&#39;A&#39;</span><span class="p">),</span>
</span><span class='line'>       <span class="p">(</span><span class="mi">4</span><span class="p">,</span> <span class="s1">&#39;C&#39;</span><span class="p">);</span>
</span><span class='line'><span class="k">go</span>
</span></code></pre></td></tr></table></div></figure>


<h4>Source Data Dependence</h4>

<p>As with temporary tables, data set modifications are done independently of the original data. This should be pretty clear from the INSERT INTO syntax:</p>

<figure class='code'><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
</pre></td><td class='code'><pre><code class='sql'><span class='line'><span class="k">declare</span> <span class="o">@</span><span class="n">JK_Test_Table_Variable</span> <span class="k">TABLE</span> <span class="p">(</span>
</span><span class='line'>       <span class="n">myIntKey</span> <span class="nb">int</span> <span class="k">PRIMARY</span> <span class="k">KEY</span> <span class="n">CLUSTERED</span><span class="p">,</span>
</span><span class='line'>       <span class="n">myVarchar</span> <span class="nb">varchar</span><span class="p">(</span><span class="mi">10</span><span class="p">)</span> <span class="k">not</span> <span class="k">null</span>
</span><span class='line'><span class="p">);</span>
</span><span class='line'>
</span><span class='line'><span class="k">insert</span> <span class="k">into</span> <span class="o">@</span><span class="n">JK_Test_Table_Variable</span>
</span><span class='line'><span class="k">select</span> <span class="o">*</span>
</span><span class='line'>  <span class="k">from</span> <span class="n">dbo</span><span class="p">.</span><span class="n">JK_Temp_Stuff_Test_Table</span><span class="p">;</span>
</span><span class='line'>
</span><span class='line'><span class="k">update</span> <span class="o">@</span><span class="n">JK_Test_Table_Variable</span>
</span><span class='line'>   <span class="k">set</span> <span class="n">myVarchar</span> <span class="o">=</span> <span class="s1">&#39;Whooo&#39;</span><span class="p">;</span>
</span><span class='line'>
</span><span class='line'><span class="k">select</span> <span class="o">*</span> <span class="k">from</span> <span class="o">@</span><span class="n">JK_Test_Table_Variable</span><span class="p">;</span>
</span><span class='line'><span class="k">select</span> <span class="o">*</span> <span class="k">from</span> <span class="n">dbo</span><span class="p">.</span><span class="n">JK_Temp_Stuff_Test_Table</span><span class="p">;</span>
</span></code></pre></td></tr></table></div></figure>


<h4>Cleanup</h4>

<p>And here&rsquo;s the cleanup code for this exercise:</p>

<figure class='code'><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
</pre></td><td class='code'><pre><code class='sql'><span class='line'><span class="k">drop</span> <span class="k">table</span> <span class="n">dbo</span><span class="p">.</span><span class="n">JK_Temp_Stuff_Test_Table</span><span class="p">;</span>
</span><span class='line'><span class="k">drop</span> <span class="k">table</span> <span class="n">dbo</span><span class="p">.</span><span class="n">JK_Temp_Stuff_Test_Table_2</span><span class="p">;</span>
</span></code></pre></td></tr></table></div></figure>


<h3>Moving On</h3>

<p><a href="https://www.flickr.com/photos/nebarnix/5357842950/in/photolist-heQBk-bLQXQP-bxWhzN-9apbXp-pTi1W-9asm3A-9askYo-vS2hS-che8JQ-bLQYe2-bLQXYF-che8N5-che8yS-che8FQ-che86b-che8mw-4YUuT-gN89vx-aeef6U-2FnwCC-4noLs8-2SBz8C-iU9zJn-iUce5J-iU8eZr-iU9wj6-iU8qup-iUatH3-9LcBSy-9L9QwV-9L9QMr-9L9QK6-9LcBFh-9LcC6U-9L9QfP-9L9QyB-9LcBD5-5kymy-iUam7w-iUci5Y-iUc6mY-iUc9ij-iU8hJp-iUciZd-iUc7ju-iUauS7-iUck6G-iUazXh-4nsQis-4noPsX" title="You could, however, make your decision in a vacuum chamber."><img class="left" src="http://sqlhillbilly.com/images/vacuumchamber.jpg"></a></p>

<p>We&rsquo;ve taken some time to look at some attributes of Table Variables, including strengths and weaknesses. As with Temporary Tables, you can&rsquo;t make a decision about using them in a vacuum. In particular, I&rsquo;ve cautioned against the use of table variables for large data sets &ndash; but for very small data sets, table variables can be very useful. I particularly like them for use with the OUTPUT clause of the MERGE statement when trying to log row counts in SSIS.</p>

<p>Next up: Views.</p>
]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[Temporary Query Items: Temporary Tables]]></title>
    <link href="http://sqlhillbilly.com/blog/2014/08/22/temporary-query-items-temporary-tables/"/>
    <updated>2014-08-22T08:09:16-05:00</updated>
    <id>http://sqlhillbilly.com/blog/2014/08/22/temporary-query-items-temporary-tables</id>
    <content type="html"><![CDATA[<p>Note: This is part of a series on <a href="http://sqlhillbilly.com/tqi/" title="Temporary Query Items">Temporary Query Items</a>.</p>

<p><a href="http://sqlhillbilly.com/tqi/" title="Temporary Query Items blog posts">Previously, on TQI&hellip;</a></p>

<p>We&rsquo;ve talked previously about what a Temporary Query Item is and how scope, indexing, statistics, TempDB, and memory can all affect your choice of TQI for your queries. It&rsquo;s all been important discussion, if a little unsexy &ndash; go back and read it if you haven&rsquo;t, or you may get a little lost. If you have (no really, read it first!), then you&rsquo;re ready to walk through our first Temporary Query Item &ndash; Temporary Tables.</p>

<!-- more -->


<p></p>

<h3>A Little History</h3>

<p>Temporary Tables have been present in SQL Server since at least SQL Server 2000. You should be able to make use of them everywhere.</p>

<h3>Location, Location, Location</h3>

<p>Temporary Tables live in TempDB. This is not necessarily a performance problem, as even objects in TempDB can be cached in memory. However, if too much is going on in TempDB, this can cause a performance loss due to contention. If WAY too much is going on in TempDB, use of a temporary table can actually cause you to blow TempDB, which means your query will fail. <a href="https://www.youtube.com/watch?v=Z01sQpuBe4s" title="Death By Cable">Don&rsquo;t cause your query to fail.</a> Using a temporary table certainly doesn&rsquo;t guarantee problems, but you do want to be careful.</p>

<p>Regarding location scope, temporary tables give you a little bit of flexibility. You can declare your temporary table as global or local. A global temporary table can be accessed from other connections than the creating one. A local temporary table can only be accessed from the creating connection, although it can be used and reused as long as the connection is open.</p>

<h4>Global</h4>

<p><a href="https://www.flickr.com/photos/katerha/9699715487/in/photolist-fM8ztV-9NBhX8-7kwGkj-5moM4X-2NTGRm-4eueqq-e5mcue-9NB2Mn-665Hq5-afHFsG-afN6pN-udeXq-9NB2i2-9ap5iB-afMBAo-iCua5-5csccu-9NBBX2-3e3cHN-3p34ph-9NBCHX-2xrzp-9NDB2C-5YgEJo-9NDPcb-32RiHq-9NBhDT-9ap1fM-94Y1DA-9ap4Hr-9ap4YF-9ascXb-9ap3cH-9asaau-9ap15x-aEK375-9NEpWE-9ap1Wa-9ap3An-9ap2wD-9ap2W2-9ap3Pc-9as9xs-9asdSA-9asbaL-9asayw-9asegy-9ascbE-9ap1AT-9asaWC" title="Big data"><img class="right" src="http://sqlhillbilly.com/images/bigdill.jpg"></a></p>

<p>Global temporary tables are named with a ## prefix. So, if I want a global temporary table to hold pickles, I would name it ##Pickles. Once created, this table will hang around as long as something is using it, and anyone can make use of it.</p>

<h4>Local</h4>

<p>Local temporary tables are named with a # prefix. My same temp table for holding delicious pickles would be named #Pickles. Once created, this table can only be accessed from the creating connection. They can create their own #Pickles table for THEIR delicious pickles, because behind the scenes SQL Server will postfix your local temporary table name with a long string of underscores and a hexadecimal number, and postfix theirs with a different long string of underscores and a hexadecimal number. BIG HAIR DEAL: Clustered indexes on local temp tables are global in scope. This means that two or more people attempt to create local temporary tables with identically named clustered indexes, the runner-up loses and cannot create the table.</p>

<h3>How Long Does It Live?</h3>

<p>Temporary tables present a fair amount of longevity. If SQL Server stops, then your temp table will go away. If you close the creating connection, the temp table will go away. If you drop the temp table, it will also go away. Otherwise, temporary tables will happily sit there forever occupying TempDB. This makes it one of the longest-lived TQIs, which is one of its biggest strengths.</p>

<p>I should note here that if a global temporary table is actively being referenced when dropped or the creating connection is closed, the table will actually persist until it is no longer being actively referenced. This is a little nicer than getting an error because the object suddenly doesn&rsquo;t exist in the middle of your query. It could also mean that you insert into something that immediately stops existing, though.</p>

<h3>Indexing</h3>

<p>Another strength of temporary tables is that they can be indexed. Specifically, temporary tables can have both clustered and nonclustered indexes applied to them. They can also be created with a clustered index already on them. I&rsquo;ve used this to my advantage before by making a temporary table with a copy of the data I want, then indexing it to fit my series of queries.</p>

<h3>Copy That</h3>

<p>When you create a temporary table based on an existing data set, it is created as a separate physical copy of the data. Modifications to the temp table do not affect the original data source. This can be either a strength or a weakness , depending on your purposes for using a TQI.</p>

<h3>But Does It Blend?</h3>

<p>The data contained within a temporary table is modifiable. You can insert, update, or delete records within. And you can dance if you want to.</p>

<p>As mentioned above, modifications to the temporary table does not automatically translate back to the original data used to populate it.</p>

<h3>Show Me The Code!</h3>

<h4>Test Data Setup</h4>

<p>I&rsquo;ll be using a test table to do some demonstrations. You can create/populate it here:</p>

<figure class='code'><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
</pre></td><td class='code'><pre><code class='sql'><span class='line'><span class="k">create</span> <span class="k">table</span> <span class="n">dbo</span><span class="p">.</span><span class="n">JK_Temp_Stuff_Test_Table</span> <span class="p">(</span>
</span><span class='line'>       <span class="n">myIntKey</span> <span class="nb">int</span> <span class="k">PRIMARY</span> <span class="k">KEY</span> <span class="n">CLUSTERED</span><span class="p">,</span>
</span><span class='line'>       <span class="n">myVarchar</span> <span class="nb">varchar</span><span class="p">(</span><span class="mi">10</span><span class="p">)</span> <span class="k">not</span> <span class="k">null</span>
</span><span class='line'><span class="p">);</span>
</span><span class='line'>
</span><span class='line'><span class="k">insert</span> <span class="k">into</span> <span class="n">dbo</span><span class="p">.</span><span class="n">JK_Temp_Stuff_Test_Table</span> <span class="p">(</span><span class="n">myIntKey</span><span class="p">,</span> <span class="n">myVarchar</span><span class="p">)</span>
</span><span class='line'><span class="k">values</span> <span class="p">(</span><span class="mi">5</span><span class="p">,</span> <span class="s1">&#39;Wheeee&#39;</span><span class="p">);</span>
</span><span class='line'>
</span><span class='line'><span class="k">create</span> <span class="k">table</span> <span class="n">dbo</span><span class="p">.</span><span class="n">JK_Temp_Stuff_Test_Table_2</span> <span class="p">(</span>
</span><span class='line'>       <span class="n">myIntKey</span> <span class="nb">int</span> <span class="k">PRIMARY</span> <span class="k">KEY</span> <span class="n">CLUSTERED</span><span class="p">,</span>
</span><span class='line'>       <span class="n">myOtherVarchar</span> <span class="nb">varchar</span><span class="p">(</span><span class="mi">10</span><span class="p">)</span> <span class="k">not</span> <span class="k">null</span>
</span><span class='line'><span class="p">);</span>
</span><span class='line'>
</span><span class='line'><span class="k">insert</span> <span class="k">into</span> <span class="n">dbo</span><span class="p">.</span><span class="n">JK_Temp_Stuff_Test_Table_2</span> <span class="p">(</span><span class="n">myIntKey</span><span class="p">,</span> <span class="n">myOtherVarchar</span><span class="p">)</span>
</span><span class='line'><span class="k">values</span> <span class="p">(</span><span class="mi">5</span><span class="p">,</span> <span class="s1">&#39;Waffle&#39;</span><span class="p">);</span>
</span></code></pre></td></tr></table></div></figure>


<h4>Creation</h4>

<p>Temporary tables can be created in a very similar way to plain old tables. You can do a standard create statement:</p>

<figure class='code'><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
</pre></td><td class='code'><pre><code class='sql'><span class='line'><span class="k">create</span> <span class="k">table</span> <span class="o">##</span><span class="n">JK_Test_Temp_Table</span> <span class="p">(</span>
</span><span class='line'>       <span class="n">myIntKey</span> <span class="nb">int</span> <span class="k">not</span> <span class="k">null</span><span class="p">,</span>
</span><span class='line'>       <span class="n">myVarchar</span> <span class="nb">varchar</span><span class="p">(</span><span class="mi">10</span><span class="p">)</span> <span class="k">not</span> <span class="k">null</span>
</span><span class='line'><span class="k">CONSTRAINT</span> <span class="p">[</span><span class="n">PK_JK_Test_Temp_Table</span><span class="p">]</span> <span class="k">PRIMARY</span> <span class="k">KEY</span> <span class="n">CLUSTERED</span>
</span><span class='line'><span class="p">(</span>
</span><span class='line'>  <span class="p">[</span><span class="n">myIntKey</span><span class="p">]</span> <span class="k">ASC</span>
</span><span class='line'><span class="p">)</span>
</span><span class='line'><span class="p">);</span>
</span></code></pre></td></tr></table></div></figure>


<p>As it turns out, you can also do SELECT INTO with a temp table. I&rsquo;ll show that later.</p>

<h4>Modification</h4>

<p>Inserting into a temporary table is just like regular tables as well.</p>

<figure class='code'><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
</pre></td><td class='code'><pre><code class='sql'><span class='line'><span class="k">insert</span> <span class="k">into</span> <span class="o">##</span><span class="n">JK_Test_Temp_Table</span> <span class="p">(</span><span class="n">myIntKey</span><span class="p">,</span> <span class="n">myVarchar</span><span class="p">)</span>
</span><span class='line'><span class="k">values</span> <span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="s1">&#39;A&#39;</span><span class="p">),</span>
</span><span class='line'>       <span class="p">(</span><span class="mi">2</span><span class="p">,</span> <span class="s1">&#39;B&#39;</span><span class="p">),</span>
</span><span class='line'>       <span class="p">(</span><span class="mi">3</span><span class="p">,</span> <span class="s1">&#39;A&#39;</span><span class="p">),</span>
</span><span class='line'>       <span class="p">(</span><span class="mi">4</span><span class="p">,</span> <span class="s1">&#39;C&#39;</span><span class="p">);</span>
</span></code></pre></td></tr></table></div></figure>


<p>Temporary tables are also updateable:</p>

<figure class='code'><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
</pre></td><td class='code'><pre><code class='sql'><span class='line'><span class="k">update</span> <span class="n">tgt</span>
</span><span class='line'>   <span class="k">set</span> <span class="n">myVarchar</span> <span class="o">=</span> <span class="s1">&#39;D&#39;</span>
</span><span class='line'>  <span class="k">from</span> <span class="o">##</span><span class="n">JK_Test_Temp_Table</span> <span class="n">tgt</span>
</span><span class='line'> <span class="k">where</span> <span class="n">myInt</span> <span class="o">=</span> <span class="mi">4</span><span class="p">;</span>
</span><span class='line'>
</span><span class='line'><span class="k">select</span> <span class="o">*</span>
</span><span class='line'>  <span class="k">from</span> <span class="o">##</span><span class="n">JK_Test_Temp_Table</span><span class="p">;</span>
</span></code></pre></td></tr></table></div></figure>


<h4>Indexing</h4>

<p>You can create indexes on your temp table after the fact. Run the SELECT with the execution plan, create the index, then rerun the SELECT and note the change in execution plan. BIG HAIRY WARNING: If you attempt to create an index on a temp table with the execution plan enabled, SSMS will blow up.</p>

<figure class='code'><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
</pre></td><td class='code'><pre><code class='sql'><span class='line'><span class="c1">-- Enable Execution Plan</span>
</span><span class='line'><span class="k">select</span> <span class="o">*</span>
</span><span class='line'>  <span class="k">from</span> <span class="o">##</span><span class="n">JK_Test_Temp_Table</span>
</span><span class='line'><span class="k">where</span> <span class="n">myVarchar</span> <span class="o">=</span> <span class="s1">&#39;A&#39;</span><span class="p">;</span>
</span><span class='line'>
</span><span class='line'><span class="c1">-- Disable Execution Plan</span>
</span><span class='line'><span class="k">create</span> <span class="n">nonclustered</span> <span class="k">index</span> <span class="n">JK_Test_Temp_Table_SIDX_1</span> <span class="k">on</span> <span class="o">##</span><span class="n">JK_Test_Temp_Table</span><span class="p">(</span>
</span><span class='line'>       <span class="n">myVarchar</span> <span class="k">asc</span>
</span><span class='line'><span class="p">);</span>
</span><span class='line'>
</span><span class='line'><span class="c1">-- Enable Execution Plan</span>
</span><span class='line'><span class="k">select</span> <span class="o">*</span>
</span><span class='line'>  <span class="k">from</span> <span class="o">##</span><span class="n">JK_Test_Temp_Table</span>
</span><span class='line'><span class="k">where</span> <span class="n">myVarchar</span> <span class="o">=</span> <span class="s1">&#39;A&#39;</span><span class="p">;</span>
</span></code></pre></td></tr></table></div></figure>


<h4>Global vs Local</h4>

<p>So far, we&rsquo;ve been using a global temporary table. Here, we&rsquo;ll look at local temporary tables &ndash; run the CREATE and INSERT in one window, then run the SELECT in another:</p>

<figure class='code'><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
</pre></td><td class='code'><pre><code class='sql'><span class='line'><span class="k">create</span> <span class="k">table</span> <span class="o">#</span><span class="n">JK_Test_Temp_Table_2</span> <span class="p">(</span>
</span><span class='line'>       <span class="n">myIntKey</span> <span class="nb">int</span> <span class="k">not</span> <span class="k">null</span><span class="p">,</span>
</span><span class='line'>       <span class="n">myVarchar</span> <span class="nb">varchar</span><span class="p">(</span><span class="mi">10</span><span class="p">)</span> <span class="k">not</span> <span class="k">null</span>
</span><span class='line'><span class="k">CONSTRAINT</span> <span class="p">[</span><span class="n">PK_JK_Test_Temp_Table_2</span><span class="p">]</span> <span class="k">PRIMARY</span> <span class="k">KEY</span> <span class="n">CLUSTERED</span>
</span><span class='line'><span class="p">(</span>
</span><span class='line'>  <span class="p">[</span><span class="n">myIntKey</span><span class="p">]</span> <span class="k">ASC</span>
</span><span class='line'><span class="p">)</span>
</span><span class='line'><span class="p">);</span>
</span><span class='line'>
</span><span class='line'><span class="k">insert</span> <span class="k">into</span> <span class="o">#</span><span class="n">JK_Test_Temp_Table_2</span> <span class="p">(</span><span class="n">myIntKey</span><span class="p">,</span> <span class="n">myVarchar</span><span class="p">)</span>
</span><span class='line'><span class="k">values</span> <span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="s1">&#39;A&#39;</span><span class="p">),</span>
</span><span class='line'>       <span class="p">(</span><span class="mi">2</span><span class="p">,</span> <span class="s1">&#39;B&#39;</span><span class="p">),</span>
</span><span class='line'>       <span class="p">(</span><span class="mi">3</span><span class="p">,</span> <span class="s1">&#39;A&#39;</span><span class="p">),</span>
</span><span class='line'>       <span class="p">(</span><span class="mi">4</span><span class="p">,</span> <span class="s1">&#39;C&#39;</span><span class="p">);</span>
</span></code></pre></td></tr></table></div></figure>




<figure class='code'><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
</pre></td><td class='code'><pre><code class='sql'><span class='line'><span class="k">select</span> <span class="o">*</span>
</span><span class='line'>  <span class="k">from</span> <span class="o">#</span><span class="n">JK_Test_Temp_Table_2</span>
</span><span class='line'><span class="k">where</span> <span class="n">myVarchar</span> <span class="o">=</span> <span class="s1">&#39;A&#39;</span><span class="p">;</span>
</span></code></pre></td></tr></table></div></figure>


<h4>Longevity</h4>

<p>The temporary table goes away when you close the creating connection. Close your original window, then run this:</p>

<figure class='code'><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
</pre></td><td class='code'><pre><code class='sql'><span class='line'><span class="k">select</span> <span class="o">*</span>
</span><span class='line'>  <span class="k">from</span> <span class="o">##</span><span class="n">JK_Test_Temp_Table</span>
</span><span class='line'><span class="k">where</span> <span class="n">myVarchar</span> <span class="o">=</span> <span class="s1">&#39;A&#39;</span><span class="p">;</span>
</span></code></pre></td></tr></table></div></figure>


<h4>Source Data Dependence</h4>

<p>And finally, we can see modifying the data in the temporary table is independent of the data that created it. Note the SELECT INTO syntax:</p>

<figure class='code'><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
</pre></td><td class='code'><pre><code class='sql'><span class='line'><span class="k">select</span> <span class="o">*</span>
</span><span class='line'>  <span class="k">into</span> <span class="o">#</span><span class="n">JK_Test_Temp_Table_3</span>
</span><span class='line'> <span class="k">from</span> <span class="n">dbo</span><span class="p">.</span><span class="n">JK_Temp_Stuff_Test_Table</span><span class="p">;</span>
</span><span class='line'>
</span><span class='line'><span class="k">update</span> <span class="o">#</span><span class="n">JK_Test_Temp_Table_3</span>
</span><span class='line'>   <span class="k">set</span> <span class="n">myVarchar</span> <span class="o">=</span> <span class="s1">&#39;Whooo&#39;</span><span class="p">;</span>
</span><span class='line'>
</span><span class='line'><span class="k">select</span> <span class="o">*</span> <span class="k">from</span> <span class="o">#</span><span class="n">JK_Test_Temp_Table_3</span><span class="p">;</span>
</span><span class='line'><span class="k">select</span> <span class="o">*</span> <span class="k">from</span> <span class="n">dbo</span><span class="p">.</span><span class="n">JK_Temp_Stuff_Test_Table</span><span class="p">;</span>
</span></code></pre></td></tr></table></div></figure>


<h4>Cleanup</h4>

<p>And here&rsquo;s the cleanup code for this exercise:</p>

<figure class='code'><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
</pre></td><td class='code'><pre><code class='sql'><span class='line'><span class="k">drop</span> <span class="k">table</span> <span class="n">dbo</span><span class="p">.</span><span class="n">JK_Temp_Stuff_Test_Table</span><span class="p">;</span>
</span><span class='line'><span class="k">drop</span> <span class="k">table</span> <span class="n">dbo</span><span class="p">.</span><span class="n">JK_Temp_Stuff_Test_Table_2</span><span class="p">;</span>
</span></code></pre></td></tr></table></div></figure>


<h3>Moving On</h3>

<p>In this post, we&rsquo;ve focused on some of the strengths and weaknesses of temporary tables. While this is not sufficient information to decide that a temporary table would be a more or less advantageous choice of Temporary Query Item in all cases, it should give you some ideas on how to make use of them in your queries. Next up, we&rsquo;ll take a look at our next TQI: <a href="http://sqlhillbilly.com/blog/2014/08/22/temporary-query-items-table-variables/" title="Table Variables">Table Variables</a>.</p>
]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[Temporary Query Items: TempDB and Memory]]></title>
    <link href="http://sqlhillbilly.com/blog/2014/08/19/temporary-query-items-tempdb-and-memory/"/>
    <updated>2014-08-19T11:42:11-05:00</updated>
    <id>http://sqlhillbilly.com/blog/2014/08/19/temporary-query-items-tempdb-and-memory</id>
    <content type="html"><![CDATA[<p>Note: This is part of a series on <a href="http://sqlhillbilly.com/tqi/" title="Temporary Query Items">Temporary Query Items</a>.</p>

<p>In previous posts, I&rsquo;ve talked some about what Temporary Query Items are and why you might need them. I&rsquo;ve also given an overview of scope and how it might affect your choices of TQIs for your own queries. Likewise, I&rsquo;ve worked through indexing and statistics, a reasonable understanding of which can help to understand the advantagese and disadvantages of some TQIs. Now, I&rsquo;d like to talk a bit about TempDB and memory, and how they can also affect your choice of Temporary Query Item.</p>

<!-- more -->


<h3>Memory: Where The Party Is</h3>

<p><a href="https://www.flickr.com/photos/goingslo/8300907259/in/photolist-fzp6jE-dDwjwF-3ggSWv-6fh6L7-8an1cX-m5HDUK-gbiAea-5bqPgt-bngEkS-c4PL6f-jKUF25-7toejH-4CECDN-5KtAAz-dnETf6-nY8myk-ajgAaK-dcye5U-i9XgEj-7tC95Y-9DZKFx-73R7am-kko7Gx-hvcn2-5ynr6J-fywyM9-7Yqxqq-95YxsB-7z62bL-9mixCQ-8rbpu5-gnrbDV-3e2oJX-4i1Cfz-snfom-84ih65-6TByQJ-2aPHTq-j79f5G-7SneE2-i8WfwL-9v1q2E-aTjnLK-hZngT-93fEw5-apF7BQ-doYZmA-9DZKvM-9voDqr-9E3DaC" title="Not the performance boost you were hoping for"><img class="left" src="http://sqlhillbilly.com/images/ram.jpg"></a></p>

<p>Memory is the most critical hardware component when it comes to SQL Server. If you have more memory than you have data, then you won&rsquo;t have to worry about disk latency (much.) Memory is incredibly fast compared to every other piece of hardware you have (excepting CPU caches, but those are generally out of your control.) You want more memory. You always want more memory. More RAM.</p>

<p><a href="https://www.flickr.com/photos/chickie-poo/301429224/in/photolist-9FuwZb-sCUpA-98nZCK-kvqYR4-94S1qP-97LiUD-97LiER-97PrLU-8mw3rP-dgrERW-7AhE8M-3d4DuU-zUriU-6qicr8" title="Mo' betta, fo sho."><img class="right" src="http://sqlhillbilly.com/images/mobetta.jpg"></a></p>

<p>Right. So more RAM is mo&#8217; betta. But we have a 2 terabyte data warehouse &ndash; that really doesn&rsquo;t fit into RAM. And memory isn&rsquo;t just for storing data like it is stored on disk &ndash; we also have to store data in the form that we&rsquo;re querying. That means that we need a way to handle running out of memory for a query without crashing the whole system. What we have is TempDB.</p>

<h3>TempDB: The Public Toilet of SQL Server</h3>

<p><a href="https://www.flickr.com/photos/iguanajo/143370293/in/photolist-dENZB-asQ4s8-7eGszq-7eCxQM-4y4vQk-54YLV4-ebKX1A-76GfY5-asSFC9-5297L-4biDzd-5bSYa-4tCKp-aHXVjg-czbjr-bx5hYa-kAY8t-jfswob-9RpHda-nJfVQs-8MACp-dSUnRE-697J78-93p3b-nf6hP-fJT3C4-88Aiqw-7TiXMR-4Aa76p-4ZyD5w-asSHs3-bCot4w-N1A1m-4X2w1-6GiP18-ewurXG-bwuivR-cVZ9ZL-esFSDL-4upbo2-954RvC-2f7W83-ewrhFp-pkva4-esFNMs-mCE7T-8LiCoB-i9AyG-7AqBbB-dnDoUW" title="Good advice when you're thinking about shrinking tempdb"><img class="left" src="http://sqlhillbilly.com/images/pushsoft.jpg"></a></p>

<p>Brent Ozar describes TempDB as <a href="http://www.brentozar.com/archive/2013/12/video-the-secrets-of-tempdb/" title="I love this analogy">the public toilet</a> of SQL Server. Everything that needs to temporarily dump to disk in SQL Server uses TempDB. This includes index rebuilds, the version store, and even innocent little SELECT statements.</p>

<p><a href="https://www.flickr.com/photos/pagarneau/5057713273/in/photolist-8GW6WZ-muJFDc-8A4wLi-ebX3g4-9dvdkw-a7aLh7-kHaCF2-dx1BP4-95WwNp-8Tej3E-8qdg83-a93Hen-aBoGHX-9pd7w7-8A22kE-8H6R3W-8zi77i-8ttpTZ-dJrhie-8o7tbj-9eRgZu-e9dHyN-8pue9q-f1oC1f-nrGRY5-95ZzDo-8A5keS-obaVGR-bSApoM-dUW5tH-8mNwF8-8n85PY-8kC8ai-8iXkDc-8hNkzA-8pxfBX-aqU2nP-a77SAa-d6yPcf-8rn4oy-dXbT3u-kLi85B-fjTPVF-8A23uL-ejaLJW-8pCJTV-a9zeZ3-8nZNjF-i26Bsh-8L7knV" title="What's the most resilient parasite? Disk latency."><img class="right" src="http://sqlhillbilly.com/images/inception.jpg"></a></p>

<p>TempDB is slow, even if you put it on SSDs. Memory is a TON faster. You could theoretically allocate a RAMDisk for TempDB, but&hellip;</p>

<h3>Why Does It Matter?</h3>

<p>Okay, so memory is fast, TempDB is slow, but TempDB is there when we run out of memory. What does that have to do with Temporary Query Items? Good question!</p>

<p>Some of our TQIs live in TempDB. Some of them don&rsquo;t. What this means for you, is that your choice of Temporary Query Item might be influenced by how it uses or abuses TempDB.</p>

<h3>Moving On</h3>

<p>The only major concern that I haven&rsquo;t touched on when it comes to the differences among Temporary Query Items is syntax. Each one has unique ways in which to invoke it, and I could theoretically dedicate a post just to that. However, you&rsquo;ve been waiting for specifics, and specifics you shall have! I&rsquo;ll cover syntax of each TQI individually as I come to them.</p>

<p>First up: <a href="http://sqlhillbilly.com/blog/2014/08/22/temporary-query-items-temporary-tables/" title="Temp Tables">temporary tables</a>.</p>
]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[Temporary Query Items: Indexing and Statistics]]></title>
    <link href="http://sqlhillbilly.com/blog/2014/08/18/temporary-query-items-indexing-and-statistics/"/>
    <updated>2014-08-18T21:17:53-05:00</updated>
    <id>http://sqlhillbilly.com/blog/2014/08/18/temporary-query-items-indexing-and-statistics</id>
    <content type="html"><![CDATA[<p>Note: This is part of a series on <a href="http://sqlhillbilly.com/tqi/" title="Temporary Query Items">Temporary Query Items</a>.</p>

<p><a href="http://www.homestarrunner.com/sbemail84.html" title="Strong Bad teaches us about differences"><img class="right" src="http://sqlhillbilly.com/images/strongbaddifferences.jpg"></a></p>

<p>In my previous posts, I&rsquo;ve talked about <a href="http://sqlhillbilly.com/blog/2014/08/16/temporary-query-items-introduction/" title="Temporary Query Items: Introduction">what Temporary Query Items are</a> and how <a href="http://sqlhillbilly.com/blog/2014/08/17/temporary-query-items-scope/" title="Temporary Query Items: Scope">scope</a> helps to define the similarities and differences among the different TQIs. Before we can launch into an in-depth discussion of our first Temporary Query Item, though, we need to take a little time to talk about indexing and statistics.</p>

<!-- more -->


<h3>Index? What, like the card?</h3>

<p>Many people have made many comparisons of indexes in SQL Server to more familiar real-life objects. The obvious analog is to an index in a book, where you look up certain terms and the index points you to the page numbers in the book where the term can be found &ndash; <a href="http://technet.microsoft.com/en-us/library/aa933129(v=sql.80).aspx" title="Technet article on indexes being like indexes in books">here&rsquo;s Technet</a>. Others have compared indexes to entries in the phone book, like <a href="http://www.brentozar.com/archive/2006/04/sql-server-training-for-developers-primary-keys-indexes/" title="Brent Ozar on Indexes">Brent Ozar</a>.  <a href="http://blog.sqlauthority.com/2007/03/30/sql-server-index-seek-vs-index-scan-table-scan/#comment-4099" title="Comment by Mark Solomon">Mark Solomon</a> suggests that non-clustered indexes are like miniature tables (SQL Server tables, not dining room tables.)</p>

<p>Analogies are nice and all, but what&rsquo;s the reality? In essence, indexes do two things: Holding (and potentially organizing) data, and holding pointers to the physical location of data. Pointers, as you&rsquo;ll recall from your hours of C coding earlier today, contain an address to a location in memory. In SQL Server indexing, a pointer contains the address of a row in the table.</p>

<p>Before we move on with that idea though, we should talk about the two types of indexes in SQL Server: Clustered and non-clustered. They&rsquo;re both types of indexes, of course, but they tend to be defined by their differences more than their similarities.</p>

<h4>Clustered</h4>

<p><a href="https://www.flickr.com/photos/73416633@N00/402545511/in/photolist-Bz9KR-bDHu4M-bDHEbR-7QeXbB-7QeXpi-6STSPj-aZcwp6-7tu2FZ-bDHuWa-dsyFnQ-bqNwvu-bDHmiB-bqNBTW-K2GD7-24z28S-k7hxnT-bDHAXe-e9F83a-Pa8gj-9e3oYa-eksexY-gtAs63-4TFf8e-ggiHb6-hC8PEf-5YasAU-24z26o-dRepwL-7Urdcf-6k98Tx-bqNCny-fryq2i-aZcwAt-93T6Q4-bqNExb-8uEkmP-hDYQ4-KSWDQ-bqNAU5-bDHzec-7aiqtW-4w8xpR-bq86gw-aZcwfZ-47KEW8-bqNrXm-aZcwb2-aZcw8Z-aZcwy2-aZcwqD" title="There can apparently be lots more than one"><img class="left" src="http://sqlhillbilly.com/images/highlander.jpg"></a></p>

<p>A clustered index is the technical term for an index that defines the physical structure of a table. You can have up to one clustered index per table &ndash; though technically, a table without a clustered index isn&rsquo;t really a table, it&rsquo;s a <a href="http://msdn.microsoft.com/en-us/library/hh213609.aspx" title="Technet article on Heaps">heap</a>. When you have a clustered index, then the data in the table will be ordered on disk according to the definition of the index, which is a large part of why there can only be one clustered index.</p>

<h4>Non-clustered</h4>

<p><a href="https://www.flickr.com/photos/tomswift/4063496521/in/photolist-7c5tWR-48LBPr-7RCJpU-jPDz4k-6cugWL-kf7Sec-m4Fu6-gDSdQn-an6XgZ-njyyFM-9J6PPy-bT3HV8-pSY6Z-igEHEq-nesYYu-5xYfBS-5gSvf-5xZnb5-9S4SKZ-pGdTj-8P7Dee-jPEsJ2-jPEr9P-7KDeBf-8gQRM3-jPEqM6-7KDdRd-hSZcx-6zKEEA-9fMaw-4vDy8s-4vDt9y-4vzrpk-4vDwqm-5wFoux-nhw9yL-5xTQ7v-nxJhse-8P6utj-8LHXQU-5JyNN9-5xTS7M-4vDA7S-4vzxDx-4vDDv1-4vDAz5-4vzkmR-4vDvtW-4vDux3-4vDwWw" title="A highly performant OLTP table"><img class="right" src="http://sqlhillbilly.com/images/heap.jpg"></a></p>

<p>A non-clustered index does not enforce any ordering on the data in the table. Instead, each row within the non-clustered index contains a pointer to the original row in the clustered index. Any columns contained within the non-clustered index will be stored along with the pointer (so, those columns are essentially duplicated.) There can be up to 999 non-clustered indexes on a table (according to <a href="http://fundamentals.sqlpass.org/MeetingDetails.aspx?EventID=1299" title="Table Indexing 101 Presentation">Denny Cherry</a> that&rsquo;s not a challenge), and non-clustered indexes can be applied even when there is no clustered index. When it comes to heaps, the pointer to the non-clustered index is replaced by a pointer to a RowID in the heap.</p>

<h4>Okay, so why do we have them again?</h4>

<p>Both kinds of indexes in SQL Server are there to help support queries. Without indexing, finding data in our tables would require scanning the whole table every time. With indexes, we can know exactly where to look for what data, and so we can be much more efficient. If you want more on that, Brent Ozar has a fantastic course that walks through how this works called <a href="http://www.brentozar.com/training-videos-online/how-to-think-like-sql-server/" title="How To Think Like SQL Server course">How To Think Like SQL Server</a>, which for $29 is a steal.</p>

<h3>Statistics. What is this, baseball?</h3>

<p><a href="https://www.flickr.com/photos/wallyg/5948559822/in/photolist-a4DVAf-f1exJ8-f1exHZ-4G6aHs-bZYDgq-frdf3-ksjM5-HsNis-fbCAo-HsNay-HNECM-6XNtug-4G1VP8-f1exHF-4Svkvx-6XSv9y-6XSvpW-HsN2u-6XT5jJ-6XT4yE-6XT4YS-6XP3Dk-6XT4P5-frdBp-oy36nK-57FCPm-oeQAmW-6kqWiH-6kvbfJ-6kuBGG-6kuzXj-6kuBxb-6kreV4-6PxkKF-8jWz8E-eaxpV4-eaD4D7-eaxpUD-a4AR6n-6kuK5j-GHgt8-jqPir-eXE49-of2gLL-4NajFw-6VDMY8-hSLANY-4f23qJ-b55G7H-ov7Kq7" title="World record holder for waiting for the pitch the longest amount of time"><img class="left" src="http://sqlhillbilly.com/images/baseballstatue.jpg"></a></p>

<p>I love baseball statistics. Not because I like baseball, but because you really start to see what can happen when you try very hard to identify new outliers time after time. We&rsquo;re about to the point that &ldquo;Most pitches thrown left-handed by a right-handed pitcher against a switch-hitting batter during a September game at 50 degrees when the catcher&rsquo;s name begins with M&rdquo; will be a thing that is said and celebrated by some announcer somewhere. That is RIDICULOUSLY specific.</p>

<p>Statistics in SQL Server aren&rsquo;t nearly that bad, though. Where indexes contain information about the intersection of values in columns with rows, statistics contain information about the distribution of values in columns. Statistics contain some information about <a href="http://en.wikipedia.org/wiki/Cardinality_(SQL_statements)" title="Wikipedia entry on cardinality">cardinality</a> and ranges of values as well. I&rsquo;m a big fan of Erin Stellato&rsquo;s <a href="https://attendee.gotowebinar.com/recording/3673031098698431745" title="Statistics Starters recording">Statistics Starters</a> presentation for more information on statistics.</p>

<h3>What does this have to do with Temporary Query Thingies?</h3>

<p>The SQL Server optimizer uses indexes and statistics to help it make the right decisions about turning your queries into execution plans. This means that understanding how TQIs interact with indexes and statistics can help you make better decisions about which ones are right for you and what you may be doing at any given moment.</p>

<h3>Moving On</h3>

<p>We&rsquo;re much closer to talking about Temporary Tables, our first Temporary Query Item, in depth. However, there&rsquo;s one more topic I really need to cover before that &ndash; <a href="http://sqlhillbilly.com/blog/2014/08/19/temporary-query-items-tempdb-and-memory/" title="TempDB and Memory, live together in harmony">TempDB and Memory</a>.</p>
]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[Temporary Query Items: Scope]]></title>
    <link href="http://sqlhillbilly.com/blog/2014/08/17/temporary-query-items-scope/"/>
    <updated>2014-08-17T01:01:51-05:00</updated>
    <id>http://sqlhillbilly.com/blog/2014/08/17/temporary-query-items-scope</id>
    <content type="html"><![CDATA[<p>Note: This is part of a series on <a href="http://sqlhillbilly.com/tqi/" title="Temporary Query Items">Temporary Query Items</a>.</p>

<p><a href="http://sqlhillbilly.com/blog/2014/08/16/temporary-query-items-introduction/" title="Temporary Query Items: Introduction">Last time</a> we took a look at what a temporary query item is, and why it is useful. In this post, I&rsquo;ll be looking at how Scope plays into the similarities and differences among Temporary Query Items. An understanding of scope and how it relates to each TQI is crucial to being able to choose the best one for your situation, which is why I&rsquo;ve dedicated an entire post to it.</p>

<!--more-->


<h3>What is Scope?</h3>

<p>Wikipedia has <a href="http://en.wikipedia.org/wiki/Scope_(computer_science)" title="Scope">a great article</a> about scope from a generic computer science standpoint. My &ldquo;quick and dirty&rdquo; definition of scope is &ldquo;The Time and Place of validity for a temporary item.&rdquo; It&rsquo;s a decent definition, and it tells us that we&rsquo;re concerned with two things: When and Where.</p>

<h3>When</h3>

<p><a href="https://www.flickr.com/photos/spodzone/2319081759/in/photolist-4wVUdg-5gBjJ1-aKPeBV-656BLb-9i7rec-baM796-ak5918-mVfL6e-9Yw3im-2wGAoY-dP3ER5-4GpEvM-4wPf2y-8vJGry-4XHMNv-6ibpW-76F2zt-6BH4ku-7EHuQF-6ZL6Yf-bSVZaP-bE2fSL-bSVWQg-bSW1cx-bE2cT3-bSW13K-bE2hMb-bSVYWF-bE2cBw-bE2gcq-bE2i1q-bE2fXo-bSW2FH-bSVVUK-bSW1uD-bSVXs4-bE2eNj-bSW1kP-bE2hvE-bSVYJT-bE2hDh-bE2hgC-bE2dp5-Q8RdQ-6En8F4-9NGKrb-oav8Z3-nsn2GM-9NJybd-9NAsCc" title="Actual Photo of a Derived Table"><img class="right" src="http://sqlhillbilly.com/images/tempfix.jpg"></a></p>

<p>The <em>Temporary</em> part of <em>Temporary Query Items</em> tells us that they only exist or are only accessible for a limited period of time. This isn&rsquo;t to say that there&rsquo;s a time limit on the existence of any of the TQIs in the traditional sense of the word &ndash; on the contrary, in all cases the data will hang around at least until the end of execution of the query you&rsquo;re using it in. Not all TQIs are created equal in this respect, though &ndash; some of them are only valid for a single use, others are valid until manually dropped or until the connection is closed, and one even survives reboots. Likewise, the actual data contained within the TQI may or may not change over time based on how the underlying data changes. In a way, that&rsquo;s a whole nother aspect of scope &ndash; can the data get stale?</p>

<p><a href="https://www.flickr.com/photos/cote/315420179/in/photolist-tSBqX-6B2Uag-5RPeHp-8xDrV-8a7zM8-dAKSGx-Y6nWr-aqzAr-4sKcRB-9meCdW-ar2ZuT-7B75mF-4GeXDH-2KMQQk-5q3kuz-3X1fey-97ps77-fN5u32-h3FdQK-psifH-4718pb-8QXMZV-aaqwoo-4nYdWY-4sPgFf-4sGr1k-6VBKZ9-36bBm8-4948E-aAqkxL-kVUnWL-bgy4jM-9wC1vb-i1H5g-5ioEH9-8a7ugH-4ncA5K-6iBv3s-53fBVd-2bCdRv-nX7Tgg-68eajQ-4AWDB5-5idZYH-4Quro-3KnTZj-3Kiz1V-9nDs4r-EV9o5-acg1Tn" title="Oh no, stale data!"><img class="left" src="http://sqlhillbilly.com/images/staledata.jpg"></a></p>

<h3>Where</h3>

<p>The <em>Where</em> aspect of Temporary Query Items is mostly related to your ability to reuse TQIs across multiple queries, executions, or connections. Some TQIs can only be used in one query, while others can be used multiple times during a single execution. Others can be used universally within the database or server.</p>

<h3>Moving on</h3>

<p>This was a relatively short post. I believe for simplicity&rsquo;s sake that I&rsquo;ll leave a discussion of how these aspects of scope play out in each TQI for posts dedicated to each TQI. Before we move on to our first TQI, however, we need to talk a little about <a href="http://sqlhillbilly.com/blog/2014/08/18/temporary-query-items-indexing-and-statistics/">indexing and statistics</a>.</p>
]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[Temporary Query Items: Introduction]]></title>
    <link href="http://sqlhillbilly.com/blog/2014/08/16/temporary-query-items-introduction/"/>
    <updated>2014-08-16T22:43:47-05:00</updated>
    <id>http://sqlhillbilly.com/blog/2014/08/16/temporary-query-items-introduction</id>
    <content type="html"><![CDATA[<p>Note: This is part of a series on <a href="http://sqlhillbilly.com/tqi/" title="Temporary Query Items">Temporary Query Items</a>.</p>

<p>While working with one of my junior developers on a query one day, I discovered that she had inadvertently deleted a few records due to not understanding how a CTE maps back to the underlying data. I was able to quickly resolve the issue thanks to how our testing environment is set up, but it prompted me to create a presentation on what I call Temporary Query Items. What I was really looking for was different ways to use and reuse transformed, abbreviated, or combined data sets in queries. Turns out, in SQL Server we have plenty of options for that, depending on what your requirements are, and so I was able to put together a pretty good presentation on the upsides and downsides of each. In this series of posts, I&rsquo;ll be exploring what I cover in my presentation. This post: An intro to Temporary Query Items.</p>

<!--more-->


<h3>But Why <del>Male Models</del> Temporary Query Items?</h3>

<p><a href="https://www.flickr.com/photos/grilledbabypandas/183786467/in/photolist-heXjc-6ke3hd-3nXi4M-6ke5io-6k9YMk-4tUbyW-5R51QH-7Yk1eR-boYS7E-umD9p-umMUX-bEsrh2-5rGpGm-5javR8-6fvDCb-iRrW6G-fEGZYB-8sLrUq-64DENc-f4J6X3-cYFDD9-b5A2Dx-2hTvpv-ncrvi5-YCWSz-8TF4vt-ddRJQW-YH5TG-nvWogX-aisGzY-h4TmxP-i89Eyp-e6Fcx4-fcKkFQ-8wJQQv-aH3fPF-aXCyWg-aXCxCM-aXCxQr-aXCxqp-aXCzdr-dFod7K-71wkNJ-6B2pTj-nDga8F-8GHfrt-2SBRaP-dxtx4-f4KRbs-79V22j" title="Junior Developer Using a Table Variable"><img class="right" src="http://sqlhillbilly.com/images/sandcastle.jpg"></a></p>

<p>I&rsquo;d like to clarify my terminology a little bit here. My original inclination was to call them <em>Temporary Objects</em>, but I wanted to be careful because some of them&hellip; well&hellip; aren&rsquo;t. <em>Objects</em> in SQL Server are usually the kinds of things you can find in <a href="http://msdn.microsoft.com/en-us/library/ms190324.aspx">sys.objects</a>, and some of my TQIs can&rsquo;t be found there due to scoping &ndash; but that&rsquo;s a Good Thing&trade; because it means that sys.objects doesn&rsquo;t get all gunked up with stuff that&rsquo;s transient anyway. And that&rsquo;s where the <em>Temporary</em> part of the nomenclature comes in &ndash; everything I&rsquo;m going to talk about is transient in some way. These are the sand castles and seasonal flowers of T-SQL, the stuff that doesn&rsquo;t need to live long. Every last one of them is meant to support <em>Queries</em>, and only to support queries.</p>

<p>And as for <em>Items</em>? Well, I&rsquo;ve already told you that these aren&rsquo;t all <em>Objects</em>. I thought about <em>Fluff</em>, except that title doesn&rsquo;t really convey how useful they are. They aren&rsquo;t really <em>Clauses</em>, and they don&rsquo;t fit too well as <em>Sub-Queries</em> or <em>Statements</em>. So <em>Items</em> they are.</p>

<h3>Who Are You Calling Temporary?</h3>

<p>While researching potential candidates for my list, I found five things that I would call useful for some form of transient use while querying. I&rsquo;ll go more in-depth on these in later blog posts.</p>

<ul>
<li>Temporary Tables</li>
<li>Table Variables</li>
<li>Views</li>
<li>Common Table Expressions (aka CTEs)</li>
<li>Derived Tables</li>
</ul>


<h3>But Why <del>Male Models</del> Temporary Query Items?</h3>

<p>Okay, so now we know what a TQI is, and it&rsquo;s somewhat clear WHY they&rsquo;re called TQIs. But what do we need them for? Why even have them?</p>

<h4>Reuse, Reduce, Recycle</h4>

<p>One of the most useful attributes of a TQI is data reuse in a query. Let&rsquo;s say that you&rsquo;ve got two related tables that you want to join together and pull some columns from. Now let&rsquo;s say also that you&rsquo;ll want to take the resulting data set, and join it to itself three times. Without a TQI, you&rsquo;re looking at a six-table join, with multiple join conditions repeated over and over. With one of the Temporary Query Items, you can cut the amount of code you&rsquo;re working with down quite a bit &ndash; six joins becomes three or four joins, depending on the approach used. I&rsquo;ll cover how this works in particular in posts for each of the TQIs.</p>

<p>Alternatively, maybe you have multiple different queries, all doing some of the same things. With a TQI, you can wrap parts of those queries into one easily-referenced item that you can keep using without having to duplicate the code everywhere.</p>

<h4>Code Simplification With Limited Duplication Duration</h4>

<p>That title is a mouthful, so I&rsquo;ll break this down. Let&rsquo;s say that you have a query that gets run quite often, building a customized view of a dataset (whether you&rsquo;re transforming the data, limiting columns, limiting rows, or even combing tables.) It&rsquo;s a query that performs pretty well, and it&rsquo;s really not worth duplicating the data in a permanent form such as a table. A TQI could be just the ticket for making things easier on your users or your automated process.</p>

<h4>Consistent View with Automated Cleanup</h4>

<p><a href="https://www.flickr.com/photos/phileast/361405092/in/photolist-xWi9h-4DMTwM-78hir4-hMDp9-ccFuRq-bVjh44-bVjeNz-bVjeEk-bVjey6-ccFuJE-bVjemr-bVjfUF-4DS9Ch-3irPf-6UQYcW-7324ts-7324pb-7324ky-7VfJ1n-8VapBC-7VfJbF-FjuaL-7ViY9A-apz3UJ-7VfHU4-5sayie-4XyE4j-D5NoS-bDvktZ-4XyE3S-4Xyubh-4Xyuby-fsRwiE-6ey32M-4Fro5b-5JVdqi-8nuzEC-8x4Fc2-iLnua9-5SuKT-3mgx7Z-9Q4ma-4Xyubq-4XyE3W-4XyE4f-5AKddS-2KphHe-cf5s2N-7K2hrr-gBpR1n" title="A Self-Cleaning TQI"><img class="left" src="http://sqlhillbilly.com/images/duckling.jpg"></a></p>

<p>Again, all TQIs are transient. Most of them come with some automated form of self-cleanup, meaning that you avoid the issue of forgetting to drop a table that you only needed for a bit.</p>

<h4>Location, Location, Location</h4>

<p>Most of these TQIs live in memory, although some can live partially in tempdb. This could help reduce the impact of disk i/o (ask Brent Ozar, <a href="http://www.youtube.com/watch?v=fD1CZVc6oUk">Disk I/O is Bad</a>.)</p>

<h3>Getting On With It</h3>

<p>&ldquo;Okay Mister Slacker, you&rsquo;ve piqued my interest &ndash; bring on the Temporary Tables!&rdquo; I love your enthusiasm! But, we&rsquo;re not quite ready for those details yet &ndash; first, we have to talk about <a href="http://sqlhillbilly.com/blog/2014/08/17/temporary-query-items-scope/" title="Scope">scope</a>.</p>
]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[T-SQL Tuesday: Assumptions]]></title>
    <link href="http://sqlhillbilly.com/blog/2014/07/07/t-sql-tuesday-assumptions/"/>
    <updated>2014-07-07T17:51:28-05:00</updated>
    <id>http://sqlhillbilly.com/blog/2014/07/07/t-sql-tuesday-assumptions</id>
    <content type="html"><![CDATA[<p><a href="http://devnambi.com/2014/tsql-tuesday/" title="See the original post!"><img class="right" src="http://sqlhillbilly.com/images/TSQL2sDay150x150.jpg"></a></p>

<p>For this month&rsquo;s T-SQL Tuesday, <a href="https://www.twitter.com/DevNambi" title="Tweet Him!">Dev Nambi</a> has chosen the topic of assumptions. He assumed I would have a big assumption at work to talk about. Good assumption!</p>

<!-- more -->


<p>Probably the biggest assumption we deal with in my unit at work is Things Are The Way They Are For A Reason, And Therefore We Should Continue To Do Them That Way (hereafter abbreviated Things Are And Should Be.) I love talking about this assumption because I have a long and storied history with it. Well, I have a storied history with it, anyway. And length of time is all in the eye of the beholder. I know, because I used to be able to count to approximately a million in fifteen minutes, and now I can spend fifteen minutes ordering breakfast and not notice. But I digress (which surprises nobody!)</p>

<p>When I began working as a Data Warehouse Forklift Operator, I entered a culture that had deliberately taken a &ldquo;Produce First, Understand Later&rdquo; approach. If it could be templatized it was, and if it couldn&rsquo;t be templatized, it was maintained by one of the more savvy and experienced developers. We were in the process of moving from an ETL tool that did everything for you (badly) and to a tool that only did some things for you (SSIS). The old tool transformed data in DB2, and now we wanted to transform the data on SQL Server. For the first several months I worked on code, I assumed that Things Are And Should Be. After all, I was new to the data warehousing world, and we had experienced Architects and Designers who Knew What They Were Doing, right?</p>

<p><img src="http://sqlhillbilly.com/images/dangerous-forklift.jpg" title="Our ETL Process" alt="Our ETL Process" /></p>

<p>Fast forward about six months. I had discovered many cases in which the way we did things was suboptimal, circuituous, and even Not Quite What We Wanted. I began to suspect that my Big Assumption was perhaps not too helpful. I spent a lot of time waiting on code that worked, but took its sweet time with the large quantities of data it was processing. Some of the architecture of what I was working with seemed overly complicated. Bits and pieces were clumsy. But, I liked what I did and I was still new, after all, so what did I know?</p>

<p>After a year or so of running the Data Forklift, I began to wonder why the code I was modifying was written like it was. After all, surely I could do my job better if I knew what exactly I was trying to accomplish. So, I began asking questions &ndash; and found that, more often than I was comfortable with, the answer was <a href="http://www.snopes.com/weddings/newlywed/secret.asp" title="This Is The Story Of A... Ham">I Don&rsquo;t Know, We&rsquo;ve Just Always Done It That Way</a>. So I began to trace execution paths and transformations through the code. Many post-it notes and spreadsheets were sacrificed to the study of the code in order to understand. There were flashes of inspiration in the shower, Aha! moments while driving home, and many an evening&rsquo;s sleep interrupted as I determined the answer to another question of Why Would You Do It That Way? While I was left scratching my head, I also determined that I would pursue the path of the architect, understanding the process so deeply that I could see my way clear to improvements.</p>

<p>It&rsquo;s now been three years, and I now have a half dozen <a href="http://sqlhillbilly.com/images/XSKEl9x.jpg" title="Junior Developer">junior developers</a> depending on me to teach them&hellip; well, pretty much everything about what we do, why, and how! Err&hellip; And how. Anyway, I find them asking many of the same questions, and I&rsquo;ve had to resist the urge to give them the same old answers. It&rsquo;s easy to default back to Things Are And Should Be. After all, much of the code they&rsquo;re now questioning is code that I&rsquo;ve modified from the way we used to do things! However, I believe now that the best thing I can do for the team is to head Things Are And Should Be off at the pass. If I can pass on all I know to them, then we&rsquo;ll all be better. A developer who questions nothing will never become an asset to the team &ndash; and my team has too much potential to settle for mediocrity.</p>
]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[T-SQL Tuesday: An Interview Invitation]]></title>
    <link href="http://sqlhillbilly.com/blog/2014/05/12/t-sql-tuesday-an-interview-invitation/"/>
    <updated>2014-05-12T22:43:05-05:00</updated>
    <id>http://sqlhillbilly.com/blog/2014/05/12/t-sql-tuesday-an-interview-invitation</id>
    <content type="html"><![CDATA[<p><a href="http://borishristov.com/blog/t-sql-tuesday-54-interview-invitation/"><img class="right" src="http://sqlhillbilly.com/images/TSQL2sDay150x150.jpg"></a></p>

<p>For this month&rsquo;s T-SQL Tuesday, <a href="https://www.twitter.com/BorisHristov">Boris Hristov</a> has chosen the topic of interviews.</p>

<!-- more -->


<p>If I could give one piece of advice to beginning developers looking to do well in an interview (and I can, because this is the internet after all and why are you reading my blog if you don&rsquo;t believe that?), then it would be to remember that honesty is key. I don&rsquo;t really care as much about what you know as I do about your ethics. My team is currently looking to fill three positions, and I would love to have them filled soon. However, I won&rsquo;t even suggest applying to a candidate who I am not sure I can trust. If you don&rsquo;t have fanatical levels of integrity, I don&rsquo;t want you on my team. I can and will do everything I can to help you become competent, but I&rsquo;m not going to waste my time teaching you to be honest (unless you want my help with that, in which case I&rsquo;ll do what I can.)</p>

<p>Otherwise, I&rsquo;m a technical kind of guy. If I&rsquo;m interviewing you, then you&rsquo;ll quickly discover that I&rsquo;m going to make it very difficult to bluster about your skill level and knowledge of certain tools. Probably my favorite question is &ldquo;Can you name two things you hate about [Technology X]?&rdquo; because it&rsquo;s an excellent gauge of actual familiarity. I&rsquo;m certainly not looking for fanboys of this platform or that environment or those manufacturers &ndash; the reality is that all software sucks, some just work better than others. And to follow up &ldquo;How would you do it better?&rdquo; As a college student, I used to think that my dream job would involve a cubicle in the corner where I got requirements and pizza as input, and produced code as output. What I&rsquo;ve found, though, is that writing code is a small part of how I earn my paycheck even as a developer. What is more beneficial to my company and to my personal growth is the time I spend architecting ways to do this or that better, and I can stay sharp on that by architecting out how I would rewrite functionality in the software I use day to day.</p>

<p>So wake up every day, resolve to do the right thing, and find a new problem to solve. I believe that&rsquo;ll take you a long way towards an interview matching its intended function of matching the right people to the right job.</p>
]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[T-SQL Tuesday: Why So Serious?]]></title>
    <link href="http://sqlhillbilly.com/blog/2014/04/08/t-sql-tuesday-why-so-serious/"/>
    <updated>2014-04-08T09:27:00-05:00</updated>
    <id>http://sqlhillbilly.com/blog/2014/04/08/t-sql-tuesday-why-so-serious</id>
    <content type="html"><![CDATA[<p><a href="http://www.mattvelic.com/t-sql-tuesday-52-announcement/"><img class="right" src="http://sqlhillbilly.com/images/TSQL2sDay150x150.jpg"></a></p>

<p>For this month&rsquo;s T-SQL Tuesday, <a href="https://www.twitter.com/mvelic">Matt Velic</a> has chosen the topic of Dirty Little Tricks you can play on your coworkers/developers/those $@%! consultants using T-SQL. I&rsquo;m a bit impish, so naturally it was time to dust off the ol&#8217; blog for this topic!</p>

<!-- more -->


<p>Anybody who follows me on Twitter knows that I&rsquo;m often faced with problems of assumed ordering. (Remember kids &ndash; if you didn&rsquo;t order it, it ain&rsquo;t ordered! It just looks like it is!) So, my Dirty Little T-SQL Trick involves sneakily breaking SSIS.</p>

<p>Let&rsquo;s say that I have a Data Flow Task that does some ETL for me. It pulls my source data, does a key lookup against the target, and then routes any no matches through a surrogate key generator. After any necessary key generation, everything is pulled back together in a Merge Join, then inserted or updated into the target.</p>

<p>Now, it&rsquo;s important to note that you can&rsquo;t just feed any old thing into a Merge Join. No, the inputs have to be sorted &ndash; or at least, they have to TELL you that they&rsquo;re sorted. You can do this with a Sort Transformation, though that&rsquo;s generally slower than sorting in your source. Now, you CAN use the Advanced Editor for your source, to set the IsSorted property of the Output to True. You also have to set one or more of the output columns as a sort key, but once you&rsquo;ve done that, your Merge Join will believe that the input is sorted, and sorted just like you tell it.</p>

<p>So where&rsquo;s the trick? Well, I mentioned before that you can&rsquo;t just feed any old thing into a Merge Join. See, what it TELLS you is that it&rsquo;s doing a join. But it&rsquo;s not a SQL join, because in SQL order is not guaranteed. In SQL Server, Table A inner join Table B will give you an inner join, regardless of physical or logical ordering of the data. In SSIS, a Merge Join is essentially an array comparison that starts at the beginning of both arrays and reads through to the end. If your data is out of order, tough luck! And if the arrays are ordered correctly but in opposite order (ascending versus descending) then a very interesting thing happens &ndash; the Merge Join will match exactly one row!</p>

<p>So here&rsquo;s my trick: Change the ORDER BY clause on the source query to order opposite of what the Merge Join expects. It won&rsquo;t complain, and the solution is non-obvious (at least, non-obvious if you&rsquo;ve never encountered it before). And it will leave your coworkers scratching their heads when they get only one row!</p>
]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[Accidents - Happy and Otherwise]]></title>
    <link href="http://sqlhillbilly.com/blog/2013/10/22/accidents-happy-and-otherwise/"/>
    <updated>2013-10-22T21:40:00-05:00</updated>
    <id>http://sqlhillbilly.com/blog/2013/10/22/accidents-happy-and-otherwise</id>
    <content type="html"><![CDATA[<p>I have been blessed throughout my life by an abundance of good fortune. You might call it serendipity &ndash; a series of happy accidents, all leading me to my current position. This is not to discount my natural talent nor my hard work, but I do admit that it all seems a little strange looking back. Perhaps I&rsquo;m too quick to forget my failures, or at least the ones that didn&rsquo;t direct me to where I am now.</p>

<!-- more -->


<p>A short history of how I got where I am would of course include a retelling of taking Computer Science 101 because I thought Intro to Computing would bore the life out of me. It would also include how I accidentally became the dialog editor of an open-source game because I was the first contributor who spoke English natively, and how that lead me to be a Google Summer of Code mentor for the same project. And it would of course include how I accidentally got into the SQL Server and Data Warehousing worlds when the internship I thought I was applying for lead me to the internship in the Data Warehouse.</p>

<p>I suppose I should be more careful using the term &ldquo;accident&rdquo; (and I <strong>know</strong> I should be more careful using the term &ldquo;We&rdquo; but that&rsquo;s a different chat.) It kind of has a connotation in my mind of just falling into events and places Mr. Bean-style. I&rsquo;m really not oblivious, perhaps I&rsquo;m just happier with the idea that I&rsquo;m not completely in control.</p>

<p>Either way, some of my happiest accidents yet happened last week, at the PASS Summit. There, I met some people who challenged my preconceptions of what a community can be. Special thanks go out to <a href="http://www.mickeystuewe.com">Mickey Stuewe</a> and <a href="http://www.allenkinsel.com/">Allen Kinsel</a> for their support in not only making me feel welcome, but in introducing me around to others in the community until I felt comfortable enough to begin introducing myself. I also met some people who surprised me by their depth of knowledge and speed of thought &ndash; I want to point out <a href="https://twitter.com/rob_farley">Rob Farley</a> in particular here, who I look forward to misunderstanding for many years to come. I also had the opportunity to speak to such fine folks as <a href="(https://twitter.com/onupdatecascade">Merrill Aldrich</a>, <a href="https://twitter.com/mecheph">Stuart Miller</a>, and <a href="https://twitter.com/SQLBek">Andy Yun</a> who helped me to understand that others faced many of the same challenges as I face every day &ndash; I&rsquo;m not alone. I had the chance to talk to great consultants like <a href="http://www.made2mentor.com">David Stein</a>, <a href="https://twitter.com/billinkc">Bill Fellows</a>, <a href="http://timradney.com/">Tim Radney</a>, <a href="http://www.brentozar.com/">Brent Ozar, Kendra Little, and Jes Borland</a>, who really solidified in my mind that there are short-term contractors out there who really do care to see clients learn, grow, and succeed &ndash; as it turns out, they&rsquo;re not all the bargain-basement quality &ldquo;consultants&rdquo; I&rsquo;m used to dealing with at all. And finally, special thanks goes out to folks like <a href="https://twitter.com/SQLinthWild">Gail Shaw</a>, who made it clear that the heroes of the SQL Server world are not all highly driven, self-seeking individuals, but instead regular folk who get kind of embarassed when mobbed by adoring fans.</p>

<p>Unfortunately, into each life a little rain must fall, and not all accidents are happy. If I&rsquo;ve introduced myself to you or been introduced to you, you&rsquo;ll know that I go by JK. These are my first and middle initials, and I chose this nickname because of a collision between my first and last name with someone else where I work, as well as a collision between my first name and that of another member of my team. I&rsquo;m not trying to hide my identity, it just started as a way to minimize confusion (indeed, JK is a nickname my mother developed for me in college when she hired another person with my first name.) I was a bit stunned this week to discover that I had recently been confused with a local registered sex offender who shares my real first and last name, but not my middle name or initial. Let me make this clear &ndash; I am <strong>not</strong> a child molester. The idea makes me sick. And anyone who suggests that I am is confused or mislead.</p>

<p>And now, back to your normally scheduled nonsense.</p>
]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[Summit 2013 Reflections]]></title>
    <link href="http://sqlhillbilly.com/blog/2013/10/21/summit-2013-reflections/"/>
    <updated>2013-10-21T21:01:00-05:00</updated>
    <id>http://sqlhillbilly.com/blog/2013/10/21/summit-2013-reflections</id>
    <content type="html"><![CDATA[<p>It&rsquo;s now the Monday night after the <a href="http://www.sqlpass.org">PASS</a> <a href="http://www.sqlpass.org/summit/2013/PASStv.aspx">Summit</a> and it&rsquo;s time for this slacker to get back into the rhythm of blogging. I&rsquo;m working on Big Big Things<sup>TM</sup> for the blog in the near future, but for now I&rsquo;d like to do a multi-part series of reflections on my experiences at the Summit.</p>

<!-- more -->


<p>Anyone who has not been to the PASS Summit most likely thinks it&rsquo;s just a big training event, a place where you can pay a lot of money to go learn about SQL Server. If you think that, you don&rsquo;t know what you&rsquo;re missing.</p>

<p>The most valuable part of the Summit, in my opinion, is the amazing amount of networking and community building you get to do. Folks on Twitter are fond of using the <a href="https://twitter.com/search?q=%23sqlfamily&amp;src=hash">#sqlfamily</a> hashtag to refer to their friends and colleagues in the SQL Server community. That sounds cute and trite, but the reality is that the PASS Summit is like the biggest and most intimate family reunion you&rsquo;ve ever experienced. I&rsquo;m kind of a weird guy &ndash; I dress funny, look awkward, and tend to talk too much. But I was not once made to feel like I didn&rsquo;t belong last week. And the best part is, you don&rsquo;t have to explain what it is you do to your grandmother again. We understand you. We know what struggles you deal with. We don&rsquo;t care what you look like, where you&rsquo;re from, or who you know. And for once in my life, I&rsquo;m using We correctly.</p>

<p>The second most valuable part of the Summit is the SQL Server Clinic held by Microsoft. This is your opportunity to go ask questions of the people who support and maintain SQL Server, in person, for absolutely free. This is a pretty unique opportunity and something everyone should take advantage of (except me &ndash; I only have really strange and awkward questions.)</p>

<p>And finally, you have the opportunity to learn about the newest and most exciting changes coming down the pike in the SQL Server world. This year, Dr. Dewitt gave a fascinating keynote on the changes coming to Hekaton in SQL Server 2014. Hekaton is their name for their in-memory OLTP solution &ndash; it looks like it&rsquo;s going to rock the OLTP performance world. I only wish we could take better advantage of it in the data warehousing world!</p>

<p>Oh, and there&rsquo;s some training, but the cheapest way to get that is to spend about $250 and get the sessions on USB. But the real draw are the three things I mentioned above.</p>
]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[Slide Deck: Deployment WORST Practices]]></title>
    <link href="http://sqlhillbilly.com/blog/2013/10/09/slide-deck-deployment-worst-practices/"/>
    <updated>2013-10-09T22:41:00-05:00</updated>
    <id>http://sqlhillbilly.com/blog/2013/10/09/slide-deck-deployment-worst-practices</id>
    <content type="html"><![CDATA[<p>As promised, <a href="http://sqlhillbilly.com/slides/Deployment_Worst_Practices.odp">here</a> is the slide deck from my presentation last night to the Southwest Missouri SQL Server User&rsquo;s Group on Deployment WORST Practices. I had a lot of fun putting this together (thanks go out to <a href="https://twitter.com/GFritchey">Grant Fritchey</a> and <a href="https://twitter.com/mvelic">Matt Velic</a> for the inspiration!) Do note that it&rsquo;s in OpenDocument Presentation format &ndash; I use Libreoffice Impress to do my presentations. You shouldn&rsquo;t have any trouble opening it in whatever you use, as far as I know.</p>

<p>Update: Apparently the ODP wasn&rsquo;t working right for some people. <a href="http://sqlhillbilly.com/slides/Deployment_Worst_Practices.ppt">Here</a> is a PowerPoint version that will hopefully be better.</p>
]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[T-SQL Tuesday: SQL Swag Wishlist]]></title>
    <link href="http://sqlhillbilly.com/blog/2013/10/08/t-sql-tuesday-sql-swag-wishlist/"/>
    <updated>2013-10-08T12:24:00-05:00</updated>
    <id>http://sqlhillbilly.com/blog/2013/10/08/t-sql-tuesday-sql-swag-wishlist</id>
    <content type="html"><![CDATA[<p><a href="http://www.kendalvandyke.com/2013/10/t-sql-tuesday-47-your-best-sql-server.html"><img class="right" src="http://sqlhillbilly.com/images/TSQL2sDay150x150.jpg"></a></p>

<p>For this month&rsquo;s T-SQL Tuesday topic, <a href="https://www.twitter.com/SQLDBA">Kendal Van Dyke</a> chose the topic of the best SQL Server Swag you&rsquo;ve received. Unfortunately, I haven&rsquo;t snagged any yet &ndash; that&rsquo;s what I&rsquo;m hoping for at the <a href="http://www.sqlpass.org/summit/2013/">PASS Summit</a> next week. Here, in no particular order, is the Swag I&rsquo;m hoping to Snag at the Summit:</p>

<!-- more -->


<ul>
<li>A <a href="http://www.sqlsentry.com/">SQL Sentry</a> tea cozy</li>
<li><a href="http://brentozar.com/">Brent Ozar Unlimited</a> boxer shorts, signed by the team</li>
<li>An official <a href="http://www.red-gate.com">Red Gate</a> secret decoder ring</li>
<li>A copy of Deadpool volume 3 issue #3 signed by <a href="https://www.twitter.com/gfritchey">Grant Fritchey</a> and <a href="https://www.twitter.com/Neil_Hambly">Neil Hambly</a></li>
<li>An illustrated guide to pronouncing <a href="https://www.twitter.com/MladenPrajdic">Mladen Prajdic&rsquo;s</a> name</li>
<li><a href="https://www.twitter.com/SQL_Ferret">SQL Ferret</a></li>
<li>A grappling hook</li>
<li>One of those tiny umbrellas</li>
<li>Knighthood</li>
<li>A pair of solid gold six-shooters</li>
<li>A bottle of water from Mars</li>
<li>My own Comedy Central Special</li>
<li>18 mosquito nets</li>
</ul>

]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[Troubleshooting Empty String Comparison Issues]]></title>
    <link href="http://sqlhillbilly.com/blog/2013/10/07/troubleshooting-empty-string-comparison-issues/"/>
    <updated>2013-10-07T21:25:00-05:00</updated>
    <id>http://sqlhillbilly.com/blog/2013/10/07/troubleshooting-empty-string-comparison-issues</id>
    <content type="html"><![CDATA[<p>While testing an SSIS package today, I discovered that I was missing some rows I expected to build. Digging in revealed that the missing rows had a column in the key that looked like an empty string to me, but that didn&rsquo;t seem to be comparing correctly. SQL Server is usually pretty good about this, so I had a mystery to solve!</p>

<!-- more -->


<p>The first thing to realize about SQL Server is that an empty string and a string composed of all spaces are considered to be equivalent. That is, the following code will return Yes:</p>

<figure class='code'><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
</pre></td><td class='code'><pre><code class='sql'><span class='line'><span class="k">SELECT</span> <span class="k">CASE</span>
</span><span class='line'>       <span class="k">WHEN</span> <span class="s1">&#39;&#39;</span> <span class="o">=</span> <span class="s1">&#39;    &#39;</span>
</span><span class='line'>       <span class="k">THEN</span> <span class="s1">&#39;Yes&#39;</span>
</span><span class='line'>       <span class="k">ELSE</span> <span class="s1">&#39;No&#39;</span>
</span><span class='line'>       <span class="k">END</span> <span class="k">as</span> <span class="n">Are_empty_strings_equivalent_to_spaces</span>
</span></code></pre></td></tr></table></div></figure>


<p>Note that the empty string &lsquo;&rsquo; and a string with a NULL value are not the same thing! An empty string is just a string with a length of zero. So, if I compare what appears to be two CHAR(22) fields that are composed of spaces for equality, I should get a value of TRUE returned. The fact that I wasn&rsquo;t seeing this was an indicator that something weirder was going on.</p>

<p>The first tool I pulled out of the box was the trusty pair of <a href="http://msdn.microsoft.com/en-us/library/ms177827.aspx">LTRIM()</a> and <a href="http://msdn.microsoft.com/en-us/library/ms178660.aspx">RTRIM()</a>. LTRIM() removes leading spaces from the passed string, and RTRIM() removes trailing spaces. I really didn&rsquo;t expect this to work, but gave it a try anyway because I tend to stumble upon weird stuff like this from time to time where SQL Server doesn&rsquo;t behave as I expect. That&rsquo;s not to say that it behaves incorrectly &ndash; I&rsquo;ll believe my understanding is at fault first! Anyway, I did check my strings for their <a href="http://msdn.microsoft.com/en-us/library/ms190329.aspx">LEN()</a> attributes when wrapped in LTRIM() and RTRIM(), and discovered that one version of the string showed length 0 (the empty string), and the other showed length 20! Curiouser and curiouser.</p>

<p>My next stop on this train was to make use of the <a href="http://msdn.microsoft.com/en-us/library/ms177545.aspx">ASCII</a> function to examine what was actually in those strings. This function tells you the <a href="http://en.wikipedia.org/wiki/ASCII">ASCII code</a> of the first character in the string you pass it. When combined with <a href="http://technet.microsoft.com/en-us/library/ms187748.aspx">SUBSTRING</a>, you can examine any character in your string to see what it is. I was looking for ASCII codes of 32 (space), but to my surprise the source column had 0 (NUL) in 20 of the 22 characters! As it turns out, at some point in the past, our DB2 source-side system put those NUL terminators into the first 20 bytes of that column, and our previous ETL solution maintained the NUL bytes into the data warehouse source layer. However, SSIS quite helpfully substituted space characters for the NUL characters, meaning that when the time came to join back to the original table, the comparison failed and we lost rows.</p>

<p>In our case, this is old data so we&rsquo;re just going to plug it. However, I could have stuck a <a href="http://technet.microsoft.com/en-us/library/ms186862.aspx">REPLACE(colname,CHAR(0),CHAR(32))</a> in the staging query and solved the problem as well. If you run into a similar problem, that may be the solution you want, though you may be dealing with different characters. Hope it helps!</p>
]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[Why Maintenance Is Important]]></title>
    <link href="http://sqlhillbilly.com/blog/2013/10/05/why-maintenance-is-important/"/>
    <updated>2013-10-05T22:31:00-05:00</updated>
    <id>http://sqlhillbilly.com/blog/2013/10/05/why-maintenance-is-important</id>
    <content type="html"><![CDATA[<p>This evening I got into my vehicle to come home from about 45 minutes away to discover, much to my chagrin, that the truck wouldn&rsquo;t start. I had jumper cables and friends nearby to get me going, but I wasn&rsquo;t sure I would make it home &ndash; there was a cable with a bit of a loose connection and nothing I could really do to fix it at the time. I finally adjusted the cable sufficiently to keep the truck running and made it home, but it left me thinking &ndash; why don&rsquo;t have I have a maintenance plan for my truck?</p>

<!-- more -->


<p>The idea behind maintenance is that if you sacrifice a little bit of time regularly, you&rsquo;re less likely to have a major malfunction later down the road. This obviously applies to cars, but it also applies to SQL Server databases. <a href="https://www.twitter.com/bradmcgehee">Brad McGehee</a> has written a great book on SQL Server maintenance plans &ndash; it sits on my bookshelf at work and taunts me about how I need to figure out how to apply it to developer work. Brad&rsquo;s book is targeted at DBAs. I do have an idea or two about how an ETL developer can plan for regular maintenance:</p>

<ul>
<li>Plan performance reviews for your packages in Production. Look at what&rsquo;s running the longest, and what individual steps within each package are running the longest. Watch especially for big changes in runtime and map out what changed to cause the new runtimes.</li>
<li>Periodically review what individual pieces of code do. Rejustify it and analyze whether it could be rewritten to better fit business needs.</li>
<li>Read lots of blog posts and consider how each new technique could enhance your ETL process.</li>
<li>Review index usage stats in your databases to ensure that you have the correct indexes and that you don&rsquo;t have any taking up space but not helping out.</li>
<li>Take the time to review your architecture and make sure it&rsquo;s still working for you instead of against you.</li>
</ul>


<p>Those are just a few ideas, but I hope they help you to start thinking about what you can do to maintain what you build. Maintenance isn&rsquo;t often fun or glamorous, but it can reduce the number of emergencies you have.</p>
]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[Harassment and Culture]]></title>
    <link href="http://sqlhillbilly.com/blog/2013/10/04/harassment-and-culture/"/>
    <updated>2013-10-04T10:53:00-05:00</updated>
    <id>http://sqlhillbilly.com/blog/2013/10/04/harassment-and-culture</id>
    <content type="html"><![CDATA[<p>When and where I grew up, it was far from uncommon to hear this phrase: &ldquo;If I didn&rsquo;t pick on you, you&rsquo;d think I didn&rsquo;t like you anymore.&rdquo; I received and gave a lot of good-natured ribbing, which certainly helped with my sense of humor and my tolerance of others. It&rsquo;s a philosophy that has served me well, but I&rsquo;m also aware that it&rsquo;s a philosophy that, taken to an extreme, could result in bullying or harassment.</p>

<!-- more -->


<p>This morning, <a href="https://www.twitter.com/denisemc06">Denise McInerny</a> posted a <a href="http://www.sqlpass.org/Community/PASSBlog/tabid/1476/entryid/573/PASS-Summit-2013-An-Anti-Harrassment-Zone.aspx">blog post</a> about the PASS <a href="http://www.sqlpass.org/summit/2013/AntiHarassment.aspx">Anti-Harassment Policy</a> as a reminder of the kind of behavior that will not be tolerated at the PASS Summit in just over a week. I applaud the intent of such a policy while lamenting its necessity. I also don&rsquo;t anticipate any violations &ndash; in my time in the SQL Server community I have met nothing but class acts.</p>

<p>With that in mind, though, I do want to comment a bit on some things that the AHP doesn&rsquo;t specifically cover. In the past, I have had the opportunity to work with people from diverse geographical and cultural backgrounds, as well as a diverse set of ages and experience levels. The truth is that what is perfectly acceptable or even encouraged in one culture or amongst one generation is seen as impolite or even taboo in another. I was reminded of this last night at a non-SQL Server event when a young lady was recounting how some of her friends had spent quite a lot of time discussing the size of a part of her body in a way that was meant to be complimentary, but that would have been considered offensive when and where I was her age, and Denise&rsquo;s blog post this morning served as additional reinforcement of the idea that perhaps it&rsquo;s better to be overly explicit in what&rsquo;s acceptable than to have to worry about charges of harassment being thrown around (merited or otherwise.)</p>

<p>With that in mind, here&rsquo;s my personal checklist of things to watch out for in your own behavior if you&rsquo;re attending the Summit (or even just interacting with others on Twitter):</p>

<ul>
<li>Please refrain from comments on body parts (yours or those of others). What you mean as a compliment could very well be seen as harassment.</li>
<li>You want to sell yourself well, but you too much self-promotion is often seen as bragging.</li>
<li>If someone else has done the work, do not try to pass it off as your own. In American culture, there exists a fairly strong ethic that copying the work of others and passing it off as your own is bad &ndash; we call it plagiarism. This isn&rsquo;t a concept that exists as strongly in some other cultures, but it&rsquo;s important to be aware of it when dealing with professionals from a multi-national perspective.</li>
<li>Don&rsquo;t force your presence on others. There are hundreds of professionals attending PASS &ndash; you want to network as much as possible. If someone doesn&rsquo;t seem overly interested in your attention, excuse yourself and find someone else to talk to.</li>
<li>Try to limit your use of &lsquo;acceptable&rsquo; stereotype humor. I&rsquo;m a hillbilly from Missouri, and I make plenty of jokes about people from Kansas, Arkansas, and Illinois. I&rsquo;ve run most of by people from those areas, so I&rsquo;m fairly comfortable telling those jokes. My mother, who is naturally blonde, tells more dumb blonde jokes than anyone else I&rsquo;ve met. This topical humor is alright for the places I call home, but I&rsquo;ll be leaving these jokes home when I go to the Summit.</li>
<li>Don&rsquo;t make the mistake of looking down on someone because of where they come from, what they do, or how long they&rsquo;ve been doing it. We all had to start somewhere, and people will pick up on disdain more easily than you think. If you&rsquo;re friendly with me but harsh with others, we won&rsquo;t become fast friends.</li>
<li>Be very, very careful about physical contact and personal space issues. Americans tend to stand further apart and speak louder than in some other cultures. Some people enjoy physical cues of affection like patting or squeezing the shoulder, others don&rsquo;t. When in doubt, don&rsquo;t.</li>
<li>Never assume that the interactions of others can be emulated by you. If two old friends slap each other on the back and call each other names, that is not an invitation for you to do the same.</li>
<li>Above all else, prepare to have a good time and meet lots of neat people who also want to have a good time. Just don&rsquo;t let your good time become someone else&rsquo;s bad night.</li>
</ul>

]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[Meta: Blogging is Hard]]></title>
    <link href="http://sqlhillbilly.com/blog/2013/10/03/meta-blogging-is-hard/"/>
    <updated>2013-10-03T23:16:00-05:00</updated>
    <id>http://sqlhillbilly.com/blog/2013/10/03/meta-blogging-is-hard</id>
    <content type="html"><![CDATA[<p>I have no doubt about it: Blogging is hard.</p>

<!-- more -->


<p>I&rsquo;ve been attempting to blog since the early days of blogging, when your choices were LiveJournal or Xanga and Geocities pages were all the rage. I say attempting because I never really could seem to stick with it. I&rsquo;d be interested for a few days or weeks, but then I&rsquo;d fall off as I got busy or just bored. It&rsquo;s easy to come up with excuses, but at the end of the day I was just not motivated or disciplined enough to make it happen.</p>

<p>It&rsquo;s with this understanding in mind that I&rsquo;ve promised myself that I will blog once a day through the end of 2013. The important thing is not to be read, or to be popular, or even to be helpful. The important thing is to do it. No excuses about lots happening at work. No excuses about it being my vacation. No excuses about coming home tired after a long day at a cowboy action shoot. I&rsquo;m not committing to a certain length or depth of topic, as long as I write something of substance every day without fail. Blogging is hard, but they say that adversity breeds character, and I&rsquo;ve always held to the philosophy that nothing worth doing is easy. Repetition will become discipline, and all the nights spent pondering what to write will remind me that there is so much more to learn out there.</p>

<p>To anyone who is reading this and considering starting blogging or considering picking up one that you started and quit on before, I&rsquo;d encourage you to commit to a blogging challenge. In the end you&rsquo;ll learn more than you could have imagined. I just don&rsquo;t recommend an every day thing &ndash; three times a week for a month is probably plenty. Shoot, <a href="https://www.twitter.com/sqlslacker">ping me on Twitter</a> when you write a new post and I&rsquo;ll be happy to read it.</p>
]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[Twitter Tip: Be Kind To Your Followers]]></title>
    <link href="http://sqlhillbilly.com/blog/2013/10/02/twitter-tip-be-kind-to-your-followers/"/>
    <updated>2013-10-02T20:47:00-05:00</updated>
    <id>http://sqlhillbilly.com/blog/2013/10/02/twitter-tip-be-kind-to-your-followers</id>
    <content type="html"><![CDATA[<p>If you&rsquo;ve been involved with the SQL Server community at all, you should know that one of the best ways to keep in touch is through <a href="https://www.twitter.com">Twitter</a>. While there are a number of things you can learn about how to use Twitter from the basic mechanics to how to leverage its strengths (I recommend <a href="http://www.brentozar.com/Twitter/book/">this free ebook</a> from <a href="https://www.twitter.com/BrentO">Brent Ozar</a>), I&rsquo;d just like to give a couple of tips on how to be kind to your followers of various interests.</p>

<!-- more -->


<p><a href="http://www.avatar.co.nz/public-domain-image-library.html"><img class="right" src="http://sqlhillbilly.com/images/free-image-twitter.png" width="203" height="203" title="Twitter Overload" ></a>
We&rsquo;ve all been there. An insightful or funny person we follow suddenly goes on a tweeting overload about politics, or a sporting event, or live-tweeting an event they&rsquo;re thrilled to be attending but that we&rsquo;re not quite so thrilled to be hearing about. It&rsquo;s not that they&rsquo;re a bad person, they&rsquo;re just a normal person with diverse interests, and sometimes those interests don&rsquo;t sync up with our own. So, how do you avoid being that person? Sure, you&rsquo;ve got like 30 followers, but you still want to be respectful of their time and their Tweetstream. I have two suggestions that can help you be an unsung hero of Twitter.</p>

<p>My first suggestion is for topics that are either controversial or will produce a large number of tweets over time. Politics and religion are usually two of the more egregious ones, but if you find yourself letting loose with 200 tweets per Cardinals game or 90 pictures per day from your vacation you should also consider this tip: <strong>Make a separate Twitter account.</strong></p>

<p>Story time: At one point on my personal Twitter account, I found myself developing more of an interest in political activism, which actually cost me some of my hard-won followers. I found it to be easier to move all my political content to a new account, which meant signing up with a new email address. Considering that Gmail, Hotmail, and many other webmail services are free, this really shouldn&rsquo;t be a barrier. What I found was that I gained many, many new followers on my politics-oriented account, and meanwhile I built back up my follower count on my personal account by not &ldquo;spamming&rdquo; them with what is admittedly a controversial topic. In much the same way, I consider the <a href="https://www.twitter.com/sqlslacker">@sqlslacker</a> account to be my professional branding account, and so I try to be more careful about what I say and the topics I cover there.</p>

<p>My second suggestion, which can be either substituted for the first or supplement the first, is to <strong>make use of hashtags</strong> whenever you&rsquo;re engaging in a particular topic. The primary advantage of this approach is that many Twitter clients today allow you to filter out certain hashtags. If I put a filter on #NFL for example, then I won&rsquo;t be inundated by football-related tweets. I can also reject event-related tweets that I really don&rsquo;t care about without unfollowing someone who usually generates great content. A side benefit of using hashtags is that it will allow any community formed around that topic to see your input and interact with you without having to seek out your account in particular.</p>

<p>Happy Tweeting!</p>
]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[T-SQL Trickery: An Alternative To OR]]></title>
    <link href="http://sqlhillbilly.com/blog/2013/10/01/t-sql-trickery-an-alternative-to-or/"/>
    <updated>2013-10-01T20:39:00-05:00</updated>
    <id>http://sqlhillbilly.com/blog/2013/10/01/t-sql-trickery-an-alternative-to-or</id>
    <content type="html"><![CDATA[<p>One of the less pleasant aspects of doing ETL coding is dealing with requirements that don&rsquo;t allow for straightforward, well-performing code. One of the trickier aspects of pulling from multiple source tables is determining whether those tables have changed. Many times, you have to make use of staging tables to avoid having to string together several performance-killing OR conditions, but sometimes there is an easier way&hellip;</p>

<!-- more -->


<p>Let&rsquo;s say you have the following predicates in your WHERE clause:</p>

<figure class='code'><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
</pre></td><td class='code'><pre><code class='sql'><span class='line'><span class="k">AND</span> <span class="p">(</span><span class="n">A</span><span class="p">.</span><span class="n">ETL_LOAD_TIMESTAMP</span> <span class="o">&gt;</span> <span class="n">ETL</span><span class="p">.</span><span class="n">LAST_LOADED_TIMESTAMP</span>
</span><span class='line'>  <span class="k">OR</span> <span class="n">B</span><span class="p">.</span><span class="n">ETL_LOAD_TIMESTAMP</span> <span class="o">&gt;</span> <span class="n">ETL</span><span class="p">.</span><span class="n">LAST_LOADED_TIMESTAMP</span>
</span><span class='line'>  <span class="k">OR</span> <span class="k">C</span><span class="p">.</span><span class="n">ETL_LOAD_TIMESTAMP</span> <span class="o">&gt;</span> <span class="n">ETL</span><span class="p">.</span><span class="n">LAST_LOADED_TIMESTAMP</span>
</span><span class='line'>  <span class="k">OR</span> <span class="n">D</span><span class="p">.</span><span class="n">ETL_LOAD_TIMESTAMP</span> <span class="o">&gt;</span> <span class="n">ETL</span><span class="p">.</span><span class="n">LAST_LOADED_TIMESTAMP</span>
</span><span class='line'>  <span class="k">OR</span> <span class="n">E</span><span class="p">.</span><span class="n">ETL_LOAD_TIMESTAMP</span> <span class="o">&gt;</span> <span class="n">ETL</span><span class="p">.</span><span class="n">LAST_LOADED_TIMESTAMP</span><span class="p">)</span>
</span></code></pre></td></tr></table></div></figure>


<p>Depending on indexing and the query plan SQL Server chooses, this can perform pretty badly. I dealt with one query this summer that did this for fourteen tables &ndash; and the &ldquo;developer lead&rdquo; I was working with couldn&rsquo;t figure out why it ran so slow!</p>

<p><img src="http://sqlhillbilly.com/images/seriously.jpg" title="Seriously." ></p>

<p>Anyway, after a little bit of deliberation, I came up with an alternative approach which took our runtime down from 20 minutes to about 10:</p>

<figure class='code'><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
</pre></td><td class='code'><pre><code class='sql'><span class='line'><span class="k">AND</span> <span class="n">ETL</span><span class="p">.</span><span class="n">LAST_LOADED_TIMESTAMP</span> <span class="o">&lt;</span>
</span><span class='line'>    <span class="p">(</span><span class="k">SELECT</span> <span class="k">MAX</span><span class="p">(</span><span class="n">ETL_LOAD_TIMESTAMP</span><span class="p">)</span> <span class="k">FROM</span> <span class="p">(</span>
</span><span class='line'>            <span class="k">SELECT</span> <span class="n">A</span><span class="p">.</span><span class="n">ETL_LOAD_TIMESTAMP</span>
</span><span class='line'>             <span class="k">UNION</span> <span class="k">ALL</span>
</span><span class='line'>            <span class="k">SELECT</span> <span class="n">B</span><span class="p">.</span><span class="n">ETL_LOAD_TIMESTAMP</span>
</span><span class='line'>             <span class="k">UNION</span> <span class="k">ALL</span>
</span><span class='line'>            <span class="k">SELECT</span> <span class="k">C</span><span class="p">.</span><span class="n">ETL_LOAD_TIMESTAMP</span>
</span><span class='line'>             <span class="k">UNION</span> <span class="k">ALL</span>
</span><span class='line'>            <span class="k">SELECT</span> <span class="n">D</span><span class="p">.</span><span class="n">ETL_LOAD_TIMESTAMP</span>
</span><span class='line'>             <span class="k">UNION</span> <span class="k">ALL</span>
</span><span class='line'>            <span class="k">SELECT</span> <span class="n">E</span><span class="p">.</span><span class="n">ETL_LOAD_TIMESTAMP</span><span class="p">)</span> <span class="n">CANDIDATE_TIMESTAMPS</span> <span class="p">)</span>
</span></code></pre></td></tr></table></div></figure>


<p>This is a T-SQL design pattern which I&rsquo;ve found handy time and again. If you don&rsquo;t care if all the timestamps are greater as long as one or more is greater, then this is the ticket. It&rsquo;s also handy for working with date overlaps &ndash; you can take the highest begin and the lowest end from all choices, which gives you the full overlap as long as your joins are correct. You can use this in WHERE clauses or in SELECTs. And note that I used UNION ALL instead of UNION &ndash; in this case, we&rsquo;d lose more from the SORT and the DISTINCT functionality of UNION than we&rsquo;d gain from not dealing with duplicate values.</p>

<p>Hope it helps!</p>
]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[Test SQL: INFORMATION_SCHEMA For Fun And Profit]]></title>
    <link href="http://sqlhillbilly.com/blog/2013/09/30/test-sql-information-schema-for-fun-and-profit/"/>
    <updated>2013-09-30T19:45:00-05:00</updated>
    <id>http://sqlhillbilly.com/blog/2013/09/30/test-sql-information-schema-for-fun-and-profit</id>
    <content type="html"><![CDATA[<p>Part of the joy of working as an ETL developer is that you get to spend a lot of time testing your code by validating large amounts of data. We have a testing tool with several built-in tests (which I maintain), but we periodically discover a new scenario which needs to be added to our toolbox. The fastest way I&rsquo;ve found of mocking up these tests is with dynamic SQL using the <a href="http://technet.microsoft.com/en-us/library/ms186778.aspx">INFORMATION_SCHEMA views</a>.</p>

<!-- more -->


<p>Firstly, what are the INFORMATION_SCHEMA views? These are an ISO standard way of querying database metadata that stays relatively static throughout the life of the DBMS. There can be improvements and changes in the underlying system tables, but INFORMATION_SCHEMA should remain relatively unchanged. I like them because they bring together information from various system tables and views such as sys.indexes or sys.tables in a way that doesn&rsquo;t require me to write the join logic as often. For mocking up a test query, they
re fantastic.</p>

<p>Let&rsquo;s say that you have a column in most of the tables in your database called ETL_PROCESSED_TIMESTAMP that does what it says on the tin, and an ACTIVE_ROW_FLAG that tells you which row is the most recent look for that entity. You&rsquo;ve discovered that Randy the unlucky intern accidentally deleted a row in one of your ETL control tables on the development server, and some of your test loads may not have any active rows loaded for that cycle. Randy doesn&rsquo;t remember touching that table, and you tested fifteen table load packages over the past week. How do you go about determining which tables are affected?</p>

<p>You could write fifteen queries to see if you have any active rows that were touched during the latest load, but that&rsquo;s a pain with three tables, and gets worse as it scales up. Also, it took you a week to discover this, and it could potentially happen again in the future. A better solution is to generate the query you need automatically, which will make it easy to stick in a testing tool &ndash; no custom code needed, just plug in some T-SQL.</p>

<p>That&rsquo;s where the INFORMATION_SCHEMA views come in. We can make use of INFORMATION_SCHEMA.COLUMNS to find tables where we have both an ETL_PROCESSED_TIMESTAMP and an ACTIVE_ROW_FLAG, then build a query based off that and INFORMATION_SCHEMA.TABLES to tell us when there are no new active rows in a table. The first place to start is finding tables with the interesting columns:</p>

<figure class='code'><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
<span class='line-number'>16</span>
</pre></td><td class='code'><pre><code class='sql'><span class='line'><span class="k">select</span> <span class="o">*</span>
</span><span class='line'>  <span class="k">from</span> <span class="n">INFORMATION_SCHEMA</span><span class="p">.</span><span class="n">TABLES</span> <span class="n">A</span>
</span><span class='line'> <span class="k">where</span> <span class="k">exists</span> <span class="p">(</span>
</span><span class='line'>       <span class="k">select</span> <span class="mi">1</span>
</span><span class='line'>         <span class="k">from</span> <span class="n">INFORMATION_SCHEMA</span><span class="p">.</span><span class="n">COLUMNS</span>
</span><span class='line'>        <span class="k">where</span> <span class="k">SCHEMA_NAME</span> <span class="o">=</span> <span class="n">A</span><span class="p">.</span><span class="k">SCHEMA_NAME</span>
</span><span class='line'>          <span class="k">and</span> <span class="k">TABLE_NAME</span> <span class="o">=</span> <span class="n">A</span><span class="p">.</span><span class="k">TABLE_NAME</span>
</span><span class='line'>          <span class="k">and</span> <span class="k">COLUMN_NAME</span> <span class="o">=</span> <span class="s1">&#39;ETL_PROCESSED_TIMESTAMP&#39;</span>
</span><span class='line'>       <span class="p">)</span>
</span><span class='line'>   <span class="k">and</span> <span class="k">exists</span> <span class="p">(</span>
</span><span class='line'>       <span class="k">select</span> <span class="mi">1</span>
</span><span class='line'>         <span class="k">from</span> <span class="n">INFORMATION_SCHEMA</span><span class="p">.</span><span class="n">COLUMNS</span>
</span><span class='line'>        <span class="k">where</span> <span class="k">SCHEMA_NAME</span> <span class="o">=</span> <span class="n">A</span><span class="p">.</span><span class="k">SCHEMA_NAME</span>
</span><span class='line'>          <span class="k">and</span> <span class="k">TABLE_NAME</span> <span class="o">=</span> <span class="n">A</span><span class="p">.</span><span class="k">TABLE_NAME</span>
</span><span class='line'>          <span class="k">and</span> <span class="k">COLUMN_NAME</span> <span class="o">=</span> <span class="s1">&#39;ACTIVE_ROW_FLAG&#39;</span>
</span><span class='line'>       <span class="p">)</span>
</span></code></pre></td></tr></table></div></figure>


<p>That&rsquo;s a good start to any testing query. Looking at the raw output can let you determine where you need to make any modifications, such as only querying a certain schema, or tables with a certain naming scheme. For my purposes, I will assume I want to work with everything I see here, so the next step is to write some dynamic SQL that will generate my test for me:</p>

<figure class='code'><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
<span class='line-number'>16</span>
<span class='line-number'>17</span>
<span class='line-number'>18</span>
<span class='line-number'>19</span>
</pre></td><td class='code'><pre><code class='sql'><span class='line'><span class="k">select</span> <span class="s1">&#39;select count(*) from &#39;</span> <span class="o">+</span> <span class="n">TABLE_SCHEMA</span> <span class="o">+</span> <span class="s1">&#39;.&#39;</span>
</span><span class='line'>     <span class="o">+</span> <span class="k">TABLE_NAME</span> <span class="o">+</span> <span class="s1">&#39; where ACTIVE_ROW_FLAG = 1 and &#39;</span>
</span><span class='line'>     <span class="o">+</span> <span class="s1">&#39;ETL_PROCESSED_TIMESTAMP = (SELECT MAX(ETL_PROCESSED_TIMESTAMP) &#39;</span>
</span><span class='line'>     <span class="o">+</span> <span class="s1">&#39;from &#39;</span> <span class="o">+</span> <span class="n">TABLE_SCHEMA</span> <span class="o">+</span> <span class="s1">&#39;.&#39;</span> <span class="o">+</span> <span class="k">TABLE_NAME</span> <span class="o">+</span> <span class="s1">&#39;);&#39;</span>
</span><span class='line'>  <span class="k">from</span> <span class="n">INFORMATION_SCHEMA</span><span class="p">.</span><span class="n">TABLES</span> <span class="n">A</span>
</span><span class='line'> <span class="k">where</span> <span class="k">exists</span> <span class="p">(</span>
</span><span class='line'>       <span class="k">select</span> <span class="mi">1</span>
</span><span class='line'>         <span class="k">from</span> <span class="n">INFORMATION_SCHEMA</span><span class="p">.</span><span class="n">COLUMNS</span>
</span><span class='line'>        <span class="k">where</span> <span class="k">SCHEMA_NAME</span> <span class="o">=</span> <span class="n">A</span><span class="p">.</span><span class="k">SCHEMA_NAME</span>
</span><span class='line'>          <span class="k">and</span> <span class="k">TABLE_NAME</span> <span class="o">=</span> <span class="n">A</span><span class="p">.</span><span class="k">TABLE_NAME</span>
</span><span class='line'>          <span class="k">and</span> <span class="k">COLUMN_NAME</span> <span class="o">=</span> <span class="s1">&#39;ETL_PROCESSED_TIMESTAMP&#39;</span>
</span><span class='line'>       <span class="p">)</span>
</span><span class='line'>   <span class="k">and</span> <span class="k">exists</span> <span class="p">(</span>
</span><span class='line'>       <span class="k">select</span> <span class="mi">1</span>
</span><span class='line'>         <span class="k">from</span> <span class="n">INFORMATION_SCHEMA</span><span class="p">.</span><span class="n">COLUMNS</span>
</span><span class='line'>        <span class="k">where</span> <span class="k">SCHEMA_NAME</span> <span class="o">=</span> <span class="n">A</span><span class="p">.</span><span class="k">SCHEMA_NAME</span>
</span><span class='line'>          <span class="k">and</span> <span class="k">TABLE_NAME</span> <span class="o">=</span> <span class="n">A</span><span class="p">.</span><span class="k">TABLE_NAME</span>
</span><span class='line'>          <span class="k">and</span> <span class="k">COLUMN_NAME</span> <span class="o">=</span> <span class="s1">&#39;ACTIVE_ROW_FLAG&#39;</span>
</span><span class='line'>       <span class="p">)</span>
</span></code></pre></td></tr></table></div></figure>


<p>This is a fairly good start, and will give the output as a series of SQL queries that can be copied and run individually or run as a whole in SSMS. You could write a cursor to go through the result set and call exec() on each query, or you could get a little fancier:</p>

<figure class='code'><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
<span class='line-number'>16</span>
<span class='line-number'>17</span>
<span class='line-number'>18</span>
<span class='line-number'>19</span>
<span class='line-number'>20</span>
<span class='line-number'>21</span>
<span class='line-number'>22</span>
<span class='line-number'>23</span>
<span class='line-number'>24</span>
<span class='line-number'>25</span>
<span class='line-number'>26</span>
<span class='line-number'>27</span>
<span class='line-number'>28</span>
<span class='line-number'>29</span>
<span class='line-number'>30</span>
<span class='line-number'>31</span>
<span class='line-number'>32</span>
<span class='line-number'>33</span>
<span class='line-number'>34</span>
<span class='line-number'>35</span>
</pre></td><td class='code'><pre><code class='sql'><span class='line'><span class="k">with</span> <span class="n">basequery</span> <span class="k">as</span> <span class="p">(</span>
</span><span class='line'><span class="k">select</span> <span class="n">ROW_NUMBER</span><span class="p">()</span> <span class="n">OVER</span> <span class="p">(</span><span class="k">ORDER</span> <span class="k">BY</span> <span class="n">TABLE_SCHEMA</span><span class="p">,</span> <span class="k">TABLE_NAME</span><span class="p">)</span> <span class="k">AS</span> <span class="n">RANKING</span>
</span><span class='line'>      <span class="p">,</span> <span class="n">TABLE_SCHEMA</span>
</span><span class='line'>      <span class="p">,</span> <span class="k">TABLE_NAME</span>
</span><span class='line'>  <span class="k">from</span> <span class="n">INFORMATION_SCHEMA</span><span class="p">.</span><span class="n">TABLES</span> <span class="n">A</span>
</span><span class='line'> <span class="k">where</span> <span class="k">exists</span> <span class="p">(</span>
</span><span class='line'>       <span class="k">select</span> <span class="mi">1</span>
</span><span class='line'>         <span class="k">from</span> <span class="n">INFORMATION_SCHEMA</span><span class="p">.</span><span class="n">COLUMNS</span>
</span><span class='line'>        <span class="k">where</span> <span class="k">SCHEMA_NAME</span> <span class="o">=</span> <span class="n">A</span><span class="p">.</span><span class="k">SCHEMA_NAME</span>
</span><span class='line'>          <span class="k">and</span> <span class="k">TABLE_NAME</span> <span class="o">=</span> <span class="n">A</span><span class="p">.</span><span class="k">TABLE_NAME</span>
</span><span class='line'>          <span class="k">and</span> <span class="k">COLUMN_NAME</span> <span class="o">=</span> <span class="s1">&#39;ETL_PROCESSED_TIMESTAMP&#39;</span>
</span><span class='line'>       <span class="p">)</span>
</span><span class='line'>   <span class="k">and</span> <span class="k">exists</span> <span class="p">(</span>
</span><span class='line'>       <span class="k">select</span> <span class="mi">1</span>
</span><span class='line'>         <span class="k">from</span> <span class="n">INFORMATION_SCHEMA</span><span class="p">.</span><span class="n">COLUMNS</span>
</span><span class='line'>        <span class="k">where</span> <span class="k">SCHEMA_NAME</span> <span class="o">=</span> <span class="n">A</span><span class="p">.</span><span class="k">SCHEMA_NAME</span>
</span><span class='line'>          <span class="k">and</span> <span class="k">TABLE_NAME</span> <span class="o">=</span> <span class="n">A</span><span class="p">.</span><span class="k">TABLE_NAME</span>
</span><span class='line'>          <span class="k">and</span> <span class="k">COLUMN_NAME</span> <span class="o">=</span> <span class="s1">&#39;ACTIVE_ROW_FLAG&#39;</span>
</span><span class='line'>       <span class="p">)</span>
</span><span class='line'><span class="p">)</span>
</span><span class='line'>
</span><span class='line'><span class="k">select</span> <span class="s1">&#39;select &#39;&#39;&#39;</span> <span class="o">+</span> <span class="n">TABLE_SCHEMA</span> <span class="o">+</span> <span class="s1">&#39;.&#39;</span> <span class="o">+</span> <span class="k">TABLE_NAME</span> <span class="o">+</span> <span class="s1">&#39;&#39;&#39; AS TABLE, &#39;</span>
</span><span class='line'>     <span class="o">+</span> <span class="s1">&#39;count(*) AS ACTIVE_ROWS from &#39;</span> <span class="o">+</span> <span class="n">TABLE_SCHEMA</span> <span class="o">+</span> <span class="s1">&#39;.&#39;</span>
</span><span class='line'>     <span class="o">+</span> <span class="k">TABLE_NAME</span> <span class="o">+</span> <span class="s1">&#39; where ACTIVE_ROW_FLAG = 1 and &#39;</span>
</span><span class='line'>     <span class="o">+</span> <span class="s1">&#39;ETL_PROCESSED_TIMESTAMP = (SELECT MAX(ETL_PROCESSED_TIMESTAMP) &#39;</span>
</span><span class='line'>     <span class="o">+</span> <span class="s1">&#39;from &#39;</span> <span class="o">+</span> <span class="n">TABLE_SCHEMA</span> <span class="o">+</span> <span class="s1">&#39;.&#39;</span> <span class="o">+</span> <span class="k">TABLE_NAME</span> <span class="o">+</span> <span class="s1">&#39;) &#39;</span>
</span><span class='line'>     <span class="o">+</span> <span class="k">case</span> <span class="k">when</span> <span class="k">exists</span> <span class="p">(</span>
</span><span class='line'>                 <span class="k">select</span> <span class="mi">1</span>
</span><span class='line'>                   <span class="k">from</span> <span class="n">basequery</span>
</span><span class='line'>                  <span class="k">where</span> <span class="n">RANKING</span> <span class="o">&gt;</span> <span class="n">A</span><span class="p">.</span><span class="n">RANKING</span><span class="p">)</span>
</span><span class='line'>            <span class="k">then</span> <span class="s1">&#39;union &#39;</span>
</span><span class='line'>            <span class="k">else</span> <span class="s1">&#39;&#39;</span>
</span><span class='line'>            <span class="k">end</span>
</span><span class='line'>  <span class="k">from</span> <span class="n">basequery</span> <span class="n">A</span>
</span><span class='line'> <span class="k">order</span> <span class="k">by</span> <span class="n">RANKING</span>
</span></code></pre></td></tr></table></div></figure>


<p>That guy will build you an all-in-one query that pulls from all tables and combines it all into one result set with the number of rows it found and the name of the table it queried. I added some logic to order all the rows so that it automatically stop adding UNIONs when it gets to the last row.  The thing to keep in mind here is that if there are a lot of tables and these columns are not indexed, this query could take a while. You could add some where clauses to limit it to certain schemas or even particular tables. You could also build a CTE where you pull the results and then only show the rows where the count equals zero to zoom in on troublemakers. It&rsquo;s also possible to have it build in GOs for you, which means you start getting rows earlier but go back to getting them one at a time. These are all left as exercises for the reader.</p>
]]></content>
  </entry>
  
</feed>
