<?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 Fool &#187; filtered indexes</title>
	<atom:link href="http://sqlfool.com/tag/filtered-indexes/feed/" rel="self" type="application/rss+xml" />
	<link>http://sqlfool.com</link>
	<description>Adventures in SQL Tuning - a blog for the rest of us</description>
	<lastBuildDate>Wed, 02 Nov 2011 20:39:11 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.0</generator>
		<item>
		<title>Filtered Indexes Work-Around</title>
		<link>http://sqlfool.com/2010/02/filtered-indexes-work-around/</link>
		<comments>http://sqlfool.com/2010/02/filtered-indexes-work-around/#comments</comments>
		<pubDate>Mon, 01 Feb 2010 16:00:14 +0000</pubDate>
		<dc:creator>Michelle Ufford</dc:creator>
				<category><![CDATA[Performance & Tuning]]></category>
		<category><![CDATA[SQL 2008]]></category>
		<category><![CDATA[SQL Tips]]></category>
		<category><![CDATA[Syndication]]></category>
		<category><![CDATA[2008]]></category>
		<category><![CDATA[filtered indexes]]></category>
		<category><![CDATA[performance]]></category>

		<guid isPermaLink="false">http://sqlfool.com/?p=1307</guid>
		<description><![CDATA[Recently, I needed to create a stored procedure that queried a rather large table. The table has a filtered index on a date column, and it covers the query. However, the Query Optimizer was not using the index, which was increasing the execution time (not to mention IO!) by at least 10x. This wasn't the [...]]]></description>
			<content:encoded><![CDATA[<p>Recently, I needed to create a stored procedure that queried a rather large table.  The table has a filtered index on a date column, and it covers the query.  However, the Query Optimizer was not using the index, which was increasing the execution time (not to mention IO!) by at least 10x.  This wasn't the first time I've had the Optimizer fail to use a filtered index.  Normally when this happens, I use a table hint to force the filtered index -- after I verify that it is indeed faster, of course.  However, since this was a stored procedure, I was receiving the following error message whenever I tried to execute the proc:</p>
<p><span style="color: #ff0000;">Query processor could not produce a query plan because of the hints defined in this query. Resubmit the query without specifying any hints and without using SET FORCEPLAN.</span></p>
<p>SQL Server would not allow me to execute the stored procedure using the filtered index hint.  If I removed the hint, it executed, but it used a different, non-covering and far more expensive index.  For those of you not familiar with this issue, allow me to illustrate the problem.</p>
<p>First, create a table to play with and populate it with some bogus data:</p>

<div class="wp_syntax"><div class="code"><pre class="tsql" style="font-family:monospace;"><span style="color: #0000FF;">Create</span> <span style="color: #0000FF;">Table</span> dbo.<span style="color: #202020;">filteredIndexTest</span>
<span style="color: #808080;">&#40;</span>
      myID   <span style="color: #0000FF;">int</span> <span style="color: #0000FF;">Identity</span><span style="color: #808080;">&#40;</span><span style="color: #000;">1</span>,<span style="color: #000;">3</span><span style="color: #808080;">&#41;</span>
    , myDate <span style="color: #0000FF;">smalldatetime</span> 
    , myData <span style="color: #0000FF;">char</span><span style="color: #808080;">&#40;</span><span style="color: #000;">100</span><span style="color: #808080;">&#41;</span>
&nbsp;
    <span style="color: #0000FF;">Constraint</span> PK_filteredIndexTest
        <span style="color: #0000FF;">Primary</span> <span style="color: #0000FF;">Key</span> <span style="color: #0000FF;">Clustered</span><span style="color: #808080;">&#40;</span>myID<span style="color: #808080;">&#41;</span>
<span style="color: #808080;">&#41;</span>;
&nbsp;
<span style="color: #0000FF;">Set</span> <span style="color: #0000FF;">NoCount</span> <span style="color: #0000FF;">On</span>;
<span style="color: #0000FF;">Declare</span> @<span style="color: #0000FF;">date</span> <span style="color: #0000FF;">smalldatetime</span> <span style="color: #808080;">=</span> <span style="color: #FF0000;">'2010-01-01'</span>;
&nbsp;
<span style="color: #0000FF;">While</span> @<span style="color: #0000FF;">date</span> <span style="color: #808080;">&lt;</span> <span style="color: #FF0000;">'2010-02-01'</span>
<span style="color: #0000FF;">Begin</span>
&nbsp;
    <span style="color: #0000FF;">Insert</span> <span style="color: #0000FF;">Into</span> dbo.<span style="color: #202020;">filteredIndexTest</span>
    <span style="color: #808080;">&#40;</span>
          myDate
        , myData
    <span style="color: #808080;">&#41;</span>
    <span style="color: #0000FF;">Select</span> @<span style="color: #0000FF;">date</span>
        , <span style="color: #FF0000;">'Date: '</span> <span style="color: #808080;">+</span> <span style="color: #0000FF;">Convert</span><span style="color: #808080;">&#40;</span><span style="color: #0000FF;">varchar</span><span style="color: #808080;">&#40;</span><span style="color: #000;">20</span><span style="color: #808080;">&#41;</span>, @<span style="color: #0000FF;">date</span>, <span style="color: #000;">102</span><span style="color: #808080;">&#41;</span>;
&nbsp;
    <span style="color: #0000FF;">Set</span> @<span style="color: #0000FF;">date</span> <span style="color: #808080;">=</span> <span style="color: #FF00FF;">DateAdd</span><span style="color: #808080;">&#40;</span><span style="color: #0000FF;">minute</span>, <span style="color: #000;">1</span>, @<span style="color: #0000FF;">date</span><span style="color: #808080;">&#41;</span>;
&nbsp;
<span style="color: #0000FF;">End</span>;
&nbsp;
<span style="color: #0000FF;">Select</span> <span style="color: #FF00FF;">Count</span><span style="color: #808080;">&#40;</span><span style="color: #808080;">*</span><span style="color: #808080;">&#41;</span> <span style="color: #0000FF;">From</span> dbo.<span style="color: #202020;">filteredIndexTest</span>;</pre></div></div>

<p>It looks like this will generate 44,640 rows of test data... plenty enough for our purposes.  Now, let's create our filtered index and write a query that will use it:</p>

<div class="wp_syntax"><div class="code"><pre class="tsql" style="font-family:monospace;"><span style="color: #0000FF;">Create</span> <span style="color: #0000FF;">NonClustered</span> <span style="color: #0000FF;">Index</span> IX_filteredIndexTest_1
    <span style="color: #0000FF;">On</span> dbo.<span style="color: #202020;">filteredIndexTest</span><span style="color: #808080;">&#40;</span>myDate<span style="color: #808080;">&#41;</span>
    Include <span style="color: #808080;">&#40;</span>myData<span style="color: #808080;">&#41;</span>
    <span style="color: #0000FF;">Where</span> myDate <span style="color: #808080;">&gt;=</span> <span style="color: #FF0000;">'2010-01-27'</span>;
&nbsp;
<span style="color: #0000FF;">Select</span> <span style="color: #0000FF;">Distinct</span> myData
<span style="color: #0000FF;">From</span> dbo.<span style="color: #202020;">filteredIndexTest</span>
<span style="color: #0000FF;">Where</span> myDate <span style="color: #808080;">&gt;=</span> <span style="color: #FF0000;">'2010-01-28'</span>;</pre></div></div>

<p>If you look at the execution plan for this query, you'll notice that the Optimizer is using the filtered index.  Perfect!  Now let's parameterize it.</p>

<div class="wp_syntax"><div class="code"><pre class="tsql" style="font-family:monospace;"><span style="color: #0000FF;">Declare</span> @myDate1 <span style="color: #0000FF;">smalldatetime</span> <span style="color: #808080;">=</span> <span style="color: #FF0000;">'2010-01-28'</span>;
&nbsp;
<span style="color: #0000FF;">Select</span> <span style="color: #0000FF;">Distinct</span> myData
<span style="color: #0000FF;">From</span> dbo.<span style="color: #202020;">filteredIndexTest</span>
<span style="color: #0000FF;">Where</span> myDate <span style="color: #808080;">&gt;=</span> @myDate1;</pre></div></div>

<p>Uh oh.  Looking at the execution plan, we see that SQL Server is no longer using the filtered index.  Instead, it's scanning the clustered index!  Why is this?  There's actually a good explanation for it.  The reason is that I could, in theory, pass a date to my parameter that fell outside of the filtered date range.  If that's the case, then SQL Server could not utilize the filtered index.  Personally, I think it's a bug and SQL Server should identify whether or not a filtered index could be used based on the actual value submitted, but... that's a whole other blog post.  <img src='http://sqlfool.com/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' />   </p>
<p>So what can we do?  Well, dynamic SQL may be able to help us out in this case.  Let's give it a go.  First, let's try parameterized dynamic SQL.</p>

<div class="wp_syntax"><div class="code"><pre class="tsql" style="font-family:monospace;"><span style="color: #0000FF;">Declare</span> @mySQL1 <span style="color: #0000FF;">nvarchar</span><span style="color: #808080;">&#40;</span><span style="color: #000;">2000</span><span style="color: #808080;">&#41;</span>
    , @myParam <span style="color: #0000FF;">nvarchar</span><span style="color: #808080;">&#40;</span><span style="color: #000;">2000</span><span style="color: #808080;">&#41;</span> <span style="color: #808080;">=</span> <span style="color: #FF0000;">'@p_myDate2 smalldatetime'</span>
    , @myDate2 <span style="color: #0000FF;">smalldatetime</span> <span style="color: #808080;">=</span> <span style="color: #FF0000;">'2010-01-28'</span>;
&nbsp;
<span style="color: #0000FF;">Set</span> @mySQL1 <span style="color: #808080;">=</span> <span style="color: #FF0000;">'Select Distinct myData
              From dbo.filteredIndexTest
              Where myDate &gt;= @p_myDate2'</span>;
&nbsp;
<span style="color: #0000FF;">Execute</span> <span style="color: #AF0000;">sp_executeSQL</span> @mySQL1, @myParam, @p_myDate2 <span style="color: #808080;">=</span> @myDate2;</pre></div></div>

<p>Looking at the execution plan, we see we're still scanning on the clustered index.  This is because the parameterized dynamic SQL resolves to be the exact same query as the one above it.  Let's try unparameterized SQL instead:</p>

<div class="wp_syntax"><div class="code"><pre class="tsql" style="font-family:monospace;"><span style="color: #0000FF;">Declare</span> @mySQL2 <span style="color: #0000FF;">nvarchar</span><span style="color: #808080;">&#40;</span><span style="color: #000;">2000</span><span style="color: #808080;">&#41;</span>
    , @myDate3 <span style="color: #0000FF;">smalldatetime</span> <span style="color: #808080;">=</span> <span style="color: #FF0000;">'2010-01-28'</span>;
&nbsp;
<span style="color: #0000FF;">Set</span> @mySQL2 <span style="color: #808080;">=</span> <span style="color: #FF0000;">'Select Distinct myData
              From dbo.filteredIndexTest
              Where myDate &gt;= '</span><span style="color: #FF0000;">''</span> <span style="color: #808080;">+</span> <span style="color: #0000FF;">Cast</span><span style="color: #808080;">&#40;</span>@myDate3 <span style="color: #0000FF;">As</span> <span style="color: #0000FF;">varchar</span><span style="color: #808080;">&#40;</span><span style="color: #000;">20</span><span style="color: #808080;">&#41;</span><span style="color: #808080;">&#41;</span> <span style="color: #808080;">+</span> <span style="color: #FF0000;">''</span><span style="color: #FF0000;">''</span>;
&nbsp;
<span style="color: #0000FF;">Execute</span> <span style="color: #AF0000;">sp_executeSQL</span> @mySQL2;
&nbsp;
<span style="color: #008080;">-- Drop Table dbo.filteredIndexTest;</span></pre></div></div>

<p>Voila!  We have a seek on our filtered index.  Why?  Because the statement resolves to be identical to our first query, where we hard-coded the date value in the WHERE clause.</p>
<p>Now, I want to stress this fact: you should always, ALWAYS use parameterized dynamic SQL whenever possible.  Not only is it safer, but it's also faster, because it can reuse cached plans.  But sometimes you just cannot accomplish the same tasks with it.  This is one of those times.  If you do end up needing to use unparameterized dynamic SQL as a work-around, please make sure you're validating your input, especially if you're interfacing with any sort of external source.</p>
<p>There's an even easier work-around for this problem that <a href="http://twitter.com/CrappyCodingGuy ">Dave</a> (<a href="http://www.crappycoding.com" target="_blank">http://www.crappycoding.com</a>) shared with me: <a href="http://msdn.microsoft.com/en-us/library/ms181714.aspx" target="_blank">recompile</a>.</p>
<p>Adding "Option (Recompile)" to the end of your statements will force the Optimizer to re-evaluate which index will best meet the needs of your query every time the statement is executed.  More importantly, it evaluates the plan based on the actual values passed to the parameter... just like in our hard-coded and dynamic SQL examples.  Let's see it in action:</p>

<div class="wp_syntax"><div class="code"><pre class="tsql" style="font-family:monospace;"><span style="color: #0000FF;">DECLARE</span> @myDate4 <span style="color: #0000FF;">SMALLDATETIME</span> <span style="color: #808080;">=</span> <span style="color: #FF0000;">'2010-01-28'</span>;
&nbsp;
<span style="color: #0000FF;">SELECT</span> <span style="color: #0000FF;">DISTINCT</span> myData
<span style="color: #0000FF;">FROM</span> dbo.<span style="color: #202020;">filteredIndexTest</span> 
<span style="color: #0000FF;">WHERE</span> myDate <span style="color: #808080;">&gt;=</span> @myDate4
<span style="color: #0000FF;">OPTION</span> <span style="color: #808080;">&#40;</span>RECOMPILE<span style="color: #808080;">&#41;</span>;
&nbsp;
<span style="color: #0000FF;">DECLARE</span> @myDate5 <span style="color: #0000FF;">SMALLDATETIME</span> <span style="color: #808080;">=</span> <span style="color: #FF0000;">'2010-01-20'</span>;
&nbsp;
<span style="color: #0000FF;">SELECT</span> <span style="color: #0000FF;">DISTINCT</span> myData
<span style="color: #0000FF;">FROM</span> dbo.<span style="color: #202020;">filteredIndexTest</span> 
<span style="color: #0000FF;">WHERE</span> myDate <span style="color: #808080;">&gt;=</span> @myDate5
<span style="color: #0000FF;">OPTION</span> <span style="color: #808080;">&#40;</span>RECOMPILE<span style="color: #808080;">&#41;</span>;</pre></div></div>

<p>If we look at the execution plans for the 2 queries above, we see that the first query seeks on the filtered index, and the second query scans on the clustered index.  This is because the second query cannot be satisfied with the filtered index because we initially limited our index to dates greater than or equal to 1/27/2010.  </p>
<p>There are, of course, trade-offs associated with each approach, so use whichever one best meets your needs.  Do you have another work-around for this issue?  If so, please let me know.  <img src='http://sqlfool.com/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' /> </p>
<p>Update:  </p>
<p>Alex Kuznetsov (<a href="http://www.simple-talk.com/author/alex-kuznetsov/">http://www.simple-talk.com/author/alex-kuznetsov/</a>) shared this method too:</p>

<div class="wp_syntax"><div class="code"><pre class="tsql" style="font-family:monospace;"><span style="color: #0000FF;">DECLARE</span> @myDate1 <span style="color: #0000FF;">SMALLDATETIME</span> <span style="color: #808080;">=</span> <span style="color: #FF0000;">'2010-01-28'</span>;
&nbsp;
<span style="color: #0000FF;">SELECT</span> <span style="color: #0000FF;">DISTINCT</span> myData
<span style="color: #0000FF;">FROM</span> dbo.<span style="color: #202020;">filteredIndexTest</span>
<span style="color: #0000FF;">WHERE</span> myDate <span style="color: #808080;">=</span> @myDate1
<span style="color: #808080;">AND</span> myDate <span style="color: #808080;">&gt;=</span> <span style="color: #FF0000;">'2010-01-27'</span>;</pre></div></div>

<p>Like the other examples, this will result in an index seek on the filtered index.  Basically, by explicitly declaring the start date of your filter, you're letting the Optimizer know that the filtered index can satisfy the request, regardless of the parameter value passed.  Thanks for the tip, Alex!  <img src='http://sqlfool.com/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' /> </p>
]]></content:encoded>
			<wfw:commentRss>http://sqlfool.com/2010/02/filtered-indexes-work-around/feed/</wfw:commentRss>
		<slash:comments>6</slash:comments>
		</item>
		<item>
		<title>Filtered Indexes: What You Need To Know</title>
		<link>http://sqlfool.com/2009/04/filtered-indexes-what-you-need-to-know/</link>
		<comments>http://sqlfool.com/2009/04/filtered-indexes-what-you-need-to-know/#comments</comments>
		<pubDate>Mon, 20 Apr 2009 17:32:56 +0000</pubDate>
		<dc:creator>Michelle Ufford</dc:creator>
				<category><![CDATA[Performance & Tuning]]></category>
		<category><![CDATA[SQL 2008]]></category>
		<category><![CDATA[Syndication]]></category>
		<category><![CDATA[2008]]></category>
		<category><![CDATA[filtered indexes]]></category>
		<category><![CDATA[indexes]]></category>

		<guid isPermaLink="false">http://sqlfool.com/?p=831</guid>
		<description><![CDATA[Filtered indexes are probably my favorite feature in 2008. That's saying a lot, since there are so many great new features to choose from. In this post, I want to explore a little about how filtered indexes work, how they can be applied, and some of the "gotchas" to be aware of. First, for those [...]]]></description>
			<content:encoded><![CDATA[<p>Filtered indexes are probably my favorite feature in 2008.  That's saying a lot, since there are so many great new features to choose from.  In this post, I want to explore a little about how filtered indexes work, how they can be applied, and some of the "gotchas" to be aware of.</p>
<p>First, for those of you who may not yet know about filtered indexes, allow me enlighten you.  In short, filtered indexes allow you to create an index on a subset of data using a filtering predicate.  Filters can only be applied to non-clustered indexes.  The general syntax of a filtered index is:</p>

<div class="wp_syntax"><div class="code"><pre class="tsql" style="font-family:monospace;"><span style="color: #0000FF;">Create</span> <span style="color: #0000FF;">NonClustered</span> <span style="color: #0000FF;">Index</span> <span style="color: #808080;">&#91;</span>index_name<span style="color: #808080;">&#93;</span>
<span style="color: #0000FF;">On</span> <span style="color: #808080;">&#91;</span>table_name<span style="color: #808080;">&#93;</span> <span style="color: #808080;">&#40;</span><span style="color: #808080;">&#91;</span>column_list<span style="color: #808080;">&#93;</span><span style="color: #808080;">&#41;</span>
Include <span style="color: #808080;">&#40;</span><span style="color: #808080;">&#91;</span>column_list<span style="color: #808080;">&#93;</span><span style="color: #808080;">&#41;</span>
<span style="color: #0000FF;">Where</span> <span style="color: #808080;">&#91;</span>filtered_criteria<span style="color: #808080;">&#93;</span>;</pre></div></div>

<p>For our purposes, we're going to be working with the Sales.SalesOrderDetail table in the AdventureWorks database.  Let's look at a specific example.  Suppose we have a query that regularly searches on the [SpecialOfferID] column.</p>

<div class="wp_syntax"><div class="code"><pre class="tsql" style="font-family:monospace;"><span style="color: #0000FF;">Select</span> SalesOrderID
    , <span style="color: #FF00FF;">Count</span><span style="color: #808080;">&#40;</span><span style="color: #808080;">*</span><span style="color: #808080;">&#41;</span> <span style="color: #0000FF;">As</span> <span style="color: #FF0000;">'CountOfLineItem'</span>
    , <span style="color: #FF00FF;">Sum</span><span style="color: #808080;">&#40;</span>LineTotal<span style="color: #808080;">&#41;</span> <span style="color: #0000FF;">As</span> <span style="color: #FF0000;">'SumOfLineTotal'</span>
<span style="color: #0000FF;">From</span> Sales.<span style="color: #202020;">SalesOrderDetail</span>
<span style="color: #0000FF;">Where</span> SpecialOfferID <span style="color: #808080;">&lt;&gt;</span> <span style="color: #000;">1</span>
<span style="color: #0000FF;">Group</span> <span style="color: #0000FF;">By</span> SalesOrderID;</pre></div></div>

<p>We notice that there's no covering index for this query by looking at the actual execution plan:</p>
<div id="attachment_842" class="wp-caption alignnone" style="width: 310px"><a href="http://sqlfool.com/wp-content/uploads/2009/04/queryplan1.jpg"><img src="http://sqlfool.com/wp-content/uploads/2009/04/queryplan1-300x133.jpg" alt="Query Plan - Clustered Scan" title="queryplan1" width="300" height="133" class="size-medium wp-image-842" /></a><p class="wp-caption-text">Query Plan - Clustered Scan</p></div>
<p>If this is a commonly executed query, then we'd probably want to toss an index on it.  Before we get started, let's take a look at what the distribution of values are on that column:</p>

<div class="wp_syntax"><div class="code"><pre class="tsql" style="font-family:monospace;"><span style="color: #0000FF;">Select</span> SpecialOfferID
    , <span style="color: #FF00FF;">Count</span><span style="color: #808080;">&#40;</span><span style="color: #808080;">*</span><span style="color: #808080;">&#41;</span> <span style="color: #0000FF;">As</span> <span style="color: #FF0000;">'rows'</span>
<span style="color: #0000FF;">From</span> Sales.<span style="color: #202020;">SalesOrderDetail</span>
<span style="color: #0000FF;">Group</span> <span style="color: #0000FF;">By</span> SpecialOfferID
<span style="color: #0000FF;">Order</span> <span style="color: #0000FF;">By</span> <span style="color: #FF00FF;">Count</span><span style="color: #808080;">&#40;</span><span style="color: #808080;">*</span><span style="color: #808080;">&#41;</span> <span style="color: #0000FF;">Desc</span>;</pre></div></div>

<p>Our distribution of values is:</p>

<div class="wp_syntax"><div class="code"><pre class="text" style="font-family:monospace;">SpecialOfferID rows
-------------- -----------
1              115884
2              3428
3              606
13             524
14             244
16             169
7              137
8              98
11             84
4              80
9              61
5              2</pre></div></div>

<p>As you can see, [SpecialOfferID] = 1 accounts for 96% of our values.  In 2005, we'd create an index that may look something like this:</p>

<div class="wp_syntax"><div class="code"><pre class="tsql" style="font-family:monospace;"><span style="color: #0000FF;">Create</span> <span style="color: #0000FF;">NonClustered</span> <span style="color: #0000FF;">Index</span> IX_Sales_SalesOrderDetail_SpecialOfferID
    <span style="color: #0000FF;">On</span> Sales.<span style="color: #202020;">SalesOrderDetail</span><span style="color: #808080;">&#40;</span>SpecialOfferID<span style="color: #808080;">&#41;</span>
    Include <span style="color: #808080;">&#40;</span>SalesOrderID, LineTotal<span style="color: #808080;">&#41;</span>;</pre></div></div>

<p>Now if we re-run our original query, this is what we see:</p>
<div id="attachment_836" class="wp-caption alignnone" style="width: 310px"><a href="http://sqlfool.com/wp-content/uploads/2009/04/queryplan2.jpg"><img src="http://sqlfool.com/wp-content/uploads/2009/04/queryplan2-300x160.jpg" alt="Indexed Query Plan" title="queryplan2" width="300" height="160" class="size-medium wp-image-836" /></a><p class="wp-caption-text">Indexed Query Plan</p></div>
<p>So we're now performing a non-clustered index seek instead of a clustered index scan.  Already this results in some pretty significant performance improvements.  To see this, we're going to use the <a href="http://msdn.microsoft.com/en-us/library/ms181714.aspx" target="_blank">INDEX query hint</a> to force an index scan.  We're also going to use the DBCC command <a href="http://msdn.microsoft.com/en-us/library/ms187762.aspx" target="_blank">DROPCLEANBUFFERS</a>, which will allow us to clear the buffer cache and better examine what's happening with our IO.</p>

<div class="wp_syntax"><div class="code"><pre class="tsql" style="font-family:monospace;"><span style="color: #0000FF;">Set</span> <span style="color: #0000FF;">Statistics</span> IO <span style="color: #0000FF;">On</span>;
&nbsp;
<span style="color: #0000FF;">DBCC</span> DropCleanBuffers;
&nbsp;
<span style="color: #0000FF;">Select</span> SalesOrderID
    , <span style="color: #FF00FF;">Count</span><span style="color: #808080;">&#40;</span><span style="color: #808080;">*</span><span style="color: #808080;">&#41;</span> <span style="color: #0000FF;">As</span> <span style="color: #FF0000;">'CountOfLineItem'</span>
    , <span style="color: #FF00FF;">Sum</span><span style="color: #808080;">&#40;</span>LineTotal<span style="color: #808080;">&#41;</span> <span style="color: #0000FF;">As</span> <span style="color: #FF0000;">'SumOfLineTotal'</span>
<span style="color: #0000FF;">From</span> Sales.<span style="color: #202020;">SalesOrderDetail</span> <span style="color: #0000FF;">With</span> 
    <span style="color: #808080;">&#40;</span><span style="color: #0000FF;">Index</span><span style="color: #808080;">&#40;</span>PK_SalesOrderDetail_SalesOrderID_SalesOrderDetailID<span style="color: #808080;">&#41;</span><span style="color: #808080;">&#41;</span>
<span style="color: #0000FF;">Where</span> SpecialOfferID <span style="color: #808080;">&lt;&gt;</span> <span style="color: #000;">1</span>
<span style="color: #0000FF;">Group</span> <span style="color: #0000FF;">By</span> SalesOrderID;
&nbsp;
<span style="color: #0000FF;">DBCC</span> DropCleanBuffers;
&nbsp;
<span style="color: #0000FF;">Select</span> SalesOrderID
    , <span style="color: #FF00FF;">Count</span><span style="color: #808080;">&#40;</span><span style="color: #808080;">*</span><span style="color: #808080;">&#41;</span> <span style="color: #0000FF;">As</span> <span style="color: #FF0000;">'CountOfLineItem'</span>
    , <span style="color: #FF00FF;">Sum</span><span style="color: #808080;">&#40;</span>LineTotal<span style="color: #808080;">&#41;</span> <span style="color: #0000FF;">As</span> <span style="color: #FF0000;">'SumOfLineTotal'</span>
<span style="color: #0000FF;">From</span> Sales.<span style="color: #202020;">SalesOrderDetail</span>
<span style="color: #0000FF;">Where</span> SpecialOfferID <span style="color: #808080;">&lt;&gt;</span> <span style="color: #000;">1</span>
<span style="color: #0000FF;">Group</span> <span style="color: #0000FF;">By</span> SalesOrderID;
&nbsp;
<span style="color: #0000FF;">Set</span> <span style="color: #0000FF;">Statistics</span> IO <span style="color: #0000FF;">Off</span>;</pre></div></div>


<div class="wp_syntax"><div class="code"><pre class="text" style="font-family:monospace;">Clustered Index Scan:
Table 'SalesOrderDetail'. Scan count 1, logical reads 1240, physical reads 17, read-ahead reads 1242...
&nbsp;
NonClustered Index Seek:
Table 'SalesOrderDetail'. Scan count 2, logical reads 30, physical reads 4, read-ahead reads 480...</pre></div></div>

<p>As you can see, the non-clustered (NC) index seek performs quite a bit better.  Now let's create a filtered index and explore what happens:</p>

<div class="wp_syntax"><div class="code"><pre class="tsql" style="font-family:monospace;"><span style="color: #0000FF;">Create</span> <span style="color: #0000FF;">NonClustered</span> <span style="color: #0000FF;">Index</span> FIX_Sales_SalesOrderDetail_SpecialOfferID_Filtered
    <span style="color: #0000FF;">On</span> Sales.<span style="color: #202020;">SalesOrderDetail</span><span style="color: #808080;">&#40;</span>SalesOrderID<span style="color: #808080;">&#41;</span>
    Include <span style="color: #808080;">&#40;</span>LineTotal<span style="color: #808080;">&#41;</span>
    <span style="color: #0000FF;">Where</span> SpecialOfferID <span style="color: #808080;">&lt;&gt;</span> <span style="color: #000;">1</span>;</pre></div></div>

<p>First, let's look at the pages consumed by each index:</p>

<div class="wp_syntax"><div class="code"><pre class="tsql" style="font-family:monospace;"><span style="color: #0000FF;">SELECT</span> i.<span style="color: #202020;">name</span>, ddips.<span style="color: #202020;">index_depth</span>, ddips.<span style="color: #202020;">index_level</span>
    , ddips.<span style="color: #202020;">page_count</span>, ddips.<span style="color: #202020;">record_count</span>
<span style="color: #0000FF;">FROM</span> sys.<span style="color: #202020;">indexes</span> <span style="color: #0000FF;">AS</span> i
Join sys.<span style="color: #202020;">dm_db_index_physical_stats</span><span style="color: #808080;">&#40;</span><span style="color: #FF00FF;">DB_ID</span><span style="color: #808080;">&#40;</span><span style="color: #808080;">&#41;</span>, 
    <span style="color: #FF00FF;">OBJECT_ID</span><span style="color: #808080;">&#40;</span>N<span style="color: #FF0000;">'Sales.SalesOrderDetail'</span><span style="color: #808080;">&#41;</span>, Null, Null, N<span style="color: #FF0000;">'Detailed'</span><span style="color: #808080;">&#41;</span> <span style="color: #0000FF;">AS</span> ddips
    <span style="color: #0000FF;">ON</span> i.<span style="color: #FF00FF;">OBJECT_ID</span> <span style="color: #808080;">=</span> ddips.<span style="color: #FF00FF;">OBJECT_ID</span>
    And i.<span style="color: #202020;">index_id</span> <span style="color: #808080;">=</span> ddips.<span style="color: #202020;">index_id</span>
<span style="color: #0000FF;">WHERE</span> i.<span style="color: #202020;">name</span> In <span style="color: #808080;">&#40;</span><span style="color: #FF0000;">'IX_Sales_SalesOrderDetail_SpecialOfferID'</span>
    , <span style="color: #FF0000;">'FIX_Sales_SalesOrderDetail_SpecialOfferID_Filtered'</span>
    , <span style="color: #FF0000;">'PK_SalesOrderDetail_SalesOrderID_SalesOrderDetailID'</span><span style="color: #808080;">&#41;</span>
    <span style="color: #808080;">AND</span> ddips.<span style="color: #202020;">index_level</span> <span style="color: #808080;">=</span> <span style="color: #000;">0</span>;</pre></div></div>


<div class="wp_syntax"><div class="code"><pre class="text" style="font-family:monospace;">name                                                       index_depth index_level page_count  record_count
---------------------------------------------------------- ----------- ----------- ----------- --------------------
PK_SalesOrderDetail_SalesOrderID_SalesOrderDetailID        3           0           1234        121317
IX_Sales_SalesOrderDetail_SpecialOfferID                   3           0           480         121317
FIX_Sales_SalesOrderDetail_SpecialOfferID_Filtered         2           0           19          5433</pre></div></div>

<p>If you scroll over, you'll see that the clustered index consumes the most pages, naturally.  The non-filtered NC index consumes less pages than the clustered index because it's narrower; however, it still consumes more pages than the filtered index because it's storing every data row.  The filtered index, with only 5433 rows stored, is by far our smallest index, consuming 96% less space than our non-filtered NC index.  </p>
<p>Because we're using less space to store this index, we should also see an equivalent performance boost.  Let's verify that this is the case:</p>

<div class="wp_syntax"><div class="code"><pre class="tsql" style="font-family:monospace;"><span style="color: #0000FF;">Set</span> <span style="color: #0000FF;">Statistics</span> IO <span style="color: #0000FF;">On</span>;
&nbsp;
<span style="color: #0000FF;">DBCC</span> DropCleanBuffers;
&nbsp;
<span style="color: #0000FF;">Select</span> SalesOrderID
    , <span style="color: #FF00FF;">Count</span><span style="color: #808080;">&#40;</span><span style="color: #808080;">*</span><span style="color: #808080;">&#41;</span> <span style="color: #0000FF;">As</span> <span style="color: #FF0000;">'CountOfLineItem'</span>
    , <span style="color: #FF00FF;">Sum</span><span style="color: #808080;">&#40;</span>LineTotal<span style="color: #808080;">&#41;</span> <span style="color: #0000FF;">As</span> <span style="color: #FF0000;">'SumOfLineTotal'</span>
<span style="color: #0000FF;">From</span> Sales.<span style="color: #202020;">SalesOrderDetail</span> <span style="color: #0000FF;">With</span> <span style="color: #808080;">&#40;</span><span style="color: #0000FF;">Index</span><span style="color: #808080;">&#40;</span>IX_Sales_SalesOrderDetail_SpecialOfferID<span style="color: #808080;">&#41;</span><span style="color: #808080;">&#41;</span>
<span style="color: #0000FF;">Where</span> SpecialOfferID <span style="color: #808080;">&lt;&gt;</span> <span style="color: #000;">1</span>
<span style="color: #0000FF;">Group</span> <span style="color: #0000FF;">By</span> SalesOrderID;
&nbsp;
<span style="color: #0000FF;">DBCC</span> DropCleanBuffers;
&nbsp;
<span style="color: #0000FF;">Select</span> SalesOrderID
    , <span style="color: #FF00FF;">Count</span><span style="color: #808080;">&#40;</span><span style="color: #808080;">*</span><span style="color: #808080;">&#41;</span> <span style="color: #0000FF;">As</span> <span style="color: #FF0000;">'CountOfLineItem'</span>
    , <span style="color: #FF00FF;">Sum</span><span style="color: #808080;">&#40;</span>LineTotal<span style="color: #808080;">&#41;</span> <span style="color: #0000FF;">As</span> <span style="color: #FF0000;">'SumOfLineTotal'</span>
<span style="color: #0000FF;">From</span> Sales.<span style="color: #202020;">SalesOrderDetail</span>
<span style="color: #0000FF;">Where</span> SpecialOfferID <span style="color: #808080;">&lt;&gt;</span> <span style="color: #000;">1</span>
<span style="color: #0000FF;">Group</span> <span style="color: #0000FF;">By</span> SalesOrderID;
&nbsp;
<span style="color: #0000FF;">Set</span> <span style="color: #0000FF;">Statistics</span> IO <span style="color: #0000FF;">Off</span>;</pre></div></div>


<div class="wp_syntax"><div class="code"><pre class="text" style="font-family:monospace;">NonClustered Index Seek:
Table 'SalesOrderDetail'. Scan count 2, logical reads 30, physical reads 4, read-ahead reads 480
&nbsp;
Filtered Index Scan:
Table 'SalesOrderDetail'. Scan count 1, logical reads 24, physical reads 2, read-ahead reads 22</pre></div></div>

<div id="attachment_846" class="wp-caption alignnone" style="width: 310px"><a href="http://sqlfool.com/wp-content/uploads/2009/04/queryplan3.jpg"><img src="http://sqlfool.com/wp-content/uploads/2009/04/queryplan3-300x273.jpg" alt="Filtered Query Plan" title="queryplan3" width="300" height="273" class="size-medium wp-image-846" /></a><p class="wp-caption-text">Filtered Query Plan</p></div>
<p>As expected, we get the best results with our filtered index scan.  </p>
<p>You'll notice that I did *not* create the index on the [SpecialOfferID] column like I did in [IX_Sales_SalesOrderDetail_SpecialOfferID].  This is because my query doesn't care what my [SpecialOfferID] value is, just as long as it's not equal to 1.  My non-filtered NC index was created on [SpecialOfferID] because it needed to navigate the B-TREE to find the records where [SpecialOfferID] <> 1.  With my filtered index, the query optimizer knows that all of my records already meet the criteria, so doesn't need to navigate through the index to find the matching results.  </p>
<p>We could choose to include the [SpecialOfferID] data in our filtered index, but we'd most likely want to make it an included column rather than part of the index key.  In fact, it's important to note that, if I don't add [SpecialOfferID] as an included column and I want to return it in the results, i.e.</p>

<div class="wp_syntax"><div class="code"><pre class="tsql" style="font-family:monospace;"><span style="color: #0000FF;">Select</span> SalesOrderID
    , SpecialOfferID
    , <span style="color: #FF00FF;">Count</span><span style="color: #808080;">&#40;</span><span style="color: #808080;">*</span><span style="color: #808080;">&#41;</span> <span style="color: #0000FF;">As</span> <span style="color: #FF0000;">'CountOfLineItem'</span>
    , <span style="color: #FF00FF;">Sum</span><span style="color: #808080;">&#40;</span>LineTotal<span style="color: #808080;">&#41;</span> <span style="color: #0000FF;">As</span> <span style="color: #FF0000;">'SumOfLineTotal'</span>
<span style="color: #0000FF;">From</span> Sales.<span style="color: #202020;">SalesOrderDetail</span>
<span style="color: #0000FF;">Where</span> SpecialOfferID <span style="color: #808080;">&lt;&gt;</span> <span style="color: #000;">1</span>
<span style="color: #0000FF;">Group</span> <span style="color: #0000FF;">By</span> SalesOrderID
    , SpecialOfferID;</pre></div></div>

<p><strong>my filtered index will not be used</strong> and I will instead scan on the clustered index once more (assuming [IX_Sales_SalesOrderDetail_SpecialOfferID] does not exist).  This is because the <strong>filtering criteria is not included anywhere on the actual index page</strong>.  This is actually good news, in my opinion, since it allows you to create even leaner indexes.  And like I already mentioned, if you do need the data returned, you can always add the filtering criteria as included columns.</p>
<p>What if you're trying to find out whether or not an index is filtered, and what it's filtered on?  The sys.indexes catalog view has been updated in 2008 to include this information:</p>

<div class="wp_syntax"><div class="code"><pre class="tsql" style="font-family:monospace;"><span style="color: #0000FF;">Select</span> name, has_filter, filter_definition
<span style="color: #0000FF;">From</span> sys.<span style="color: #202020;">indexes</span> 
<span style="color: #0000FF;">Where</span> name In <span style="color: #808080;">&#40;</span><span style="color: #FF0000;">'IX_Sales_SalesOrderDetail_SpecialOfferID'</span>
    , <span style="color: #FF0000;">'FIX_Sales_SalesOrderDetail_SpecialOfferID_Filtered'</span>
    , <span style="color: #FF0000;">'PK_SalesOrderDetail_SalesOrderID_SalesOrderDetailID'</span><span style="color: #808080;">&#41;</span>;</pre></div></div>


<div class="wp_syntax"><div class="code"><pre class="text" style="font-family:monospace;">name                                                   has_filter filter_definition
------------------------------------------------------ ---------- -------------------------
FIX_Sales_SalesOrderDetail_SpecialOfferID_Filtered     1          ([SpecialOfferID]&lt;&gt;(1))
IX_Sales_SalesOrderDetail_SpecialOfferID               0          NULL
PK_SalesOrderDetail_SalesOrderID_SalesOrderDetailID    0          NULL</pre></div></div>

<p>I personally recommend Kimberly Tripp's system stored proc, <a href="http://www.sqlskills.com/blogs/kimberly/post/sp_helpindex2-to-show-included-columns-(20052b)-and-filtered-indexes-(2008)-which-are-not-shown-by-sp_helpindex.aspx" target="_blank">sp_helpindex2</a>.  It returns a lot of good information about your indexes, such as included columns and filtering criteria.</p>
<p>That's all I have for today.  Hopefully, you now understand how powerful filtered indexes can be.  When used properly, filtered indexes can use less space, consume less IO, and improve overall query performance.  </p>
]]></content:encoded>
			<wfw:commentRss>http://sqlfool.com/2009/04/filtered-indexes-what-you-need-to-know/feed/</wfw:commentRss>
		<slash:comments>5</slash:comments>
		</item>
	</channel>
</rss>

