 




<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>SQL Server with Mr. Denny &#187; Table Variables</title>
	<atom:link href="http://itknowledgeexchange.techtarget.com/sql-server/tag/table-variables/feed/" rel="self" type="application/rss+xml" />
	<link>http://itknowledgeexchange.techtarget.com/sql-server</link>
	<description></description>
	<lastBuildDate>Fri, 17 May 2013 17:04:01 +0000</lastBuildDate>
	<language>en-US</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	
		<item>
		<title>What a difference a temp table makes over a table variable</title>
		<link>http://itknowledgeexchange.techtarget.com/sql-server/what-a-difference-a-temp-table-makes-over-a-table-variable/</link>
		<comments>http://itknowledgeexchange.techtarget.com/sql-server/what-a-difference-a-temp-table-makes-over-a-table-variable/#comments</comments>
		<pubDate>Mon, 01 Nov 2010 11:00:27 +0000</pubDate>
		<dc:creator>Denny Cherry</dc:creator>
				<category><![CDATA[SQL Server]]></category>
		<category><![CDATA[Statistics]]></category>
		<category><![CDATA[Table Variables]]></category>
		<category><![CDATA[Temp Tables]]></category>

		<guid isPermaLink="false">http://itknowledgeexchange.techtarget.com/sql-server/?p=1073</guid>
		<description><![CDATA[I was working on performance tuning the data deletion process that I use to remove data from our database.  We delete massive amounts of data on a daily basis, so our data deletion process is pretty important to us.  As we&#8217;ve grown our customer base the amount of data to be deleted every day has [...]]]></description>
				<content:encoded><![CDATA[<p>I was working on performance tuning the data deletion process that I use to remove data from our database.  We delete massive amounts of data on a daily basis, so our data deletion process is pretty important to us.  As we&#8217;ve grown our customer base the amount of data to be deleted every day has also grown, and the amount of time to delete that data has gone up as well.</p>
<p>We it has started to take stupid amounts of time to delete the data, so I started digging into the data deletion procedures.</p>
<p>The first thing I looked at was the actual delete statements.  These seams ok, the actual deletes were happening very quickly (we process deletes in batches of 1000 records per batch to minimize locking of data).  So next I looked at the part of the code where we select the records to be deleted.  Looking at the execution plan, everything looked ok.</p>
<p><a href="http://cdn.ttgtmedia.com/ITKE/uploads/blogs.dir/20/files/2010/04/bad_plan.jpg"><img class="alignnone size-medium wp-image-1070" src="http://cdn.ttgtmedia.com/ITKE/uploads/blogs.dir/20/files/2010/04/bad_plan.jpg" alt="" width="636" height="245" /></a></p>
<p>But this little chunk of code took about 50 minutes to run.  Pretty bad when only returning 1000 numbers back from the database.</p>
<pre class="brush: sql; title: ; notranslate">SELECT TOP (@BatchSize) a.PolicyIncidentId
FROM PolicyIncident a WITH (NOLOCK)
JOIN #ComputersToProcess ComputersToProcess ON a.ComputerId = ComputersToProcess.ComputerId
WHERE CaptureTimestamp &amp;lt; ComputersToProcess.StartDeleteAt
</pre>
<p>The first thing that I did was put a primary key on the @ComputersToProcess table variable.  That turned the table scan into a Clustered Index Scan, but didn&#8217;t do anything for performance.</p>
<p>The next thing I did was switch the table variable to a temp table (without a primary key).  This really didn&#8217;t do anything to speed up the process as there is still no statistics on the data.  However this time the execution plan actually shows you that there&#8217;s no statistic on the temp table.</p>
<p><a href="http://cdn.ttgtmedia.com/ITKE/uploads/blogs.dir/20/files/2010/04/table_var_no_stat.jpg"><img class="alignnone size-medium wp-image-1071" src="http://cdn.ttgtmedia.com/ITKE/uploads/blogs.dir/20/files/2010/04/table_var_no_stat.jpg" alt="" width="662" height="233" /></a></p>
<p>Now, I didn&#8217;t want to put at non-clustered index on the table keeping the table as a heap, and a clustered index that wasn&#8217;t a primary key wasn&#8217;t going be any more effective than a primary key, so I put a primary key on the table.  While the query cost percentage went up from 2% to 7% the actual run time went down from 50 minutes to just 1 second.</p>
<p><a href="http://cdn.ttgtmedia.com/ITKE/uploads/blogs.dir/20/files/2010/04/with_pk.jpg"><img class="alignnone size-medium wp-image-1072" src="http://cdn.ttgtmedia.com/ITKE/uploads/blogs.dir/20/files/2010/04/with_pk.jpg" alt="" width="633" height="213" /></a></p>
<p>Now I didn&#8217;t make any other code changes to the procedures, just changing from the table variable to the temp table, and adding a primary key and this one little three line query went from an hour to a second.  Its amazing how much such a small change can make things run smoother.</p>
<p>Now obviously this isn&#8217;t going to fix every problem.  But in my case I&#8217;m putting a little over 190k rows into the table variable (now temp table) and this is just to much for the table variable to take.  Keep in mind that with table variables the SQL Server has statistics, but it assumes only a single row per temp table, no matter how much data is actually in the table variable.</p>
<p>Denny</p>
<!-- wpms-network-global-inserts -->]]></content:encoded>
			<wfw:commentRss>http://itknowledgeexchange.techtarget.com/sql-server/what-a-difference-a-temp-table-makes-over-a-table-variable/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>OPENXML can be a beast sometimes.</title>
		<link>http://itknowledgeexchange.techtarget.com/sql-server/openxml-can-be-a-beast-sometimes/</link>
		<comments>http://itknowledgeexchange.techtarget.com/sql-server/openxml-can-be-a-beast-sometimes/#comments</comments>
		<pubDate>Mon, 30 Nov 2009 11:00:59 +0000</pubDate>
		<dc:creator>Denny Cherry</dc:creator>
				<category><![CDATA[OPENXML]]></category>
		<category><![CDATA[Performance Problems]]></category>
		<category><![CDATA[SQL]]></category>
		<category><![CDATA[Table Variables]]></category>

		<guid isPermaLink="false">http://itknowledgeexchange.techtarget.com/sql-server/?p=765</guid>
		<description><![CDATA[Our application includes some search functionality which is pretty complex to deal with.  In a nutshell the user can select multiple values from a couple of lists on the website and use those listed to filter down the rows which are being searched.  These lists are passed into the SQL Server as a couple of [...]]]></description>
				<content:encoded><![CDATA[<p>Our application includes some search functionality which is pretty complex to deal with.  In a nutshell the user can select multiple values from a couple of lists on the website and use those listed to filter down the rows which are being searched.  These lists are passed into the SQL Server as a couple of XML documents.  We recently had a larger customer call and complain that the search was slow.  I fired up profiler and grabbed the query.  They were right, 6 minutes is a long time for a query to take.<span id="more-765"></span></p>
<p>There&#8217;s some full text searching going on so it&#8217;s never going to really scream as there are 70 million records in one table, 57 million records in another table (with a one to many between them), and another 90 million records in the third table (this is a one to many to the table with 70 million records, and this table has the full text index on it).</p>
<p>Needless to say there was some tuning that I had to do.  The basic jist of the query was&#8230;</p>
<pre class="brush: sql; title: ; notranslate">
...
WHERE EXISTS (SELECT *
FROM OPENXML(@hDoc_Computer, '//computer')
WITH (ComputerId INT '@id') a
WHERE a.ComputerId = Application.ComputerId)
AND EXISTS (SELECT *
FROM OPENXML(@hDoc_Logon, '//login')
WITH (LogonId INT '@id') a
WHERE a.LogonId = Application.LogonId)</pre>
<p>After getting no where working on indexes and tweaking things here and there (and actually making the query take 16 minutes to run for this customer&#8217;s data) I put a couple of table variables in the procedure and loaded those table variables up with the values from the XML Documents.</p>
<pre class="brush: sql; title: ; notranslate">DECLARE @Computer TABLE
(ComputerId INT)

DECLARE @Logon TABLE
(LogonId INT)

...

INSERT INTO @Computer
SELECT ComputerId
FROM OPENXML(@hDoc_Computer, '//Computer', 2)
WITH (ComputerId INT '@ComputerId')

INSERT INTO @Logon
SELECT LogonId
FROM OPENXML(@hDoc_Logon, '//Logon', 2)
WITH (LogonId INT '@LogonId')

...
</pre>
<p>And I changed the WHERE clause to use the table variables instead.</p>
<pre class="brush: sql; title: ; notranslate">WHERE EXISTS (SELECT * FROM @Computer a WHERE a.ComputerId = Application.computerId)
AND EXISTS (SELECT * FROM @Logon a WHERE a.LogonId = Application.LogonId)</pre>
<p>This got my query run time down to about 1 minute with the execution plan showing that ~90% of the time spent is being spent on the full text search. So while I wouldn&#8217;t normally consider a query run time on 1 minute to be good, in this case it is. (This particular part of the application also goes out to the file server and uses Microsoft Search service to search millions of files for text string matches so this is now the fastest part of the search process.</p>
<p>Now don&#8217;t take this post the wrong way. I love OPENXML, it&#8217;s a great tool and I use it all over the place so that we can pass in multiple values in a single variable (all our code has to be able to run on SQL 2005 so table input parameters aren&#8217;t an option for me). OPENXML just wasn&#8217;t the write tool here, sort of.</p>
<p>I wish we could have found this performance problem in QA, but we just have no way to generate enough data to find these kinds of performance problems. But the problem is fixed and the customer is hopefully happy (for now).</p>
<p>Denny</p>
<!-- wpms-network-global-inserts -->]]></content:encoded>
			<wfw:commentRss>http://itknowledgeexchange.techtarget.com/sql-server/openxml-can-be-a-beast-sometimes/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Just how awesome are table parameters in SQL Server 2008?</title>
		<link>http://itknowledgeexchange.techtarget.com/sql-server/just-how-awesome-are-table-parameters-in-sql-server-2008/</link>
		<comments>http://itknowledgeexchange.techtarget.com/sql-server/just-how-awesome-are-table-parameters-in-sql-server-2008/#comments</comments>
		<pubDate>Mon, 08 Dec 2008 13:06:00 +0000</pubDate>
		<dc:creator>Denny Cherry</dc:creator>
				<category><![CDATA[CREATE PROCEDURE]]></category>
		<category><![CDATA[SQL Server 2008]]></category>
		<category><![CDATA[T/SQL]]></category>
		<category><![CDATA[Table Variables]]></category>

		<guid isPermaLink="false">http://itknowledgeexchange.techtarget.com/sql-server/just-how-awesome-are-table-parameters-in-sql-server-2008/</guid>
		<description><![CDATA[I would have to say, that one of the coolest new features of SQL Server 2008 is the ability to pass a table as a single parameter to a stored procedure. While we have been able to do this in the past, by using XML to pass more than one value in, then break it [...]]]></description>
				<content:encoded><![CDATA[<p>I would have to say, that one of the coolest new features of SQL Server 2008 is the ability to pass a table as a single parameter to a stored procedure.</p>
<p>While we have been able to do this in the past, by using XML to pass more than one value in, then break it apart. But this is just such a simpler, easier, more elegant solution.</p>
<p><span id="more-224"></span></p>
<p>It is a bit of a process to get it done, but once it is all setup it is a piece of cake to use.</p>
<p>You can&#8217;t just create a table as part of the input parameter to the stored procedure like this.</p>
<pre>
CREATE PROCEDURE YourProcedure
     @YourTable TABLE (Col1 INT)
AS
...
GO</pre>
<p>That would be to easy. First you have to create a User Defined Table Type by using the CREATE TYPE command. Then you create an input (or output) parameter using this table type then in your calling code create a parameter using this same user defined table type and load it with data, then call the procedure just as you normally would.</p>
<pre>
CREATE TYPE MyTableType AS TABLE
   (Id INT)
GO
CREATE PROCEDURE MyProcedure
   @Ids MyTableType OUTPUT
AS
INSERT INTO @Ids
SELECT object_Id
FROM sys.objects
GO
DECLARE @values MyTableType
exec MyProcedure @Ids=@values OUTPUT
SELECT *
FROM @values
GO
DROP PROCEDURE MyProcedure
GO
DROP Type MyTableType
GO</pre>
<p>Personally I can&#8217;t wait to to begin using this new feature, but it&#8217;ll probably be a while before we convert our system to require SQL Server 2008, as we have customers who are still running SQL Server 2000 and aren&#8217;t happy about our requirement for SQL Server 2005.</p>
<p>Denny</p>
<!-- wpms-network-global-inserts -->]]></content:encoded>
			<wfw:commentRss>http://itknowledgeexchange.techtarget.com/sql-server/just-how-awesome-are-table-parameters-in-sql-server-2008/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Temp Tables, Table Variables, and CTEs</title>
		<link>http://itknowledgeexchange.techtarget.com/sql-server/temp-tables-table-variables-and-ctes/</link>
		<comments>http://itknowledgeexchange.techtarget.com/sql-server/temp-tables-table-variables-and-ctes/#comments</comments>
		<pubDate>Mon, 10 Dec 2007 08:00:08 +0000</pubDate>
		<dc:creator>Denny Cherry</dc:creator>
				<category><![CDATA[CTE]]></category>
		<category><![CDATA[SQL]]></category>
		<category><![CDATA[T/SQL]]></category>
		<category><![CDATA[Table Variables]]></category>
		<category><![CDATA[Temp Tables]]></category>

		<guid isPermaLink="false">http://itknowledgeexchange.techtarget.com/sql-server/temp-tables-table-variables-and-ctes/</guid>
		<description><![CDATA[There are some major differences between temp tables, table variables and common table expressions (CTEs).  Some of the big differences are: Temp Tables vs. Table Variables SQL Server does not place locks on table variables when the table variables are used. Temp tables allow for multiple indexes to be created Table variables allow a single index [...]]]></description>
				<content:encoded><![CDATA[<p>There are some major differences between temp tables, table variables and common table expressions (CTEs).  Some of the big differences are:</p>
<p>Temp Tables vs. Table Variables</p>
<ol>
<li>SQL Server does not place locks on table variables when the table variables are used.</li>
<li>Temp tables allow for multiple indexes to be created</li>
<li>Table variables allow a single index the Primary Key to be created when the table variable is declared only.  There is an exception to this, that if you can create the index inline, for example by creating a unique constraint inline as shown in the comments.  However these indexes (and the table variable in general) will always be assumed to have 1 row in them, no matter how much data is within the table variable.</li>
<li>Temp tables can be created locally (#TableName) or globally (##TableName)</li>
<li>Table variables are destroyed as the batch is completed.</li>
<li>Temp tables can be used throughout multiple batches.</li>
<li>Temp tables can be used to hold the output of a stored procedure (temp tables will get this functionality in SQL Server 2008).</li>
</ol>
<p>Table variables and Temp Tables vs. CTEs</p>
<ol>
<li>CTEs are used after the command which creates them.</li>
<li>CTEs can be recursive within a single command (be careful because they can cause an infinite loop).</li>
<li>Table variables and Temp Tables can be used throughout the batch.</li>
<li>The command before the CTE must end with a semi-colon (;).</li>
<li>As Temp tables and table variables are tables you can insert, update and delete the data within the table.</li>
<li>CTEs can not have any indexes created on them, source tables much have indexes created on them.</li>
</ol>
<p>If you can think of anything that I&#8217;ve missed, feel free to post them in the comments.</p>
<p>Denny</p>
<!-- wpms-network-global-inserts -->]]></content:encoded>
			<wfw:commentRss>http://itknowledgeexchange.techtarget.com/sql-server/temp-tables-table-variables-and-ctes/feed/</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
	</channel>
</rss>
