{"id":411,"date":"2008-02-25T21:35:45","date_gmt":"2008-02-25T21:35:45","guid":{"rendered":"\/blogs\/conor\/post\/Filtered-Indexes-Make-Me-Drool.aspx"},"modified":"2008-02-25T21:35:45","modified_gmt":"2008-02-25T21:35:45","slug":"filtered-indexes-make-me-drool","status":"publish","type":"post","link":"https:\/\/www.sqlskills.com\/blogs\/conor\/filtered-indexes-make-me-drool\/","title":{"rendered":"Filtered Indexes Make Me Drool&#8230;"},"content":{"rendered":"<p>(I got CTP 6 running on another machine, so I&#8217;m working except for parallel plans now since it&#8217;s only a single-proc.&nbsp; You guys will have to wait on that operator of the day article for now \ud83d\ude42<\/p>\n<p>Kudos to my former teammembers for getting filtered indexes into CTP6.&nbsp; It&#8217;s pretty darn neat, and I&#8217;ll show you a few tidbits that are interesting.<\/p>\n<p>So, filtered indexes are not really a new &#8220;feature&#8221; in some sense.&nbsp; You can do everything in a filtered index with an indexed view.&nbsp; Of course, indexed view matching is only supported in the Enterprise edition of the software, so perhaps not everyone has seen those benefits.&nbsp; <\/p>\n<p>Indexed views are a tricky feature &#8211; generalized tree pattern matching is hard (read: CPU expensive), and if you&#8217;ve looked in the BOL the list of restrictions is so long that reminds me of filling out tax forms.&nbsp; However, the other side effect that occurs when you have indexed views is that the optimizer has to go through additional phases of its search in order to apply them.&nbsp; The optimizer has a couple of buckets of rules it can run, and most plans find a nice plan in the first or second round of rules.&nbsp; Generalized indexed view matching is restricted to the last set of buckets, which usually means that there are a lot more rules that have to run before that view can be matched.&nbsp; The bottom line is that the compilation cost isn&#8217;t bad for a single query (usually), but it&#8217;s the sort of thing that can make the difference between an application that can be used in UI response-time requirements or not.<\/p>\n<p>Enter the filtered index.&nbsp; This is a recognition that there are a lot of indexed views that don&#8217;t need tons and tons of fancy equations, joins, etc.&nbsp; These are single-tabled views that have simple predicates.&nbsp; Once you get into this ballpark, you can bolt this on to the super-efficient index matching code and you can enable a whole new class of application from what you could build previously.&nbsp; This is why I am excited about this feature.<\/p>\n<p>I haven&#8217;t looked to see where this feature will fall into the SKU matrix yet, and I&#8217;m sure that they&#8217;re still pondering that very question.&nbsp; However, you guys should play with this on CTP6 &#8211; it&#8217;s nifty!<\/p>\n<p>So, first things first.&nbsp; Let&#8217;s build one of these guys and see if we can match it:<\/p>\n<pre><span style=\"color: Black; background-color: transparent; font-family: Courier New; font-size: 11px;\"><span style=\"color: Blue; background-color: transparent; font-family: Courier New; font-size: 11px;\">create<\/span> <span style=\"color: Blue; background-color: transparent; font-family: Courier New; font-size: 11px;\">database<\/span> t1\r\n<span style=\"color: Blue; background-color: transparent; font-family: Courier New; font-size: 11px;\">use<\/span> t1\r\n<span style=\"color: Blue; background-color: transparent; font-family: Courier New; font-size: 11px;\">drop<\/span> <span style=\"color: Blue; background-color: transparent; font-family: Courier New; font-size: 11px;\">table<\/span> t1\r\n<span style=\"color: Blue; background-color: transparent; font-family: Courier New; font-size: 11px;\">create<\/span> <span style=\"color: Blue; background-color: transparent; font-family: Courier New; font-size: 11px;\">table<\/span> t1(col1 <span style=\"color: Blue; background-color: transparent; font-family: Courier New; font-size: 11px;\">int<\/span>, col2 <span style=\"color: Fuchsia; background-color: transparent; font-family: Courier New; font-size: 11px;\">nchar<\/span>(2000), col3 time)\r\n\r\n<span style=\"color: Blue; background-color: transparent; font-family: Courier New; font-size: 11px;\">create<\/span> <span style=\"color: Blue; background-color: transparent; font-family: Courier New; font-size: 11px;\">index<\/span> i1 <span style=\"color: Blue; background-color: transparent; font-family: Courier New; font-size: 11px;\">on<\/span> t1(col1) <span style=\"color: Blue; background-color: transparent; font-family: Courier New; font-size: 11px;\">where<\/span> col1 &gt; 5 <span style=\"color: Silver; background-color: transparent; font-family: Courier New; font-size: 11px;\">and<\/span> col1 &lt; 20\r\n<span style=\"color: Blue; background-color: transparent; font-family: Courier New; font-size: 11px;\">declare<\/span> @p <span style=\"color: Blue; background-color: transparent; font-family: Courier New; font-size: 11px;\">int<\/span> \r\n<span style=\"color: Blue; background-color: transparent; font-family: Courier New; font-size: 11px;\">set<\/span> @p =0\r\n<span style=\"color: Blue; background-color: transparent; font-family: Courier New; font-size: 11px;\">while<\/span> @p &lt; 20000\r\n<span style=\"color: Blue; background-color: transparent; font-family: Courier New; font-size: 11px;\">begin<\/span>\r\n<span style=\"color: Blue; background-color: transparent; font-family: Courier New; font-size: 11px;\">insert<\/span> <span style=\"color: Blue; background-color: transparent; font-family: Courier New; font-size: 11px;\">into<\/span> t1(col1) <span style=\"color: Blue; background-color: transparent; font-family: Courier New; font-size: 11px;\">values<\/span> (<span style=\"color: Fuchsia; background-color: transparent; font-family: Courier New; font-size: 11px;\">rand<\/span>()*10000)\r\n<span style=\"color: Blue; background-color: transparent; font-family: Courier New; font-size: 11px;\">set<\/span> @p=@p+1\r\n<span style=\"color: Blue; background-color: transparent; font-family: Courier New; font-size: 11px;\">end<\/span>\r\n<\/span><\/pre>\n<p>ok so I&#8217;ve filled this table up with a lot of useless data and created a nice little filtered index.<\/p>\n<pre><span style=\"color: Black; background-color: transparent; font-family: Courier New; font-size: 11px;\"><span style=\"color: Blue; background-color: transparent; font-family: Courier New; font-size: 11px;\">select<\/span> * <span style=\"color: Blue; background-color: transparent; font-family: Courier New; font-size: 11px;\">from<\/span> t1  <span style=\"color: Blue; background-color: transparent; font-family: Courier New; font-size: 11px;\">where<\/span> col1 &gt; 5 <span style=\"color: Silver; background-color: transparent; font-family: Courier New; font-size: 11px;\">and<\/span> col1 &lt; 20<\/span><\/pre>\n<p>&nbsp; |&#8211;Nested Loops(Inner Join, OUTER REFERENCES:([Bmk1000], [Expr1007]) OPTIMIZED WITH UNORDERED PREFETCH)<br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; |&#8211;Compute Scalar(DEFINE:([Expr1006]=BmkToPage([Bmk1000])))<br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; |&nbsp;&nbsp;&nbsp; |&#8211;Index Scan(OBJECT:([t1].[dbo].[t1].[i1]))<br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; |&#8211;RID Lookup(OBJECT:([t1].[dbo].[t1]), SEEK:([Bmk1000]=[Bmk1000]) LOOKUP ORDERED FORWARD)<\/p>\n<pre><span style=\"color: Black; background-color: transparent; font-family: Courier New; font-size: 11px;\"><span style=\"color: Blue; background-color: transparent; font-family: Courier New; font-size: 11px;\">select<\/span> * <span style=\"color: Blue; background-color: transparent; font-family: Courier New; font-size: 11px;\">from<\/span> t1  <span style=\"color: Blue; background-color: transparent; font-family: Courier New; font-size: 11px;\">where<\/span> col1 &gt; 5 <span style=\"color: Silver; background-color: transparent; font-family: Courier New; font-size: 11px;\">and<\/span> col1 &lt; 10<\/span><\/pre>\n<p>&nbsp; |&#8211;Nested Loops(Inner Join, OUTER REFERENCES:([Bmk1000]) OPTIMIZED)<br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; |&#8211;Compute Scalar(DEFINE:([Expr1006]=BmkToPage([Bmk1000])))<br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; |&nbsp;&nbsp;&nbsp; |&#8211;Index Seek(OBJECT:([t1].[dbo].[t1].[i1]), SEEK:([t1].[dbo].[t1].[col1] &lt; (10)) ORDERED FORWARD)<br \/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; |&#8211;RID Lookup(OBJECT:([t1].[dbo].[t1]), SEEK:([Bmk1000]=[Bmk1000]) LOOKUP ORDERED FORWARD)<\/p>\n<p>so in the first example I&#8217;ve created a query against the table that directly matches the index condition.&nbsp; It matches and even generates an index scan (slightly faster since it doesn&#8217;t have to navigate down the b-tree to start returning rows).<\/p>\n<p>The second example is interesting because is a proper subset of the index.&nbsp; The indexed view matching code can do subsumption as well.&nbsp; It generates a seek in this case and returns only part of the filtered index.<\/p>\n<p>Both cases do RID lookups back to the heap since I did SELECT * and the index is not &#8220;covering&#8221;.<\/p>\n<p>The other data point I&#8217;ll leave you with this evening is that the showplan_xml has something interesting in it:<\/p>\n<p><span class=\"t\">StmtSimple<\/span><span class=\"t\"> StatementText<\/span><span class=\"m\">=&#8221;<\/span><b>select * from t1 where col1 &gt; 5 and col1 &lt; 10<\/b><span class=\"m\">&#8220;<\/span><span class=\"t\"> StatementId<\/span><span class=\"m\">=&#8221;<\/span><b>1<\/b><span class=\"m\">&#8220;<\/span><span class=\"t\"><br \/>\nStatementCompId<\/span><span class=\"m\">=&#8221;<\/span><b>1<\/b><span class=\"m\">&#8220;<\/span><span class=\"t\"> StatementType<\/span><span class=\"m\">=&#8221;<\/span><b>SELECT<\/b><span class=\"m\">&#8220;<\/span><span class=\"t\"> StatementSubTreeCost<\/span><span class=\"m\">=&#8221;<\/span><b>0.0474183<\/b><span class=\"m\">&#8220;<\/span><span class=\"t\"><br \/>\nStatementEstRows<\/span><span class=\"m\">=&#8221;<\/span><b>7<\/b><span class=\"m\">&#8220;<\/span><span class=\"t\"> StatementOptmLevel<\/span><span class=\"m\">=&#8221;<\/span><b>FULL<\/b><span class=\"m\">&#8220;<\/span><span class=\"t\"><br \/>\nStatementOptmEarlyAbortReason<\/span><span class=\"m\">=&#8221;<\/span><b>GoodEnoughPlanFound<\/b><span class=\"m\">&#8220;<\/span><span class=\"t\"><br \/>\nParameterizedText<\/span><span class=\"m\">=&#8221;<\/span><b>(@1 tinyint,@2 tinyint)SELECT<br \/>\n* FROM [t1] WHERE [col1]&gt;@1 AND [col1]&lt;@2<\/b><span class=\"m\">&#8220;<\/span><span class=\"m\">&gt;<\/span><\/p>\n<p>Well, there are two interesting things.&nbsp; First, StatementOptmLevel=FULL means that we didn&#8217;t get a trivial plan.&nbsp; There is a parameterized text field, but I don&#8217;t think that is being used here.&nbsp; these two queries are showing up as different plans in the plan cache (and obviously they have different plans). So, absent forced autoparam, I don&#8217;t think that trivial plan is working on these guys.&nbsp; This makes sense since there is a cost-based plan choice.<\/p>\n<p>That&#8217;s it for me tonight guys.&nbsp; Happy Querying!<\/p>\n<p>Conor<\/p>\n<p>PS: Thanks for all of the comments and posts trying to help me get back to a working state.&nbsp; I&#8217;ve tried many of the suggestions without luck, but I&#8217;m going to try playing with the orca tool, perhaps tomorrow or so, to see if I can get my main machine back up and working.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>(I got CTP 6 running on another machine, so I&#8217;m working except for parallel plans now since it&#8217;s only a single-proc.&nbsp; You guys will have to wait on that operator of the day article for now \ud83d\ude42 Kudos to my former teammembers for getting filtered indexes into CTP6.&nbsp; It&#8217;s pretty darn neat, and I&#8217;ll show [&hellip;]<\/p>\n","protected":false},"author":6,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[1],"tags":[],"class_list":["post-411","post","type-post","status-publish","format-standard","hentry","category-uncategorized"],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v21.9.1 - https:\/\/yoast.com\/wordpress\/plugins\/seo\/ -->\n<title>Filtered Indexes Make Me Drool... - Conor Cunningham<\/title>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/www.sqlskills.com\/blogs\/conor\/filtered-indexes-make-me-drool\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Filtered Indexes Make Me Drool... - Conor Cunningham\" \/>\n<meta property=\"og:description\" content=\"(I got CTP 6 running on another machine, so I&#8217;m working except for parallel plans now since it&#8217;s only a single-proc.&nbsp; You guys will have to wait on that operator of the day article for now \ud83d\ude42 Kudos to my former teammembers for getting filtered indexes into CTP6.&nbsp; It&#8217;s pretty darn neat, and I&#8217;ll show [&hellip;]\" \/>\n<meta property=\"og:url\" content=\"https:\/\/www.sqlskills.com\/blogs\/conor\/filtered-indexes-make-me-drool\/\" \/>\n<meta property=\"og:site_name\" content=\"Conor Cunningham\" \/>\n<meta property=\"article:published_time\" content=\"2008-02-25T21:35:45+00:00\" \/>\n<meta name=\"author\" content=\"Conor Cunningham\" \/>\n<meta name=\"twitter:label1\" content=\"Written by\" \/>\n\t<meta name=\"twitter:data1\" content=\"Conor Cunningham\" \/>\n\t<meta name=\"twitter:label2\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data2\" content=\"5 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"WebPage\",\"@id\":\"https:\/\/www.sqlskills.com\/blogs\/conor\/filtered-indexes-make-me-drool\/\",\"url\":\"https:\/\/www.sqlskills.com\/blogs\/conor\/filtered-indexes-make-me-drool\/\",\"name\":\"Filtered Indexes Make Me Drool... - Conor Cunningham\",\"isPartOf\":{\"@id\":\"https:\/\/www.sqlskills.com\/blogs\/conor\/#website\"},\"datePublished\":\"2008-02-25T21:35:45+00:00\",\"dateModified\":\"2008-02-25T21:35:45+00:00\",\"author\":{\"@id\":\"https:\/\/www.sqlskills.com\/blogs\/conor\/#\/schema\/person\/f9106e03423de6b5157295891b8c3ae3\"},\"breadcrumb\":{\"@id\":\"https:\/\/www.sqlskills.com\/blogs\/conor\/filtered-indexes-make-me-drool\/#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/www.sqlskills.com\/blogs\/conor\/filtered-indexes-make-me-drool\/\"]}]},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/www.sqlskills.com\/blogs\/conor\/filtered-indexes-make-me-drool\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\/\/www.sqlskills.com\/blogs\/conor\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Filtered Indexes Make Me Drool&#8230;\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\/\/www.sqlskills.com\/blogs\/conor\/#website\",\"url\":\"https:\/\/www.sqlskills.com\/blogs\/conor\/\",\"name\":\"Conor Cunningham\",\"description\":\"\",\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\/\/www.sqlskills.com\/blogs\/conor\/?s={search_term_string}\"},\"query-input\":\"required name=search_term_string\"}],\"inLanguage\":\"en-US\"},{\"@type\":\"Person\",\"@id\":\"https:\/\/www.sqlskills.com\/blogs\/conor\/#\/schema\/person\/f9106e03423de6b5157295891b8c3ae3\",\"name\":\"Conor Cunningham\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/www.sqlskills.com\/blogs\/conor\/#\/schema\/person\/image\/\",\"url\":\"https:\/\/secure.gravatar.com\/avatar\/d9c37eff231ec89c1b244347d966860875eea8b55b366911d2694e8cd9913e57?s=96&d=mm&r=g\",\"contentUrl\":\"https:\/\/secure.gravatar.com\/avatar\/d9c37eff231ec89c1b244347d966860875eea8b55b366911d2694e8cd9913e57?s=96&d=mm&r=g\",\"caption\":\"Conor Cunningham\"},\"url\":\"https:\/\/www.sqlskills.com\/blogs\/conor\/author\/conor\/\"}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"Filtered Indexes Make Me Drool... - Conor Cunningham","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/www.sqlskills.com\/blogs\/conor\/filtered-indexes-make-me-drool\/","og_locale":"en_US","og_type":"article","og_title":"Filtered Indexes Make Me Drool... - Conor Cunningham","og_description":"(I got CTP 6 running on another machine, so I&#8217;m working except for parallel plans now since it&#8217;s only a single-proc.&nbsp; You guys will have to wait on that operator of the day article for now \ud83d\ude42 Kudos to my former teammembers for getting filtered indexes into CTP6.&nbsp; It&#8217;s pretty darn neat, and I&#8217;ll show [&hellip;]","og_url":"https:\/\/www.sqlskills.com\/blogs\/conor\/filtered-indexes-make-me-drool\/","og_site_name":"Conor Cunningham","article_published_time":"2008-02-25T21:35:45+00:00","author":"Conor Cunningham","twitter_misc":{"Written by":"Conor Cunningham","Est. reading time":"5 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"WebPage","@id":"https:\/\/www.sqlskills.com\/blogs\/conor\/filtered-indexes-make-me-drool\/","url":"https:\/\/www.sqlskills.com\/blogs\/conor\/filtered-indexes-make-me-drool\/","name":"Filtered Indexes Make Me Drool... - Conor Cunningham","isPartOf":{"@id":"https:\/\/www.sqlskills.com\/blogs\/conor\/#website"},"datePublished":"2008-02-25T21:35:45+00:00","dateModified":"2008-02-25T21:35:45+00:00","author":{"@id":"https:\/\/www.sqlskills.com\/blogs\/conor\/#\/schema\/person\/f9106e03423de6b5157295891b8c3ae3"},"breadcrumb":{"@id":"https:\/\/www.sqlskills.com\/blogs\/conor\/filtered-indexes-make-me-drool\/#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/www.sqlskills.com\/blogs\/conor\/filtered-indexes-make-me-drool\/"]}]},{"@type":"BreadcrumbList","@id":"https:\/\/www.sqlskills.com\/blogs\/conor\/filtered-indexes-make-me-drool\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/www.sqlskills.com\/blogs\/conor\/"},{"@type":"ListItem","position":2,"name":"Filtered Indexes Make Me Drool&#8230;"}]},{"@type":"WebSite","@id":"https:\/\/www.sqlskills.com\/blogs\/conor\/#website","url":"https:\/\/www.sqlskills.com\/blogs\/conor\/","name":"Conor Cunningham","description":"","potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/www.sqlskills.com\/blogs\/conor\/?s={search_term_string}"},"query-input":"required name=search_term_string"}],"inLanguage":"en-US"},{"@type":"Person","@id":"https:\/\/www.sqlskills.com\/blogs\/conor\/#\/schema\/person\/f9106e03423de6b5157295891b8c3ae3","name":"Conor Cunningham","image":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/www.sqlskills.com\/blogs\/conor\/#\/schema\/person\/image\/","url":"https:\/\/secure.gravatar.com\/avatar\/d9c37eff231ec89c1b244347d966860875eea8b55b366911d2694e8cd9913e57?s=96&d=mm&r=g","contentUrl":"https:\/\/secure.gravatar.com\/avatar\/d9c37eff231ec89c1b244347d966860875eea8b55b366911d2694e8cd9913e57?s=96&d=mm&r=g","caption":"Conor Cunningham"},"url":"https:\/\/www.sqlskills.com\/blogs\/conor\/author\/conor\/"}]}},"_links":{"self":[{"href":"https:\/\/www.sqlskills.com\/blogs\/conor\/wp-json\/wp\/v2\/posts\/411","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.sqlskills.com\/blogs\/conor\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.sqlskills.com\/blogs\/conor\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.sqlskills.com\/blogs\/conor\/wp-json\/wp\/v2\/users\/6"}],"replies":[{"embeddable":true,"href":"https:\/\/www.sqlskills.com\/blogs\/conor\/wp-json\/wp\/v2\/comments?post=411"}],"version-history":[{"count":0,"href":"https:\/\/www.sqlskills.com\/blogs\/conor\/wp-json\/wp\/v2\/posts\/411\/revisions"}],"wp:attachment":[{"href":"https:\/\/www.sqlskills.com\/blogs\/conor\/wp-json\/wp\/v2\/media?parent=411"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.sqlskills.com\/blogs\/conor\/wp-json\/wp\/v2\/categories?post=411"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.sqlskills.com\/blogs\/conor\/wp-json\/wp\/v2\/tags?post=411"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}