<?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 Bad Practices &#187; statistics</title>
	<atom:link href="http://www.sqlbadpractices.com/tag/statistics/feed/" rel="self" type="application/rss+xml" />
	<link>http://www.sqlbadpractices.com</link>
	<description>Blog about bad (and good) practices on SQL Server</description>
	<lastBuildDate>Mon, 26 Mar 2012 01:59:04 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.3.1</generator>
		<item>
		<title>Linked servers and distributed queries</title>
		<link>http://www.sqlbadpractices.com/linked-servers-and-distributed-queries/</link>
		<comments>http://www.sqlbadpractices.com/linked-servers-and-distributed-queries/#comments</comments>
		<pubDate>Sun, 23 Oct 2011 22:37:38 +0000</pubDate>
		<dc:creator>Francois</dc:creator>
				<category><![CDATA[Administration]]></category>
		<category><![CDATA[Performance]]></category>
		<category><![CDATA[collation]]></category>
		<category><![CDATA[distributed query]]></category>
		<category><![CDATA[linked server]]></category>
		<category><![CDATA[query plan]]></category>
		<category><![CDATA[remote query]]></category>
		<category><![CDATA[sql server]]></category>
		<category><![CDATA[statistics]]></category>
		<category><![CDATA[sub-optimal query plan]]></category>
		<category><![CDATA[TSQL]]></category>
		<category><![CDATA[Use Remote Collation]]></category>

		<guid isPermaLink="false">http://www.sqlbadpractices.com/?p=621</guid>
		<description><![CDATA[Linked servers allows to issue SQL commands against OLE DB providers. Because Microsoft also makes available an &#8220;OLE DB Provider for ODBC Drivers&#8221;, it is also possible to issue queries against a variety of ODBC drivers. With linked servers and distributed queries, you can query all sorts of data sources and merge them on the [...]<p><a href="http://www.sqlbadpractices.com/linked-servers-and-distributed-queries/">Linked servers and distributed queries</a> is a post from: <a href="http://www.sqlbadpractices.com">SQL Bad Practices</a></p>
]]></description>
			<content:encoded><![CDATA[<p>Linked servers allows to issue SQL commands against OLE DB providers. Because Microsoft also makes available an &#8220;OLE DB Provider for ODBC Drivers&#8221;, it is also possible to issue queries against a variety of ODBC drivers. With linked servers and distributed queries, you can query all sorts of data sources and merge them on the fly with your SQL Server database.<span id="more-621"></span> Example of data sources includes Analysis Services (SSAS), Access, Excel, Text files, Oracle, MySQL as well as SQL Server instances and many, many other sources.</p>
<p>This article will focus on distributed queries over SQL Server instances. Organizations frequently uses linked server in order to use data that is available on other servers or instances. This flexible strategy eliminates the need of synchronizing data over several servers (for example with replication). It is not a bad choice but one must understand the pitfalls in order to make enlightened decisions.</p>
<p><strong>Creating a linked server</strong><br />
The easiest way to start is to create a linked server that references your own server:</p>
<pre class="brush: sql">
-- &#039;localhost&#039; is the standard &quot;loopback&quot; hostname
-- It points to the local machine
sp_addlinkedserver &#039;localhost&#039;
</pre>
<p>Once the &#8220;localhost&#8221; linked server is created, you can reference objects using a four-part name in the form <em>linkedserver.catalog.schema.object</em>, for example<em>:</em></p>
<pre class="brush: sql">
SELECT * FROM localhost.MY_DATABASE.dbo.MY_TABLE;
</pre>
<p><strong> Network Bandwidth and latency</strong><br />
The first obvious drawback of using a linked server is the network speed cost. For this single reason, linked server should not be used when we seek optimal performance (unless you need to scale your database on multiple servers but then again it&#8217;s not necessarily a good approach). There is a lot of overhead involved with SQL Server having to query the object metadata, the statistics (if possible) and send the query and results over the network. Note that we are not talking about end results here but intermediate query results so it doesn&#8217;t matter if your query return only one row. All this overhead makes remote query a lot more expensive than local query and joins between tables won&#8217;t be optimal. In general you want to use linked server when coupling is low, that is when you do not need to join intermediate results with the local database objects.</p>
<p><strong>Transactions</strong><br />
There is also significantly more overhead involved in distributed transactions. All servers involved in a transaction must have MSDTC service (Distributed Transaction Coordinator) &#8211; which must be properly installed and configured. Avoid distributed transactions unless absolutely necessary.</p>
<p><strong>Distribution statistics</strong><br />
The query processor uses statistics in order to produce the best possible query plan and SQL Server is able to use linked server statistics to optimize the query execution plan. <strong>However</strong>, the user running the query must have appropriate permissions on the remote server in order for the engine to use them. Awkwardly<strong>, </strong>for SQL Server, it turns out that the user running the query  must have the permission to run DBCC SHOW_STATISTICS. MSDN documentation states that :  &#8221;(&#8230;) <em>to obtain all available statistics, the user must own the table or be a member of the sysadmin fixed server role, the db_owner fixed database role, or the db_ddladmin fixed database role on the linked server.</em>&#8221; (link: <a href="http://msdn.microsoft.com/en-us/library/ms189811.aspx" target="_blank">http://msdn.microsoft.com/en-us/library/ms189811.aspx</a>). This is much, much more permissions that is needed to read a table. Let&#8217;s hope Microsoft will fix this flaw in the near future. You can vote for the Microsoft Connect suggestion <a title="here" href="https://connect.microsoft.com/SQLServer/feedback/details/626262/allow-statistics-to-be-read-if-data-can-be-read-performance-and-security" target="_blank">here</a>.</p>
<p><strong>Collations</strong><br />
Collations are used by SQL Server to compare and order strings. When working with remote SQL Server instances, the engine will correctly compare and order strings based on the remote column collation.  Therefore, if remote and local columns have  different collations it will result in collation conflicts. When defining a linked server, you have the option of using remote or local collation (&#8220;<strong>Use Remote Collation</strong>&#8221; in Server Options). If that option is set to true, SQL Server will try to push the ORDER BY and the WHERE clauses to the remote server. If <em>Use Remote Collation</em> is set to false, SQL Server will use <strong>the default collation of the local server instance</strong>. If the default collation of the local server instance do not match with the remote server column collation, this will result in poor performance. The local server will have to filter and order the data, thus having to transfer each row beforehand. It is obviously much faster to filter and order the data on the remote server. Then again, deciding to use the remote collation could lead to incorrect results.</p>
<p>Moreover, it is not possible to join on columns that have a different collation. The workaround is to explicitly cast the collation when querying the remote server with the COLLATE clause. But this is an expensive operation if you must scan millions of rows, especially if you need to access the column frequently. In that case, you should manually transfer the data to a local table with the proper collation. This problem can also arise on the same local database since collations are defined at the column level.</p>
<p><a href="http://www.sqlbadpractices.com/linked-servers-and-distributed-queries/">Linked servers and distributed queries</a> is a post from: <a href="http://www.sqlbadpractices.com">SQL Bad Practices</a></p>
]]></content:encoded>
			<wfw:commentRss>http://www.sqlbadpractices.com/linked-servers-and-distributed-queries/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Table variable for large tables (vs temporary tables)</title>
		<link>http://www.sqlbadpractices.com/using-table-variable-for-large-table-vs-temporary-table/</link>
		<comments>http://www.sqlbadpractices.com/using-table-variable-for-large-table-vs-temporary-table/#comments</comments>
		<pubDate>Wed, 14 Sep 2011 02:45:16 +0000</pubDate>
		<dc:creator>Francois</dc:creator>
				<category><![CDATA[Performance]]></category>
		<category><![CDATA[TSQL]]></category>
		<category><![CDATA[Index]]></category>
		<category><![CDATA[indexes]]></category>
		<category><![CDATA[OPTION (RECOMPILE)]]></category>
		<category><![CDATA[query plan]]></category>
		<category><![CDATA[set statistics profile]]></category>
		<category><![CDATA[statistics]]></category>
		<category><![CDATA[sub-optimal query plan]]></category>
		<category><![CDATA[table variables]]></category>
		<category><![CDATA[temp tables]]></category>

		<guid isPermaLink="false">http://www.sqlbadpractices.com/?p=548</guid>
		<description><![CDATA[The main reason why Microsoft introduced table variable in SQL Server 2000 is to reduce stored procedure recompilations (a recompilation occurs when the stored procedure execution plan is recreated). Table variables also have a well defined scope (the current procedure or function) and they induce less logging and locking (because their transaction last for a [...]<p><a href="http://www.sqlbadpractices.com/using-table-variable-for-large-table-vs-temporary-table/">Table variable for large tables (vs temporary tables)</a> is a post from: <a href="http://www.sqlbadpractices.com">SQL Bad Practices</a></p>
]]></description>
			<content:encoded><![CDATA[<p>The main reason why Microsoft introduced table variable in SQL Server 2000 is to reduce stored procedure recompilations (a recompilation occurs when the stored procedure execution plan is recreated). Table variables also have a well defined scope (the current procedure or function) and they induce less logging and locking (because their transaction last for a single SQL statement). These are great advantages when dealing with short simplier OLTP-style queries and processes.<span id="more-548"></span></p>
<p>However, there are huge drawbacks of using table variables when you process a lot of rows. For a large table, using a table variable is very often a bad practice&#8230;</p>
<p><strong>Statistics</strong><br />
First, they do not have any statistics (statistics are used by the query optimizer to produce the most efficient query plan based on data distribution). The following example demonstrates that the query optimizer has no clue about how many rows a table variable has when building the query plan:</p>
<pre class="brush: sql">
SET NOCOUNT ON
-- Declare table variable
DECLARE @TABLE_VARIABLE TABLE (ID INT PRIMARY KEY CLUSTERED)
DECLARE @I INT = 0

-- Insert 10K rows
BEGIN TRAN
WHILE @I &lt; 10000
BEGIN
INSERT INTO @TABLE_VARIABLE VALUES (@I)
SET @I=@I+1
END
COMMIT TRAN

-- Display all rows and output execution plan
set statistics profile on
SELECT * FROM @TABLE_VARIABLE
set statistics profile off
</pre>
<p>Result:</p>
<table border="1" cellspacing="0" cellpadding="0" align="center">
<tbody>
<tr>
<td valign="top" width="50"><strong>Rows</strong></td>
<td valign="top" width="200"><strong>StmtText</strong></td>
<td valign="top" width="50">&#8230;</td>
<td valign="top" width="50"><span style="background: #E9EEF3; font-weight: bold;">EstimateRows</span></td>
</tr>
<tr>
<td valign="top" width="50">10000</td>
<td valign="top" width="200">|&#8211;Clustered Index Scan(OBJECT:(@TABLE_VARIABLE))</td>
<td valign="top" width="50">&#8230;</td>
<td valign="top" width="50"><span style="background: #E9EEF3; font-weight: bold;">1</span></td>
</tr>
</tbody>
</table>
<p>The optimizer do not recompile queries that use table variables. In our example, although SQL Server performs a clustered index scan, it assumes the index has only one row because the engine does not have access to the table variable/clustered index statistics. Of course, such an assumption can make a huge impact on performance when a suboptimal query plan is used on a large table. A workaround is to use the OPTION (RECOMPILE) hint.</p>
<p><strong>Indexes</strong><br />
You can&#8217;t add indexes to a table variable. Creating specific indexes obviously helps to improve query performance. The workaround is  to specify constraints when declaring the table. Specifying a PRIMARY KEY CLUSTERED will create a clustered index and specifying a UNIQUE column will create a nonclustered index. However, you won&#8217;t always have the necessary flexibility of the indexes. For example, it won&#8217;t be possible to create non unique clustered indexes or nonclustered index with included columns.</p>
<p><strong>Parallel plans</strong><br />
When executing INSERTs, UPDATEs or DELETEs on a table variable, the SQL Server storage engine never generate a parallel execution plan. This is a huge handicap and affects heavily the query performance when playing with large datasets.</p>
<p><a href="http://www.sqlbadpractices.com/using-table-variable-for-large-table-vs-temporary-table/">Table variable for large tables (vs temporary tables)</a> is a post from: <a href="http://www.sqlbadpractices.com">SQL Bad Practices</a></p>
]]></content:encoded>
			<wfw:commentRss>http://www.sqlbadpractices.com/using-table-variable-for-large-table-vs-temporary-table/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Using local variables in T-SQL queries</title>
		<link>http://www.sqlbadpractices.com/using-local-variables-in-t-sql-queries/</link>
		<comments>http://www.sqlbadpractices.com/using-local-variables-in-t-sql-queries/#comments</comments>
		<pubDate>Fri, 15 Jul 2011 12:00:59 +0000</pubDate>
		<dc:creator>Francois</dc:creator>
				<category><![CDATA[Performance]]></category>
		<category><![CDATA[local variable]]></category>
		<category><![CDATA[OPTION (RECOMPILE)]]></category>
		<category><![CDATA[plan cache]]></category>
		<category><![CDATA[query plan]]></category>
		<category><![CDATA[set statistics profile]]></category>
		<category><![CDATA[statistics]]></category>
		<category><![CDATA[sub-optimal query plan]]></category>
		<category><![CDATA[TSQL]]></category>
		<category><![CDATA[WITH RECOMPILE]]></category>

		<guid isPermaLink="false">http://www.sqlbadpractices.com/?p=448</guid>
		<description><![CDATA[A query plan is a set of steps generated by the database engine to retrieve data. Query plans are produced by the query optimizer from SQL statements. SQL Server automatically caches query plans and try to reuse them whenever possible. For many applications (such as OLTP transactional applications), plan reuse is a very good thing since [...]<p><a href="http://www.sqlbadpractices.com/using-local-variables-in-t-sql-queries/">Using local variables in T-SQL queries</a> is a post from: <a href="http://www.sqlbadpractices.com">SQL Bad Practices</a></p>
]]></description>
			<content:encoded><![CDATA[<p>A query plan is a set of steps generated by the database engine to retrieve data. Query plans are produced by the query optimizer from SQL statements.</p>
<p>SQL Server automatically caches query plans and try to reuse them whenever possible. For many applications (such as OLTP transactional applications), plan reuse is a very good thing since it avoids unneeded compilations that may take much time to complete each time a query is executed. SQL Server caches query plans (execution plans based on parameter assumptions) but not execution contexts (execution plans based on the actual parameters values).<span id="more-448"></span> If you execute a query or stored procedure several times per second, you want to reuse the query plan as much as possible. However, when querying large tables, using the optimal plan is preferable since the queries may take several minutes to complete. In these cases, it is obviously better to save minutes (sometime hours) with an optimal plan at the cost of that extra 1-second of plan compilation.</p>
<p><strong>In many cases you may end up with a sub-optimal query plan because the queries are compiled before the actual parameter values are known. Such is the case when local variables are used</strong>. Let&#8217;s create a sales table with 1 million sales on July 1st and 5 sales on July 2th with a index on the sale date (tested on SQL Server 2008 R2):</p>
<pre class="brush: sql">
SET NOCOUNT ON

-- Drop Sales Table
IF EXISTS(select 1 from INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME=&#039;SALES_TABLE&#039;)
DROP TABLE SALES_TABLE

-- Create Sales Table
CREATE TABLE SALES_TABLE(
[SALES_ID] [int] NOT NULL IDENTITY(1,1) PRIMARY KEY CLUSTERED
,SALE_DATE [datetime] NOT NULL
,SALE_AMOUNT [numeric](28,10) NOT NULL
)

-- Insert Sales Data
DECLARE @I INT = 0
BEGIN TRAN
WHILE @I &lt; 1000000
BEGIN
INSERT INTO SALES_TABLE(SALE_DATE,SALE_AMOUNT) SELECT &#039;20110701&#039;, RAND() * 100.0
SET @I=@I+1
END

SET @I=0
WHILE @I &lt; 5
BEGIN
INSERT INTO SALES_TABLE(SALE_DATE,SALE_AMOUNT) SELECT &#039;20110702&#039;, RAND() * 100.0
SET @I=@I+1
END
COMMIT TRAN

-- Create index on Sale Date
CREATE NONCLUSTERED INDEX [IX_SALE_DATE] ON [SALES_TABLE]
(
[SALE_DATE] ASC
)
</pre>
<p>Let&#8217;s summarized the sales for July 2th:</p>
<pre class="brush: sql">
-- Query with constant value
set statistics profile on
SELECT SUM([SALE_AMOUNT]) FROM SALES_TABLE WHERE [SALE_DATE]=&#039;20110702&#039;
set statistics profile off
</pre>
<p>You will notice in the results that the engine does an Index Seek on the IX_SALE_DATE index. This is the optimal plan since there is only 5 sales on July 2th. Now let&#8217;s declare the local variable @mydate and set it to July 2th:</p>
<pre class="brush: sql">
-- Parametrized query with local variable
declare @mydate datetime = &#039;20110702&#039;
set statistics profile on
SELECT SUM([SALE_AMOUNT]) FROM SALES_TABLE WHERE [SALE_DATE]=@mydate
set statistics profile off
</pre>
<p>SQL Server does not use the optimal plan (it does a clustered index scan instead). Why is that? This is because the engine simply ignores the local variable value and compiles a plan based on general statistics assumptions. The compiled query plan is &#8220;good enough&#8221; for just about any value of @mydate (note that sometimes the query optimizer is way off, you must make sure that you have enough statistics and that they are up-to-date). When you are using local variables in your queries and you can afford to lose one or two seconds, you should force query recompilation using this syntax:</p>
<p>Use OPTION (RECOMPILE)</p>
<pre class="brush: sql">
-- Parametrized query with local variable and OPTION (RECOMPILE)
declare @mydate datetime = &#039;20110702&#039;
set statistics profile on
SELECT SUM([SALE_AMOUNT]) FROM SALES_TABLE WHERE [SALE_DATE]=@mydate OPTION (RECOMPILE)
set statistics profile off
</pre>
<p>Note that the same principles applies to stored procedure compilations (you can use WITH RECOMPILE argument with the EXEC statement) when you want the procedure to recompile with the provided parameters, thus avoiding parameter sniffing).</p>
<p><a href="http://www.sqlbadpractices.com/using-local-variables-in-t-sql-queries/">Using local variables in T-SQL queries</a> is a post from: <a href="http://www.sqlbadpractices.com">SQL Bad Practices</a></p>
]]></content:encoded>
			<wfw:commentRss>http://www.sqlbadpractices.com/using-local-variables-in-t-sql-queries/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>
