<?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</title>
	<atom:link href="http://www.sqlbadpractices.com/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>Speeding up aggregates with indexed views</title>
		<link>http://www.sqlbadpractices.com/speeding-up-aggregates-with-indexed-views/</link>
		<comments>http://www.sqlbadpractices.com/speeding-up-aggregates-with-indexed-views/#comments</comments>
		<pubDate>Sun, 25 Mar 2012 19:52:12 +0000</pubDate>
		<dc:creator>Francois</dc:creator>
				<category><![CDATA[Performance]]></category>
		<category><![CDATA[TSQL]]></category>
		<category><![CDATA[Index]]></category>
		<category><![CDATA[indexed view]]></category>
		<category><![CDATA[join]]></category>
		<category><![CDATA[query plan]]></category>
		<category><![CDATA[view]]></category>

		<guid isPermaLink="false">http://www.sqlbadpractices.com/?p=803</guid>
		<description><![CDATA[Ever so often, queries include some aggregates data. In a datawarehouse environment, they are frequently used for metric and Key Performance Indicator (KPI) calculations. Aggregates are also used a lot for reporting purposes and for statistical computation. When a query perform poorly, our first instinct is to add an index to speed it up. Creating a [...]<p><a href="http://www.sqlbadpractices.com/speeding-up-aggregates-with-indexed-views/">Speeding up aggregates with indexed views</a> is a post from: <a href="http://www.sqlbadpractices.com">SQL Bad Practices</a></p>
]]></description>
			<content:encoded><![CDATA[<p>Ever so often, queries include some aggregates data. In a datawarehouse environment, they are frequently used for metric and Key Performance Indicator (KPI) calculations. Aggregates are also used a lot for reporting purposes and for statistical computation.</p>
<p>When a query perform poorly, our first instinct is to add an index to speed it up. Creating a view and indexing that view is often an overlooked solution. However, an indexed view might perform much better with less performance hit (on INSERT, UPDATE &amp; DELETE) compared with an index. Enterprise edition of SQL Server is able to automatically use indexed views even when they are not referenced, just like indexes.</p>
<p>Let&#8217;s create a typical web log table with two million entries. This table contains the cookie_id of the visitor, the visit date and the transaction value if the user made a purchase during the visit.</p>
<pre class="brush: sql">
-- Create table
IF OBJECT_ID(&#039;WEBLOG&#039;,&#039;U&#039;) IS NULL
BEGIN
CREATE TABLE [WEBLOG](
[COOKIE_ID] [int] NOT NULL,
[VISIT_DATE] [date] NOT NULL,
[TRANSACTION_VALUE] [money] NOT NULL
) ON [PRIMARY]

CREATE CLUSTERED INDEX [IX_VISIT_DATE] ON [WEBLOG]
( [VISIT_DATE] ASC ) ON [PRIMARY]
END

-- Empty the table
DELETE FROM WEBLOG

-- Insert 2 million rows
DECLARE @I INT = 0
DECLARE @HITS INT = 2000000
BEGIN TRANSACTION

WHILE @I&lt;@HITS
BEGIN

INSERT INTO WEBLOG(COOKIE_ID,VISIT_DATE,TRANSACTION_VALUE)
SELECT RAND()*@HITS/100 -- COOKIE_ID
,DATEADD(day,CONVERT(INT,(RAND()*365)),&#039;20110101&#039;) -- VISIT DATE
,CASE WHEN RAND()&lt;0.01
THEN RAND()*10.0 ELSE 0.0 END -- TRANSACTION_VALUE

SET @I=@I+1
END

COMMIT TRANSACTION
</pre>
<p>We wish to report the number of visits, the lifetime value (total purchases) and revenue per visit of each cookie:</p>
<pre class="brush: sql">
SELECT COOKIE_ID
, COUNT(*) AS FREQUENCY
, SUM(TRANSACTION_VALUE) AS LIFETIME_VALUE
, AVG(TRANSACTION_VALUE) AS REVENUE_PER_VISIT
FROM [DBO].WEBLOG
GROUP BY COOKIE_ID
</pre>
<p>On average, this query takes 600.4 milliseconds on my system.  We can speed it up using a nonclustered index:</p>
<pre class="brush: sql">
CREATE NONCLUSTERED INDEX [IX_RFM] ON [WEBLOG] ([COOKIE_ID])
INCLUDE ( [TRANSACTION_VALUE]) ON [PRIMARY]
</pre>
<p>It now takes 336 milliseconds to run the query, a performance improvement of 44%. We can increase this performance gain by using an indexed view instead of creating the previous index:</p>
<pre class="brush: sql">
IF OBJECT_ID(&#039;RFM&#039;,&#039;V&#039;) IS NOT NULL
DROP VIEW [RFM]
GO

-- Create view
CREATE VIEW [RFM] WITH SCHEMABINDING AS
SELECT COOKIE_ID, SUM(TRANSACTION_VALUE) AS MONETARY
,COUNT_BIG(*) AS FREQUENCY
FROM [DBO].WEBLOG
GROUP BY COOKIE_ID
GO

-- Create clustered index on view, making it a indexed view
CREATE UNIQUE CLUSTERED INDEX IDX_RFM_V ON [RFM] (COOKIE_ID);
</pre>
<p>The query now runs in 53.8 ms on average: a 91% performance gain. The thing is, fetching the data is almost instantaneous because the result of the view is materialized: the majority of the elapsed time is spent displaying the query results. Performance do not depend on the underlying table but rather on what can be fetched from the materialized results of the view (everything, in our case):</p>
<div id="attachment_841" class="wp-caption aligncenter" style="width: 531px"><a href="http://www.sqlbadpractices.com/wp-content/uploads/2013/03/Indexed-view-execution-plan.png"><img class="size-full wp-image-841" title="Indexed-view-execution-plan" src="http://www.sqlbadpractices.com/wp-content/uploads/2013/03/Indexed-view-execution-plan.png" alt="Indexed view execution plan" width="521" height="72" /></a><p class="wp-caption-text">Query execution plan</p></div>
<p>Here, even if the average aggregate (AVG) is not defined in the view, the query optimizer is able to derive the result from the COUNT and the SUM aggregates. If the view gets big, you can also create nonclustered indexes on your view to speed-up access to subsets of your view. You get the best query performance gains if your underlying tables are large and your query results stay small (hence the benefit with aggregations).</p>
<p>The improved query speed comes with additional overhead when modifying table data (just like indexes). The following table displays a summary of my test results:</p>
<p><a href="http://www.sqlbadpractices.com/wp-content/uploads/2013/03/Indexed-views-performance.png"><img class="aligncenter size-full wp-image-830" title="Indexed-views-performance" src="http://www.sqlbadpractices.com/wp-content/uploads/2013/03/Indexed-views-performance.png" alt="Indexed views performance" width="456" height="81" /></a></p>
<p>Indexed view is also a great way to improve INNER JOINS performance. When two or more table are prejoined in an indexed view, the query optimizer can choose to retrieve the materialized view data instead of performing a costly join operation.</p>
<p>For more information on indexed views, see the following Microsoft article: <a href="http://technet.microsoft.com/en-us/library/cc917715.aspx" target="_blank">http://technet.microsoft.com/en-us/library/cc917715.asp</a>.</p>
<p><a href="http://www.sqlbadpractices.com/speeding-up-aggregates-with-indexed-views/">Speeding up aggregates with indexed views</a> is a post from: <a href="http://www.sqlbadpractices.com">SQL Bad Practices</a></p>
]]></content:encoded>
			<wfw:commentRss>http://www.sqlbadpractices.com/speeding-up-aggregates-with-indexed-views/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Windows Authentication: giving built-in or service accounts permissions to your database</title>
		<link>http://www.sqlbadpractices.com/windows-authentication-giving-builtin-service-accounts-permissions-to-your-database/</link>
		<comments>http://www.sqlbadpractices.com/windows-authentication-giving-builtin-service-accounts-permissions-to-your-database/#comments</comments>
		<pubDate>Mon, 16 Jan 2012 11:02:04 +0000</pubDate>
		<dc:creator>Francois</dc:creator>
				<category><![CDATA[Security]]></category>
		<category><![CDATA[Application Pool]]></category>
		<category><![CDATA[IIS]]></category>
		<category><![CDATA[Local Service]]></category>
		<category><![CDATA[Local System]]></category>
		<category><![CDATA[login]]></category>
		<category><![CDATA[login security]]></category>
		<category><![CDATA[Network Service]]></category>
		<category><![CDATA[permissions]]></category>
		<category><![CDATA[Services]]></category>
		<category><![CDATA[Windows Authentication]]></category>

		<guid isPermaLink="false">http://www.sqlbadpractices.com/?p=745</guid>
		<description><![CDATA[Some days ago, I was asked to do a quick security check on a web application. After reviewing authentification mechanisms best practices, we quickly looked at how the application was connecting to its databases. As with a lot of web app, the application was connecting to the database through a unique login. Positive points first: Windows Athentication [...]<p><a href="http://www.sqlbadpractices.com/windows-authentication-giving-builtin-service-accounts-permissions-to-your-database/">Windows Authentication: giving built-in or service accounts permissions to your database</a> is a post from: <a href="http://www.sqlbadpractices.com">SQL Bad Practices</a></p>
]]></description>
			<content:encoded><![CDATA[<p>Some days ago, I was asked to do a quick security check on a web application. After reviewing authentification mechanisms best practices, we quickly looked at how the application was connecting to its databases. As with a lot of web app, the application was connecting to the database through a unique login.<span id="more-745"></span></p>
<p>Positive points first:</p>
<p><strong>Windows Athentication mode was used </strong>(<strong><span style="color: #008000;">√</span></strong><span style="color: #000000;">)</span><a href="http://www.sqlbadpractices.com/wp-content/uploads/2200/01/Thumb-up-icon.png"><br />
</a> The application is using the Windows Athentication mode to connect to its databases. This is great since you don&#8217;t have to include usernames or password within your code. Moreover, Windows has all sort of policies that you an enable to make your application more secure, such as Password Complexity policy or Password expiration policy. Just make sure SQL Server enforces it (CHECK_POLICY=ON at the login level &#8211; see <a title="ALTER LOGIN" href="http://msdn.microsoft.com/en-us/library/ms189828.aspx" target="_blank">ALTER LOGIN</a>).</p>
<p>Another benefit is that no clear text password will be transferred through the communication protocol, (as it is when SQL Server password is used), preventing attackers from sniffing passwords. More details <a title="here" href="http://msdn.microsoft.com/en-us/library/ee798283(v=cs.20).aspx" target="_blank">here</a>.</p>
<p><strong>Allow remote connections to this server was unchecked </strong>(<span style="color: #008000;"><strong>√</strong></span>)<br />
Since the web application is running on the same server as SQL Server, there is no need to accept remote connections, because the connections will always be handled by the web application. This prevents all intrusions from external applications or individuals.</p>
<p><strong>SQL Server port was blocked by the Firewall </strong>(<span style="color: #008000;"><strong>√</strong></span>)<br />
By default, SQL Server listens to TCP port 1433 for remote connections. Blocking this port on the Firewall level adds a layer of protection.</p>
<p>HOWEVER, there was two major security issues:</p>
<p><strong>To much permission was given to the login </strong><span style="color: #000000;">(<strong><span style="color: #ff0000;">x</span></strong>)</span><br />
An important principle in computer security is the principle of least privilege. In this case, this principle means that the login should have had the minimal permissions needed by the application. However, the login was given administrative privileges (sysadmin server role) when he should have had only read/write permissions to some tables and execute permissions to some stored procedures.</p>
<p><strong>The login used to connect to the database was the IIS anonymous built-in account</strong> (<strong><span style="color: #ff0000;">x</span></strong>)<br />
Like a lot of web application, IIS anonymous authentication was used and the application was connecting through a single login. By default when IIS anonymous authentication mode is used, the ISS web application is running with the IUSR_<em>YourComputerName</em> windows user (IIS anonymous built-in account). So database permissions were given to that login.</p>
<p>This is a bad practice. If you give permissions to SQL Server to this login, you end up with a big security flaw: everyone who is able to run an IIS application will have access to your database (or worse, they could administer your whole SQL instance as in this specific case). Other web applications won&#8217;t even need a password because IIS automatically impersonate  this account!</p>
<p>The same is true if you have an application running as a Windows service and you decide that you want to connect to SQL Server using Windows authentication while your service runs under the Local System, Network Service or Local Service accounts. If you give these special accounts access to your database, any service (current or new) will be able to access your data without even providing a password&#8230;</p>
<p><strong>So what should be done ?</strong></p>
<p>1- Create a local account:</p>
<p style="text-align: center;"><a href="http://www.sqlbadpractices.com/wp-content/uploads/2200/01/Create-user.png"><img class="aligncenter size-full wp-image-786" style="border-image: initial; border-width: 1px; border-color: black; border-style: solid;" src="http://www.sqlbadpractices.com/wp-content/uploads/2200/01/Create-user.png" alt="Create Windows User" width="507" height="177" /></a></p>
<p style="text-align: left;">2- Give this user minimal permissions to your database:</p>
<p style="text-align: center;"><a href="http://www.sqlbadpractices.com/wp-content/uploads/2200/01/CreateLogin.png"><img class="aligncenter size-full wp-image-777" style="border-image: initial; border-width: 1px; border-color: black; border-style: solid;" src="http://www.sqlbadpractices.com/wp-content/uploads/2200/01/CreateLogin.png" alt="Create SQL Server Login" width="521" height="146" /></a></p>
<p style="text-align: left;">3- In IIS, modify the Anonymous Authentication credentials from <em>IUSR</em> to <em>Application pool identity</em>. More details <a title="here" href="http://technet.microsoft.com/en-us/library/cc770966(WS.10).aspx" target="_blank">here</a>.</p>
<p style="text-align: left;">4- Create an IIS Application Pool for your web application and set its Identity to the local user you just created:</p>
<p style="text-align: center;"><a href="http://www.sqlbadpractices.com/wp-content/uploads/2200/01/Set-application-pool-identity1.png"><img class="aligncenter size-full wp-image-782" style="border-image: initial; border-width: 1px; border-color: black; border-style: solid;" src="http://www.sqlbadpractices.com/wp-content/uploads/2200/01/Set-application-pool-identity1.png" alt="Set Application Pool Identity" width="534" height="121" /></a></p>
<p style="text-align: left;">Your web application will now be running under your local account and be able to connect to its database using Windows Authentication mode. You can do the same if your application is a Windows service: depending on your needs, you can modify the account identity under the <em>Log On</em> tab or impersonate your local account when you wish to interact to your database.</p>
<p><a href="http://www.sqlbadpractices.com/windows-authentication-giving-builtin-service-accounts-permissions-to-your-database/">Windows Authentication: giving built-in or service accounts permissions to your database</a> is a post from: <a href="http://www.sqlbadpractices.com">SQL Bad Practices</a></p>
]]></content:encoded>
			<wfw:commentRss>http://www.sqlbadpractices.com/windows-authentication-giving-builtin-service-accounts-permissions-to-your-database/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Should you shrink your database in your maintenance plan?</title>
		<link>http://www.sqlbadpractices.com/should-you-shrink-your-database-in-your-maintenance-plan/</link>
		<comments>http://www.sqlbadpractices.com/should-you-shrink-your-database-in-your-maintenance-plan/#comments</comments>
		<pubDate>Fri, 18 Nov 2011 00:38:51 +0000</pubDate>
		<dc:creator>Francois</dc:creator>
				<category><![CDATA[Administration]]></category>
		<category><![CDATA[DBCC]]></category>
		<category><![CDATA[fragmentation]]></category>
		<category><![CDATA[Maintenance Plan]]></category>
		<category><![CDATA[NOTRUNCATE]]></category>
		<category><![CDATA[SHRINKDATABASE]]></category>
		<category><![CDATA[SHRINKFILE]]></category>
		<category><![CDATA[TRUNCATEONLY]]></category>

		<guid isPermaLink="false">http://www.sqlbadpractices.com/?p=654</guid>
		<description><![CDATA[Management Studio provides a neat GUI interface to define maintenance plans for your SQL databases. This tool is available under Management -&#62; Maintenance Plans and provides many great Maintenance Plan Tasks that you can configure easily. There are many useful tasks such as &#8220;Check Database Integrity Task&#8221;, &#8220;Back Up Database Task&#8221;, &#8220;Rebuild Index Task&#8221;, etc. [...]<p><a href="http://www.sqlbadpractices.com/should-you-shrink-your-database-in-your-maintenance-plan/">Should you shrink your database in your maintenance plan?</a> is a post from: <a href="http://www.sqlbadpractices.com">SQL Bad Practices</a></p>
]]></description>
			<content:encoded><![CDATA[<p>Management Studio provides a neat GUI interface to define maintenance plans for your SQL databases. This tool is available under Management -&gt; Maintenance Plans and provides many great Maintenance Plan Tasks that you can configure easily. There are many useful tasks such as &#8220;Check Database Integrity Task&#8221;, &#8220;Back Up Database Task&#8221;, &#8220;Rebuild Index Task&#8221;, etc.<span id="more-654"></span></p>
<p style="text-align: center;"><a href="http://www.sqlbadpractices.com/wp-content/uploads/2015/11/shrink-database-maintenance-plan-mini1.png"><img class="size-full wp-image-663 aligncenter" title="shrink-database-maintenance-plan-mini" src="http://www.sqlbadpractices.com/wp-content/uploads/2015/11/shrink-database-maintenance-plan-mini1.png" alt="Maintenance Plans Shrink Database Task" width="376" height="243" /></a></p>
<p>One of the task is &#8220;Shrink Database Task&#8221; and should <strong>never </strong>be used as part of a scheduled maintenance plan. Essentially because it does nothing useful and does many awfull things.</p>
<p>Let&#8217;s start by listing the cons of shrinking a database:</p>
<p>1- First it takes time and a lot of IO and CPU. If you are lucky enough to have a time window when you can perform heavy maintenance tasks, you should use this time to do useful tasks such as rebuilding some indexes, updating you statistics, verifying the integrity of your databases, backing up your databases, testing your backups, loading and warming your cubes, etc.</p>
<p>2- Second a SHRINKDATABASE without the TRUNCATEONLY argument (which is essentially calling SHRINKFILE for each database data and log files) will attempt to move data to the beginning of the file. Doing so, it will induce index fragmentation: the data won&#8217;t be contiguous anymore and index scans will perform slower compared to non-fragmented index scans.</p>
<p>3- Also, by shrinking your files you are releasing space back to Windows (unless you specify NOTRUNCATE &#8211; and I can not imagine a case where specifying this argument would make sense). But is this a good thing? I have yet to see a database that shrinks over time so you will eventually have to grow the data file back. File grow takes time and recurrent file growth induces physical fragmentation which increases disk io latency.</p>
<p>The advantage of shrinking a database:</p>
<p>1- Shrinking the database release disk space. As a pointed out in the last bullet, this is not necessary a good thing.</p>
<p>You should consider shrinking only when the following conditions are met:</p>
<ul>
<li>You desperately need the space and can&#8217;t add disk space to the system</li>
<li>You deleted a lot of data (or grew the data file too much) and you don&#8217;t expect the data files to grow back. A good example would be to shrink files which are on a filegroup that will become read-only, such as with date-based partitions.</li>
<li>This is a manual operation and the shrink is not part of a recurrent maintenance task</li>
<li>You first tried SHRINKFILE with the TRUNCATEONLY argument. The operation won&#8217;t induce fragmentation and the SHRINKFILE will be very fast.</li>
</ul>
<div>Great article by Paul S.Randal who recommends creating a new filegroup instead of using SHRINKDATABASE: <a href="http://www.sqlskills.com/BLOGS/PAUL/post/Why-you-should-not-shrink-your-data-files.aspx" target="_blank">http://www.sqlskills.com/BLOGS/PAUL/post/Why-you-should-not-shrink-your-data-files.aspx</a>.</div>
<p><a href="http://www.sqlbadpractices.com/should-you-shrink-your-database-in-your-maintenance-plan/">Should you shrink your database in your maintenance plan?</a> is a post from: <a href="http://www.sqlbadpractices.com">SQL Bad Practices</a></p>
]]></content:encoded>
			<wfw:commentRss>http://www.sqlbadpractices.com/should-you-shrink-your-database-in-your-maintenance-plan/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<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>0</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 NOT IN operator with null values</title>
		<link>http://www.sqlbadpractices.com/using-not-in-operator-with-null-values/</link>
		<comments>http://www.sqlbadpractices.com/using-not-in-operator-with-null-values/#comments</comments>
		<pubDate>Fri, 19 Aug 2011 02:15:00 +0000</pubDate>
		<dc:creator>Francois</dc:creator>
				<category><![CDATA[Performance]]></category>
		<category><![CDATA[TSQL]]></category>
		<category><![CDATA[IN]]></category>
		<category><![CDATA[NOT IN]]></category>
		<category><![CDATA[NULL]]></category>
		<category><![CDATA[null values]]></category>
		<category><![CDATA[operator]]></category>

		<guid isPermaLink="false">http://www.sqlbadpractices.com/?p=504</guid>
		<description><![CDATA[The IN operator compares a value with a list of values. It must however be used with care when we are dealing with nulls. Let&#8217;s create a table containing three city names and a null value. The goal is check whether a city is in the list or not. -- By default ANSI_NULLS is off [...]<p><a href="http://www.sqlbadpractices.com/using-not-in-operator-with-null-values/">Using NOT IN operator with null values</a> is a post from: <a href="http://www.sqlbadpractices.com">SQL Bad Practices</a></p>
]]></description>
			<content:encoded><![CDATA[<p>The IN operator compares a value with a list of values. It must however be used with<br />
care when we are dealing with nulls.</p>
<p>Let&#8217;s create a table containing three city names and a null value. The goal is check<br />
whether a city is in the list or not.<span id="more-504"></span></p>
<pre class="brush: sql">
-- By default ANSI_NULLS is off so null comparisons follows the SQL-92 standard.
-- In future version of SQL Server, it won&#039;t be possible to modify this setting.
SET ANSI_NULLS OFF

IF EXISTS(select 1 from INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME=&#039;CITIES&#039;)
DROP TABLE [CITIES]

CREATE TABLE [CITIES] (CITY [varchar](50) NULL)

INSERT INTO CITIES
SELECT &#039;PARIS&#039; UNION ALL
SELECT &#039;MONTREAL&#039; UNION ALL
SELECT &#039;NEW YORK&#039; UNION ALL
SELECT NULL
</pre>
<p>The table now contains the following city names:</p>
<div align="center">
<table border="1" cellspacing="0" cellpadding="0">
<tbody>
<tr>
<td valign="top" width="64">PARIS</td>
</tr>
<tr>
<td valign="top" width="64">MONTREAL</td>
</tr>
<tr>
<td valign="top" width="64">NEW YORK</td>
</tr>
<tr>
<td valign="top" width="64"><em>NULL</em></td>
</tr>
</tbody>
</table>
</div>
<p>Let&#8217;s use the IN operator to determine if Montreal is in the city table:</p>
<pre class="brush: sql">
SELECT&#039;Found Montreal&#039;
WHERE&#039;Montreal&#039; IN (SELECT city from CITIES)
</pre>
<p>Montreal is found so everything is all right. Now let&#8217;s try the following query to find out if Sidney appears in the table:</p>
<pre class="brush: sql">
SELECT &#039;Found Sidney&#039;
WHERE &#039;Sidney&#039; IN (SELECT city from CITIES)
</pre>
<p>We still get the right result: Sidney is not in the list so no row is returned. Now to find out if Sidney is missing in the table, we would write something like that:</p>
<pre class="brush: sql">
SELECT &#039;Sidney Not Found&#039;
WHERE &#039;Sidney&#039; NOT IN (SELECT city from CITIES)
</pre>
<p>However here something is definitevely wrong. Sidney is not in the list and still no rows is returned. Let&#8217;s try a different approach:</p>
<pre class="brush: sql">
SELECT&#039;Sidney Not Found&#039;
WHERE &#039;Sidney&#039; NOT IN (&#039;Paris&#039;,&#039;Montreal&#039;,&#039;New York&#039;)
</pre>
<p>That one works. The null value affects the outcome of the NOT IN operator. This is because the operator compares each city in the list; the previous query is logically equivalent to the following query:</p>
<pre class="brush: sql">
SELECT &#039;Sidney Not Found&#039;
WHERE &#039;Sidney&#039;&lt;&gt;&#039;Paris&#039;
AND &#039;Sidney&#039;&lt;&gt;&#039;Montreal&#039;
</pre>
<p>We therefore get this logically equivalent query if we add a null value:</p>
<pre class="brush: sql">
SELECT &#039;Sidney Not Found&#039;
WHERE &#039;Sidney&#039;&lt;&gt;&#039;Paris&#039;
AND &#039;Sidney&#039;&lt;&gt;&#039;Montreal&#039;
AND &#039;Sidney&#039;&lt;&gt;null
</pre>
<p>&#8230; and since, by default, &#8220;Sidney &lt;&gt;null&#8221; is UNKNOWN (neither true or false), no row is returned because every condition must be true in order for the AND operator to return a TRUE result. The same counter-intuitive result happens with the IN operator, like in this example:</p>
<pre class="brush: sql">
SELECT city from CITIES
WHERE city in (select city from CITIES)
</pre>
<p>Here null is in the list disappeared, because NULL&lt;&gt;NULL.</p>
<p>When checking for existence, you should use the EXISTS operator if the columns involved are nullables. Using IN operator might produce an inferior plan and can lead to misleading results if a null value is inserted in the table. In our example, we can rewrite our query as:</p>
<pre class="brush: sql">
SELECT &#039;Sidney Not Found&#039;
WHERE NOT EXISTS
(SELECT 1/0 FROM CITIES WHERE CITY = &#039;Sidney&#039;)
</pre>
<p>The EXISTS operator returns TRUE if the subquery returns at least a row and FALSE otherwise. Also note that the columns returned by the subquery are never evaluated because there is no need to. That is why the previous query didn&#8217;t throw a &#8220;Divide by zero error&#8221;.</p>
<p><a href="http://www.sqlbadpractices.com/using-not-in-operator-with-null-values/">Using NOT IN operator with null values</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-not-in-operator-with-null-values/feed/</wfw:commentRss>
		<slash:comments>0</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>
		<item>
		<title>Enabling Boost SQL Server priority Option</title>
		<link>http://www.sqlbadpractices.com/boost-sql-server-priority/</link>
		<comments>http://www.sqlbadpractices.com/boost-sql-server-priority/#comments</comments>
		<pubDate>Sun, 12 Jun 2011 11:15:29 +0000</pubDate>
		<dc:creator>Francois</dc:creator>
				<category><![CDATA[Administration]]></category>
		<category><![CDATA[CPU]]></category>
		<category><![CDATA[server properties]]></category>

		<guid isPermaLink="false">http://www.sqlbadpractices.com/?p=406</guid>
		<description><![CDATA[On occasion I have seen Database Administrators enabling the SQL Server &#8220;Boost SQL Server priority&#8221; option. This option is available on the Server Properties Window under Processors: If you enable this option, SQL Server will run the sqlservr.exe process and threads as High Priority instead of its usual Normal priority. Hence, when SQL Server service [...]<p><a href="http://www.sqlbadpractices.com/boost-sql-server-priority/">Enabling Boost SQL Server priority Option</a> is a post from: <a href="http://www.sqlbadpractices.com">SQL Bad Practices</a></p>
]]></description>
			<content:encoded><![CDATA[<p>On occasion I have seen Database Administrators enabling the SQL Server &#8220;Boost SQL Server priority&#8221; option. This option is available on the Server Properties Window under <em>Processors</em>:<span id="more-406"></span></p>
<p style="text-align: center;"><a href="http://www.sqlbadpractices.com/wp-content/uploads/2011/06/BoostSQLServerPriority.png"><img class="aligncenter size-full wp-image-408" title="Boost SQL Server Priority" src="http://www.sqlbadpractices.com/wp-content/uploads/2011/06/BoostSQLServerPriority.png" alt="Boost SQL Server Priority" width="346" height="172" /></a></p>
<p>If you enable this option, SQL Server will run the <em>sqlservr.exe</em> process and threads as High Priority instead of its usual Normal priority. Hence, when SQL Server service will request CPU, other processes in need of CPU time won&#8217;t be prioritized. In some scenarios it can lead to problems and most of the time it won&#8217;t bring any benefit. Microsoft do not recommend to enable this feature, see this <a title="Microsoft Support article" href="http://support.microsoft.com/kb/319942" target="_blank">Microsoft Support article</a> (search for <em>Priority Boost</em>).</p>
<p>Production server generally falls into one of these two categories:</p>
<p>1. <strong>One server box handling everything. </strong>For example, SQL Server is installed on the same machine as a web server running a web application with perhaps Analysis Services or SISS processes that runs once in a while. This is the worst scenario for enabling the priority boost because you need to do several jobs at the same time therefore you want all your process to run smoothly together. One example would be to launch a CPU-intensive query within SSIS while computing complex business rules or building Data Mining models. You certainly do not wish SQL Server to take all CPU resources here because other computations are running simultaneously.</p>
<p>2. <strong>Dedicated SQL Server server</strong>. If SQL Server is the only service running then there won&#8217;t be other processes fighting for CPU time so enabling the &#8220;Boost SQL Server priority&#8221; feature brings no benefit. Plus, you could get some unpredictable results because core processes such as Windows processes or device drivers may not get enough resources when SQL Servers runs CPU-intensive queries (for example several queries performing hash joins between large tables).</p>
<p>To be fair, Microsoft mentions in this <a title="Microsoft Connect" href="http://connect.microsoft.com/SQLServer/feedback/details/559289/remove-the-priority-boost" target="_blank">Microsoft Connect</a> article that you might see some performance improvements in &#8220;high-end servers primarily with OLTP workloads&#8221;. My opinion is that even in the very rare occasions that you get small improvement for SQL Server, your overall system performance may worsen.</p>
<p>Here&#8217;s an example demonstrating that the option is very dangerous. <span style="text-decoration: underline;">Do not try this if you are connected to a local SQL Server instance</span> because your system will become completely unresponsive and you will have to manually shut down your computer. Do NOT try this on a production server&#8230;</p>
<p>1. Configure the priority boost Option (see <a title="How to: Configure the priority boost Option" href="http://msdn.microsoft.com/en-us/library/ms188709.aspx" target="_blank">How to: Configure the priority boost Option</a> for the steps) and restart SQL Server as indicated.</p>
<p>2. Open a new query window in Management Studio and run the following script:</p>
<pre class="brush: sql">
DECLARE @I INT
SELECT @I = 1
FROM sys.objects a
CROSS JOIN sys.objects b
CROSS JOIN sys.objects c
CROSS JOIN sys.objects d
CROSS JOIN sys.objects e
CROSS JOIN sys.objects f
CROSS JOIN sys.objects g
</pre>
<p>3. Repeat step #2 until the system is totally unresponsive&#8230;</p>
<p>4. Cancel all query executions and the system will come back to its normal state.</p>
<p>Nice article on the priority boost option: <a title="http://blogs.msdn.com/b/arvindsh/archive/2010/01/27/priority-boost-details-and-why-it-s-not-recommended.aspx" href="http://blogs.msdn.com/b/arvindsh/archive/2010/01/27/priority-boost-details-and-why-it-s-not-recommended.aspx" target="_blank">http://blogs.msdn.com/b/arvindsh/archive/2010/01/27/priority-boost-details-and-why-it-s-not-recommended.aspx</a>.</p>
<p><span style="text-decoration: underline;">UPDATE</span>: MSDN states in the SQL Server 2008 R2 documentation that the priority boost option will be removed in future versions of SQL Server: <a href="http://msdn.microsoft.com/en-us/library/ms180943.aspx" target="_blank">http://msdn.microsoft.com/en-us/library/ms180943.aspx</a> .</p>
<p><a href="http://www.sqlbadpractices.com/boost-sql-server-priority/">Enabling Boost SQL Server priority Option</a> is a post from: <a href="http://www.sqlbadpractices.com">SQL Bad Practices</a></p>
]]></content:encoded>
			<wfw:commentRss>http://www.sqlbadpractices.com/boost-sql-server-priority/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Keeping Maximum Server Memory default value</title>
		<link>http://www.sqlbadpractices.com/keeping-maximum-server-memory-default-value/</link>
		<comments>http://www.sqlbadpractices.com/keeping-maximum-server-memory-default-value/#comments</comments>
		<pubDate>Fri, 03 Jun 2011 01:00:01 +0000</pubDate>
		<dc:creator>Francois</dc:creator>
				<category><![CDATA[Administration]]></category>
		<category><![CDATA[DBCC]]></category>
		<category><![CDATA[memory]]></category>
		<category><![CDATA[server properties]]></category>

		<guid isPermaLink="false">http://www.sqlbadpractices.com/?p=352</guid>
		<description><![CDATA[The default value for SQL Server 2008 Maximum Server Memory setting is 2,147,483,647 MB (or 2.1 petabytes!). Therefore, by default, SQL Server will use all available memory for its own use: If you don&#8217;t lower this setting, you will reduce the memory available for other services such as Integration Services (SSIS), Analysis Services (SSAS), Reporting [...]<p><a href="http://www.sqlbadpractices.com/keeping-maximum-server-memory-default-value/">Keeping Maximum Server Memory default value</a> is a post from: <a href="http://www.sqlbadpractices.com">SQL Bad Practices</a></p>
]]></description>
			<content:encoded><![CDATA[<p>The default value for SQL Server 2008 <em>Maximum Server Memory</em> setting is 2,147,483,647 MB (or 2.1 petabytes!). Therefore, by default, SQL Server will use all available memory for its own use:<span id="more-352"></span></p>
<p><a href="http://www.sqlbadpractices.com/wp-content/uploads/2011/06/ServerPropertiesMemory.png"><img class="aligncenter size-full wp-image-365" title="SSMS_ServerPropertiesMemory" src="http://www.sqlbadpractices.com/wp-content/uploads/2011/06/ServerPropertiesMemory.png" alt="SQL Server default Maximum Server Memory" width="276" height="195" /></a></p>
<p>If you don&#8217;t lower this setting, you will reduce the memory available for other services such as Integration Services (SSIS), Analysis Services (SSAS), Reporting Services (SSIS) as well as other Windows services (you should also disable the services that you do not use).</p>
<p>I have run into cases where SQL Server 2008 had difficulties handling memory pressure and would throw the infamous  &#8220;There is insufficient system memory to run this query&#8221; error.</p>
<p>Depending on the amount of total memory of your production server, and if you don&#8217;t use other memory-intensive services, you should at least reserve 1-2 Go to the Windows operating system (for example, by specifying 14000 MB on a 16 Go machine). If your server is 64-bit, you will be able to easily verify that the <em>sqlserv.exe</em> process in Windows Task Manager will never be way higher than the value you specified (it can go a little higher). If you have a 32-bit version of SQL Server, the <em>sqlserv.exe </em>process will be limited to 2 or 3 Go depending on your server configuration.</p>
<p>To allow SQL Server to use more than 3 Go of memory on 32-bit systems, you have to configure AWE memory allocation. Thereafter, you can verify the memory utilization by issuing the <em>DBCC MEMORYSTATUS</em> command or use SQL Server counters in Performance Monitor (here Windows Task Manager will not report the correct memory utilization). That being said, you should not enable AWE if your 32-bit server doesn&#8217;t have more than 4 Go of memory. Also, keep in mind that AWE setting will be ignored on 64-bit setups.</p>
<p>It goes without saying that if you are running multiple SQL Server instances or other memory-intensive processes such as Analyses Services on the same server, you should carefully configure each services&#8217; memory settings. For example, Analysis Services provide the same memory control mechanism with <strong>Memory \ TotalMemoryLimit</strong> server property.</p>
<p><a href="http://www.sqlbadpractices.com/keeping-maximum-server-memory-default-value/">Keeping Maximum Server Memory default value</a> is a post from: <a href="http://www.sqlbadpractices.com">SQL Bad Practices</a></p>
]]></content:encoded>
			<wfw:commentRss>http://www.sqlbadpractices.com/keeping-maximum-server-memory-default-value/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>SSIS Non cached Lookups without a covering index</title>
		<link>http://www.sqlbadpractices.com/ssis-non-cached-lookups-without-a-covering-index/</link>
		<comments>http://www.sqlbadpractices.com/ssis-non-cached-lookups-without-a-covering-index/#comments</comments>
		<pubDate>Fri, 20 May 2011 02:28:20 +0000</pubDate>
		<dc:creator>Francois</dc:creator>
				<category><![CDATA[Performance]]></category>
		<category><![CDATA[SSIS]]></category>
		<category><![CDATA[covering index]]></category>
		<category><![CDATA[indexes]]></category>
		<category><![CDATA[io statistics]]></category>
		<category><![CDATA[Lookup]]></category>
		<category><![CDATA[Non Cached Lookup]]></category>
		<category><![CDATA[Partial Lookup]]></category>

		<guid isPermaLink="false">http://www.sqlbadpractices.com/?p=228</guid>
		<description><![CDATA[The vast majority of the time, you will use SSIS Lookup component in Full Cache mode. This mode is the fastest because it queries the database only once (before the data flow starts) and apply hashing in order to do high-performance comparisons. Sometimes however you will have to use Non Cached lookups. For example if [...]<p><a href="http://www.sqlbadpractices.com/ssis-non-cached-lookups-without-a-covering-index/">SSIS Non cached Lookups without a covering index</a> is a post from: <a href="http://www.sqlbadpractices.com">SQL Bad Practices</a></p>
]]></description>
			<content:encoded><![CDATA[<p>The vast majority of the time, you will use SSIS Lookup component in Full Cache mode. This mode is the fastest because it queries the database only once (before the data flow starts) and apply hashing in order to do high-performance comparisons.</p>
<p>Sometimes however you will have to use Non Cached lookups. For example if your reference table doesn&#8217;t fit in memory or if you wish to lookup rows that you just inserted in your reference table at the beginning of your data flow. You might also run into cases where you need to do inequalities lookups or where you have very few rows at the source and you wish to lookup a table which has several million rows.<span id="more-228"></span></p>
<p>Because the Non Cached mode will query the database for each row, it has to be fast.</p>
<p>If your reference table is large and you omit to create a covering index, you will get very poor performance. A covering index, by definition, is an index that contains each column that is used by your query. Note that only the columns in your WHERE statement (the Lookup join columns) must be in the index, the other columns (Lookup reference columns) need only to be in the &#8220;Included Columns&#8221; of the index. Including your reference columns in the index makes it easy for SQL Server to get your reference values without going back to the table pages.</p>
<p>Let&#8217;s create a lookup table:</p>
<pre class="brush: sql">
IF NOT EXISTS(select 1 from INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME=&#039;LOOKUP_TABLE&#039;)
CREATE TABLE LOOKUP_TABLE(
[LOOKUP] [varchar](36) NULL,
[REFERENCE] [int] NOT NULL PRIMARY KEY CLUSTERED)
</pre>
<p>And add 1 million rows:</p>
<pre class="brush: sql">
SET NOCOUNT ON

TRUNCATE TABLE LOOKUP_TABLE
DECLARE @I INT = 0
BEGIN TRAN

WHILE @I &lt; 1000000
BEGIN
INSERT INTO LOOKUP_TABLE(LOOKUP,REFERENCE) SELECT NEWID(), @I
SET @I=@I+1
END

COMMIT TRAN
</pre>
<p>Select <em>No Cache</em> or <em>Partial Cache</em> in the SSIS Lookup Component dialog box:</p>
<p><img class="aligncenter" src="http://www.sqlbadpractices.com/wp-content/uploads/2011/05/SSIS_NoCache.png" alt="SSIS Lookup No Cache" /></p>
<p>The SSIS Lookup query should be defined like this:</p>
<p><img class="aligncenter" src="http://www.sqlbadpractices.com/wp-content/uploads/2011/05/SSIS_Lookup_Query.png" alt="SSIS Lookup Query" /></p>
<p>And the SSIS Lookup Columns should be defined like this:</p>
<p><img class="aligncenter" src="http://www.sqlbadpractices.com/wp-content/uploads/2011/05/SSIS_Lookup_Columns.png" alt="SSIS Lookup Columns" /></p>
<p>The covering index is then created this way:</p>
<pre class="brush: sql">
CREATE NONCLUSTERED INDEX
[IX_NON_CLUSTERED_LOOKUP_INCLUDE_LOOKUP]
ON [LOOKUP_TABLE] ([LOOKUP] ASC)
INCLUDE ([REFERENCE])
</pre>
<p>We can appreciate the performance gain for 1000 rows:</p>
<table class="aligncenter" border="1" cellspacing="0" cellpadding="0">
<tbody>
<tr>
<td width="272" valign="top"><strong>Index</strong></td>
<td width="164" valign="top"><strong>Elapsed Time</strong></td>
</tr>
<tr>
<td width="272" valign="top">Clustered Index Scan</td>
<td width="164" valign="top">37.3 sec</td>
</tr>
<tr>
<td width="272" valign="top">Covering Index</td>
<td width="164" valign="top">0.109 sec</td>
</tr>
</tbody>
</table>
<p>Another way of judging the performance of the lookup is to display SQL Server query statistics for a single query:</p>
<p>Force SQL Server to use clustered index instead of covering index:</p>
<pre class="brush: sql">
-- Clear memory cache and show some stats
DBCC DROPCLEANBUFFERS
set statistics io on
set statistics time on

-- Use default SSIS Lookup syntax
select * from (
SELECT [LOOKUP],[REFERENCE] FROM [LOOKUP_TABLE]
WITH (INDEX (0)) -- ...but tell SQL to scan the table instead of using the covering index
) [refTable]
where [refTable].[LOOKUP] = &#039;77EE7751-6BC1-4088-874E-D8F4440BB01A&#039;
</pre>
<p><strong>Results:</strong><br />
Table &#8216;LOOKUP_TABLE&#8217;. Scan count 9, <span style="background: #E9EEF3; font-weight: bold;">logical reads 6699, physical reads 152</span><br />
CPU time = 296 ms,  <span style="background: #E9EEF3; font-weight: bold;">elapsed time = 624 ms</span>.</p>
<p>Here SQL Server will use the covering index:</p>
<pre class="brush: sql">
-- Clear memory cache and show some stats
DBCC DROPCLEANBUFFERS
set statistics io on
set statistics time on

-- Use default SSIS Lookup syntax
select * from (
SELECT [LOOKUP],[REFERENCE] FROM [LOOKUP_TABLE]
) [refTable]
where [refTable].[LOOKUP] = &#039;77EE7751-6BC1-4088-874E-D8F4440BB01A&#039;
</pre>
<p><strong>Results:</strong><br />
Table &#8216;LOOKUP_TABLE&#8217;. Scan count 1, <span style="background: #E9EEF3; font-weight: bold;">logical reads 3, physical reads 2</span><br />
CPU time = 0 ms,  <span style="background: #E9EEF3; font-weight: bold;">elapsed time = 31 ms</span>.</p>
<p><a href="http://www.sqlbadpractices.com/ssis-non-cached-lookups-without-a-covering-index/">SSIS Non cached Lookups without a covering index</a> is a post from: <a href="http://www.sqlbadpractices.com">SQL Bad Practices</a></p>
]]></content:encoded>
			<wfw:commentRss>http://www.sqlbadpractices.com/ssis-non-cached-lookups-without-a-covering-index/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>How NOT to retrieve IDENTITY value</title>
		<link>http://www.sqlbadpractices.com/how-not-to-retrieve-identity-value/</link>
		<comments>http://www.sqlbadpractices.com/how-not-to-retrieve-identity-value/#comments</comments>
		<pubDate>Fri, 06 May 2011 21:17:21 +0000</pubDate>
		<dc:creator>Francois</dc:creator>
				<category><![CDATA[TSQL]]></category>
		<category><![CDATA[@@IDENTITY]]></category>
		<category><![CDATA[bug]]></category>
		<category><![CDATA[INSERT]]></category>
		<category><![CDATA[OUTPUT]]></category>
		<category><![CDATA[SCOPE_IDENTITY()]]></category>
		<category><![CDATA[sql]]></category>
		<category><![CDATA[sql server]]></category>

		<guid isPermaLink="false">http://www.sqlbadpractices.com/?p=52</guid>
		<description><![CDATA[It is a common business case to have to reuse the auto-generated SQL Server&#8217;s IDENTITY value. One way to deal with the problem is to use the system function @@IDENTITY. For example: -- Create test table IF NOT EXISTS(select 1 from INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME=&#039;Identity_Test&#039;) CREATE TABLE Identity_Test(id int IDENTITY, value int) -- Insert row and [...]<p><a href="http://www.sqlbadpractices.com/how-not-to-retrieve-identity-value/">How NOT to retrieve IDENTITY value</a> is a post from: <a href="http://www.sqlbadpractices.com">SQL Bad Practices</a></p>
]]></description>
			<content:encoded><![CDATA[<p>It is a common business case to have to reuse the auto-generated SQL Server&#8217;s IDENTITY value. One way to deal with the problem is to use the system function @@IDENTITY. For example:<span id="more-52"></span></p>
<pre class="brush: sql">
-- Create test table
IF NOT EXISTS(select 1 from INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME=&#039;Identity_Test&#039;)
CREATE TABLE Identity_Test(id int IDENTITY, value int)

-- Insert row and retrieve IDENTITY value
INSERT INTO Identity_Test(value) VALUES(NULL);
SELECT @@IDENTITY;
</pre>
<p>The problem with this code is that <strong>you may not retrieve the identity value that you inserted</strong>. For example, if there is a trigger on the table performing an insert on another table, you will get the last created identity value. Even if you never create any trigger, you may get skewed results with replicated tables since SQL Server creates his own replication triggers.</p>
<p>One way to deal with the problem is to use SCOPE_IDENTITY():</p>
<pre class="brush: sql">
-- Create test table
IF NOT EXISTS(select 1 from INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME=&#039;Identity_Test&#039;)
CREATE TABLE Identity_Test(id int IDENTITY, value int)

-- Insert row and retrieve IDENTITY value
INSERT INTO Identity_Test(value) VALUES(NULL);
SELECT SCOPE_IDENTITY();
</pre>
<p>In theory, that should always provide the last value that you inserted. However, there is a <a href="http://support.microsoft.com/default.aspx?scid=kb;en-US;2019779">nasty bug</a> in SQL Server affecting SCOPE_IDENTITY() results when a query plan involving parallelism is generated. This is not the case in our example because <em>INSERT INTO &#8230; VALUES</em> won&#8217;t generate a parallel plan but it still is a serious issue when dealing with <em>INSERT INTO &#8230; SELECT</em> queries.</p>
<p>The recommended way of retrieving identity values (and the only one that can retrieve multiple values) is to use the OUTPUT clause:</p>
<pre class="brush: sql">
-- Create test table
IF NOT EXISTS(select 1 from INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME=&#039;Identity_Test&#039;)
CREATE TABLE Identity_Test(id int IDENTITY, value int)

-- Insert two rows and retrieve identity values
DECLARE @IDs TABLE (id INT)
INSERT INTO Identity_Test(value) OUTPUT inserted.id INTO @IDs VALUES(NULL);
INSERT INTO Identity_Test(value) OUTPUT inserted.id INTO @IDs VALUES(NULL);
SELECT id FROM @IDs;
</pre>
<p>See Microsoft Knowledge Base Article: <a href="http://support.microsoft.com/default.aspx?scid=kb;en-US;2019779">http://support.microsoft.com/default.aspx?scid=kb;en-US;2019779</a>.</p>
<p><a href="http://www.sqlbadpractices.com/how-not-to-retrieve-identity-value/">How NOT to retrieve IDENTITY value</a> is a post from: <a href="http://www.sqlbadpractices.com">SQL Bad Practices</a></p>
]]></content:encoded>
			<wfw:commentRss>http://www.sqlbadpractices.com/how-not-to-retrieve-identity-value/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Heap Tables</title>
		<link>http://www.sqlbadpractices.com/heap-tables/</link>
		<comments>http://www.sqlbadpractices.com/heap-tables/#comments</comments>
		<pubDate>Fri, 29 Apr 2011 00:00:12 +0000</pubDate>
		<dc:creator>Francois</dc:creator>
				<category><![CDATA[Performance]]></category>
		<category><![CDATA[Clustered]]></category>
		<category><![CDATA[Heap]]></category>
		<category><![CDATA[Index]]></category>
		<category><![CDATA[sql]]></category>
		<category><![CDATA[sql server]]></category>

		<guid isPermaLink="false">http://www.sqlbadpractices.com/?p=22</guid>
		<description><![CDATA[Heap tables (tables without a clustered index) are generally not part of a good database design. Unless you never actually query your table, you should always put a clustered index on it. Heap table are generally slower on selects, updates and deletes. They are also generally slower on inserts if you decide to use a [...]<p><a href="http://www.sqlbadpractices.com/heap-tables/">Heap Tables</a> is a post from: <a href="http://www.sqlbadpractices.com">SQL Bad Practices</a></p>
]]></description>
			<content:encoded><![CDATA[<p>Heap tables (tables without a clustered index) are generally not part of a good database design.</p>
<p>Unless you never actually query your table, you should always put a clustered index on it. Heap table are generally slower on selects, updates and deletes. They are also generally slower on inserts if you decide to use a nonclustered index instead of a clustered index (which you shouldn&#8217;t do).<span id="more-22"></span></p>
<p>By default, Management Studio creates a <em>clustered</em> primary key so heap are sometimes created because no primary key has been defined on a table. A primary key is <strong>NOT</strong> the same thing as a clustered index. A primary key cannot have duplicate or null values but a clustered index certainly can.</p>
<p>Moreover, a heap table is way bigger and has generally high fragmentation. Fragmentation occurs on non heap tables but you can at least rebuild your index to get rid of it.</p>
<p>If you have a log table that almost never gets queried and you don&#8217;t care about disk space then you could use a heap table. If you need a table to insert large sets of data before creating a clustered index you should create a temporary heap table for performance on inserts. Otherwise, always define a clustered index on all your tables.</p>
<p>Nice article on performance of heap+index versus clustered index: <a href="http://msdn.microsoft.com/en-us/library/cc917672.aspx">http://msdn.microsoft.com/en-us/library/cc917672</a>.</p>
<p><a href="http://www.sqlbadpractices.com/heap-tables/">Heap 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/heap-tables/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>

