{"id":402,"date":"2008-03-23T19:35:01","date_gmt":"2008-03-23T19:35:01","guid":{"rendered":"\/blogs\/conor\/post\/Hunting-for-SQL-2008-SPARSE-Column-Easter-Eggs.aspx"},"modified":"2008-03-23T19:35:01","modified_gmt":"2008-03-23T19:35:01","slug":"hunting-for-sql-2008-sparse-column-easter-eggs","status":"publish","type":"post","link":"https:\/\/www.sqlskills.com\/blogs\/conor\/hunting-for-sql-2008-sparse-column-easter-eggs\/","title":{"rendered":"Hunting for SQL 2008 SPARSE Column Easter Eggs"},"content":{"rendered":"<p>So I was playing today with the sparse column implementation in the latest SQL 2008 CTP, and I was specifically  looking to see how this thing would work with the query processor.&nbsp; I wanted to know how much information is exposed for the QP to make smart decisions when this functionality is used in complex queries with cost-based plan choices.&nbsp; If the QP doesn&#8217;t have the information, then sometimes the query plans will be sub-optimal because, well, garbage-in garbage-out.&nbsp; While the SQL Server QP does a tremendous job at making complex plan choices compared to some of the other commercial alternatives, there are still limits on what the Optimizer can model in a reasonable amount of time.&nbsp; As such, there are seams where the product tends to not work as well as one would hope.&nbsp; This will always be true.&nbsp; While I suppose that will also keep me employable, it is useful to understand those limits because it will help you know where to look or, if it&#8217;s really hard, when to ask for help.<\/p>\n<p>The SQL Server QP knows a couple of things about the data stored in a table in the storage engine:<br \/>1. How many physical pages it uses<br \/>2. How many rows it has in it (approximately)<br \/>3. Single-column statistics over a sample of the data<br \/>4. A basic notion of column interdependance to help in estimating queries with multiple scalar predicates.<\/p>\n<p>From 1 and 2 it can derive the average row width.&nbsp; That&#8217;s useful for determining things like &#8220;how big will my sort be&#8221; if the query needs to sort.&nbsp; That&#8217;s a good thing &#8211; it leads to reasonable estimates for many choices in the QP.<\/p>\n<p>So let&#8217;s add sparse columns into the mix.&nbsp; Sparse columns are useful for data with lots of NULLs.&nbsp; Often this is a result of a non-traditional third-normal form database problem or, perhaps someone who is not a database person not really trying to make something into a database problem early enough in its lifecycle.&nbsp; The point is that commercial database systems have a sweet spot around handling data sets with known (and small) sets of columns that can be stored in tables.&nbsp; There is a TON of expressiveness available in query processors that manipulate this data because this format of data is better supported than other formats.<\/p>\n<p>None of this really means that your problem is going to easily fit into a nice third-normal form system.&nbsp; Often there are legacy or performance concerns that push an application away from that sweet spot.&nbsp; Over time, various technologies have tried to bridge that gap (property tables, XML, and object-relational mappings).&nbsp; Each of them have their own reasons to be, and I don&#8217;t want to get into them in depth in my post.&nbsp; I&#8217;m going to talk about how the QP deals with these from a modeling perspective.<\/p>\n<p>I built two examples to explore how SQL Server 2008 reasons about sparse columns.&nbsp; One example creates lots of traditional, nullable float columns while the other is exactly the same except that it uses the sparse attribute.<\/p>\n<p>A few things I learned immediately:<br \/>1. Sparse columns don&#8217;t change the maximum number of columns you can create in a table.&nbsp; On the surface, this seems unfortunate, since it will limit the kinds of applications that can use the feature.&nbsp; <br \/>2. It does seem to use less space per row.&nbsp; This isn&#8217;t hard, as the row format for SQL Server has a null bitmap and also needs 2 bytes per column to store the variable offset pointers.<\/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;\">table<\/span> sp1(aaa <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;\">create<\/span> <span style=\"color: Blue; background-color: transparent; font-family: Courier New; font-size: 11px;\">table<\/span> sp2(aaa <span style=\"color: Blue; background-color: transparent; font-family: Courier New; font-size: 11px;\">int<\/span>)\r\n\r\n<span style=\"color: Blue; background-color: transparent; font-family: Courier New; font-size: 11px;\">declare<\/span> @i <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> @i=0\r\n<span style=\"color: Blue; background-color: transparent; font-family: Courier New; font-size: 11px;\">while<\/span> (@i &lt; 990)\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;\">declare<\/span> @sql <span style=\"color: Blue; background-color: transparent; font-family: Courier New; font-size: 11px;\">nvarchar<\/span>(400);\r\n<span style=\"color: Blue; background-color: transparent; font-family: Courier New; font-size: 11px;\">declare<\/span> @s <span style=\"color: Blue; background-color: transparent; font-family: Courier New; font-size: 11px;\">nvarchar<\/span>(20);\r\n<span style=\"color: Blue; background-color: transparent; font-family: Courier New; font-size: 11px;\">set<\/span> @s = @i;\r\n<span style=\"color: Blue; background-color: transparent; font-family: Courier New; font-size: 11px;\">set<\/span> @sql = <span style=\"color: Red; background-color: transparent; font-family: Courier New; font-size: 11px;\">'alter table sp1 add col'<\/span> + @s + <span style=\"color: Red; background-color: transparent; font-family: Courier New; font-size: 11px;\">' float sparse'<\/span>\r\n<span style=\"color: Blue; background-color: transparent; font-family: Courier New; font-size: 11px;\">exec<\/span> <span style=\"color: Brown; background-color: transparent; font-family: Courier New; font-size: 11px;\">sp_executesql<\/span> @sql\r\n<span style=\"color: Blue; background-color: transparent; font-family: Courier New; font-size: 11px;\">set<\/span> @i=@i+1\r\n<span style=\"color: Blue; background-color: transparent; font-family: Courier New; font-size: 11px;\">end<\/span>\r\n\r\n<span style=\"color: Blue; background-color: transparent; font-family: Courier New; font-size: 11px;\">declare<\/span> @i <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> @i=0\r\n<span style=\"color: Blue; background-color: transparent; font-family: Courier New; font-size: 11px;\">while<\/span> (@i &lt; 990)\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;\">declare<\/span> @sql <span style=\"color: Blue; background-color: transparent; font-family: Courier New; font-size: 11px;\">nvarchar<\/span>(400);\r\n<span style=\"color: Blue; background-color: transparent; font-family: Courier New; font-size: 11px;\">declare<\/span> @s <span style=\"color: Blue; background-color: transparent; font-family: Courier New; font-size: 11px;\">nvarchar<\/span>(20);\r\n<span style=\"color: Blue; background-color: transparent; font-family: Courier New; font-size: 11px;\">set<\/span> @s = @i;\r\n<span style=\"color: Blue; background-color: transparent; font-family: Courier New; font-size: 11px;\">set<\/span> @sql = <span style=\"color: Red; background-color: transparent; font-family: Courier New; font-size: 11px;\">'alter table sp2 add col'<\/span> + @s + <span style=\"color: Red; background-color: transparent; font-family: Courier New; font-size: 11px;\">' float'<\/span>\r\n<span style=\"color: Blue; background-color: transparent; font-family: Courier New; font-size: 11px;\">exec<\/span> <span style=\"color: Brown; background-color: transparent; font-family: Courier New; font-size: 11px;\">sp_executesql<\/span> @sql\r\n<span style=\"color: Blue; background-color: transparent; font-family: Courier New; font-size: 11px;\">set<\/span> @i=@i+1\r\n<span style=\"color: Blue; background-color: transparent; font-family: Courier New; font-size: 11px;\">end<\/span><\/span><\/pre>\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;\">declare<\/span> @i <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> @i=0\r\n<span style=\"color: Blue; background-color: transparent; font-family: Courier New; font-size: 11px;\">while<\/span> @i &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> sp1(col2) <span style=\"color: Blue; background-color: transparent; font-family: Courier New; font-size: 11px;\">values<\/span> (123.4)\r\n<span style=\"color: Blue; background-color: transparent; font-family: Courier New; font-size: 11px;\">set<\/span> @i=@i+1\r\n<span style=\"color: Blue; background-color: transparent; font-family: Courier New; font-size: 11px;\">end<\/span>\r\n\r\n<span style=\"color: Blue; background-color: transparent; font-family: Courier New; font-size: 11px;\">declare<\/span> @i <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> @i=0\r\n<span style=\"color: Blue; background-color: transparent; font-family: Courier New; font-size: 11px;\">while<\/span> @i &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> sp2(col2) <span style=\"color: Blue; background-color: transparent; font-family: Courier New; font-size: 11px;\">values<\/span> (123.4)\r\n<span style=\"color: Blue; background-color: transparent; font-family: Courier New; font-size: 11px;\">set<\/span> @i=@i+1\r\n<span style=\"color: Blue; background-color: transparent; font-family: Courier New; font-size: 11px;\">end<\/span><\/span><\/pre>\n<p>If we run &#8220;set statistics io on&#8221; and then run &#8220;select * from sp1&#8221; and &#8220;select * from sp2&#8221;, you&#8217;d like to see some difference in IOs:<\/p>\n<p>sp1:<br \/>(20000 row(s) affected)<br \/>Table &#8216;sp1&#8217;. Scan count 1, logical reads 86, physical reads 0, read-ahead reads 80, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.<\/p>\n<p>sp2:<br \/>(20000 row(s) affected)<br \/>Table &#8216;sp2&#8217;. Scan count 1, logical reads 20000, physical reads 1, read-ahead reads 19978, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.<\/p>\n<p>Well, that&#8217;s good &#8211; the sparse format on largely sparse data saves space.&nbsp; We can confirm that with a quick look into the system tables:<\/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> o.name <span style=\"color: Blue; background-color: transparent; font-family: Courier New; font-size: 11px;\">AS<\/span> table_name, au.type_desc, au.used_pages\r\n<span style=\"color: Blue; background-color: transparent; font-family: Courier New; font-size: 11px;\">FROM<\/span> sys.allocation_units <span style=\"color: Blue; background-color: transparent; font-family: Courier New; font-size: 11px;\">AS<\/span> au\r\n    <span style=\"color: Silver; background-color: transparent; font-family: Courier New; font-size: 11px;\">JOIN<\/span> sys.partitions <span style=\"color: Blue; background-color: transparent; font-family: Courier New; font-size: 11px;\">AS<\/span> p <span style=\"color: Blue; background-color: transparent; font-family: Courier New; font-size: 11px;\">ON<\/span> au.container_id = p.partition_id\r\n    <span style=\"color: Silver; background-color: transparent; font-family: Courier New; font-size: 11px;\">JOIN<\/span> sys.objects <span style=\"color: Blue; background-color: transparent; font-family: Courier New; font-size: 11px;\">AS<\/span> o <span style=\"color: Blue; background-color: transparent; font-family: Courier New; font-size: 11px;\">ON<\/span> p.<span style=\"color: Fuchsia; background-color: transparent; font-family: Courier New; font-size: 11px;\">object_id<\/span> = o.<span style=\"color: Fuchsia; background-color: transparent; font-family: Courier New; font-size: 11px;\">object_id<\/span>\r\n<span style=\"color: Blue; background-color: transparent; font-family: Courier New; font-size: 11px;\">WHERE<\/span> o.name <span style=\"color: Silver; background-color: transparent; font-family: Courier New; font-size: 11px;\">in<\/span> (N<span style=\"color: Red; background-color: transparent; font-family: Courier New; font-size: 11px;\">'sp1'<\/span>, N<span style=\"color: Red; background-color: transparent; font-family: Courier New; font-size: 11px;\">'sp2'<\/span>)<\/span><\/pre>\n<p>table_name&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; type_desc&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; used_pages<br \/>&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8211; &#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212; &#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8211;<br \/>sp1&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; IN_ROW_DATA&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 87<br \/>sp1&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ROW_OVERFLOW_DATA&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 0<br \/>sp2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; IN_ROW_DATA&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 20001<\/p>\n<p>(3 row(s) affected)<\/p>\n<p>We&#8217;ve now confirmed that we actually do have fewer pages.&nbsp; This is also good.<\/p>\n<p>Now let&#8217;s see how far into the QP this extends.&nbsp; Does the QP model the costs for these two queries differently?<\/p>\n<p>SP1 TotalSubtreeCost: 0.08824496<br \/>SP2 TotalSubtreeCost: 14.83936<\/p>\n<p>And that, my friends, is a &#8220;good thing&#8221;.&nbsp; This means that sparse columns are going to help your complex queries when you use a table with sparse columns in it.&nbsp; The easiest way to implement this is to simply ignore the new feature in the QP, and obviously someone did a good job to make sure that it was costed properly.&nbsp; <\/p>\n<p>I don&#8217;t believe that there are additional statistical structures to tell the QP which things are on\/off row.&nbsp; This will show up in a small number of scenarios (similar to how LOB data can be on\/off row).&nbsp; This is outside of the model for how the QP reasons about plan cost, at least from what I&#8217;ve seen from SQL 2008 and from what was publicly said about 2005.<\/p>\n<p>Thanks all,<\/p>\n<p>Conor Cunningham<\/p>\n<p><\/p>\n","protected":false},"excerpt":{"rendered":"<p>So I was playing today with the sparse column implementation in the latest SQL 2008 CTP, and I was specifically looking to see how this thing would work with the query processor.&nbsp; I wanted to know how much information is exposed for the QP to make smart decisions when this functionality is used in complex [&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-402","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>Hunting for SQL 2008 SPARSE Column Easter Eggs - 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\/hunting-for-sql-2008-sparse-column-easter-eggs\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Hunting for SQL 2008 SPARSE Column Easter Eggs - Conor Cunningham\" \/>\n<meta property=\"og:description\" content=\"So I was playing today with the sparse column implementation in the latest SQL 2008 CTP, and I was specifically looking to see how this thing would work with the query processor.&nbsp; I wanted to know how much information is exposed for the QP to make smart decisions when this functionality is used in complex [&hellip;]\" \/>\n<meta property=\"og:url\" content=\"https:\/\/www.sqlskills.com\/blogs\/conor\/hunting-for-sql-2008-sparse-column-easter-eggs\/\" \/>\n<meta property=\"og:site_name\" content=\"Conor Cunningham\" \/>\n<meta property=\"article:published_time\" content=\"2008-03-23T19:35:01+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=\"9 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\/hunting-for-sql-2008-sparse-column-easter-eggs\/\",\"url\":\"https:\/\/www.sqlskills.com\/blogs\/conor\/hunting-for-sql-2008-sparse-column-easter-eggs\/\",\"name\":\"Hunting for SQL 2008 SPARSE Column Easter Eggs - Conor Cunningham\",\"isPartOf\":{\"@id\":\"https:\/\/www.sqlskills.com\/blogs\/conor\/#website\"},\"datePublished\":\"2008-03-23T19:35:01+00:00\",\"dateModified\":\"2008-03-23T19:35:01+00:00\",\"author\":{\"@id\":\"https:\/\/www.sqlskills.com\/blogs\/conor\/#\/schema\/person\/f9106e03423de6b5157295891b8c3ae3\"},\"breadcrumb\":{\"@id\":\"https:\/\/www.sqlskills.com\/blogs\/conor\/hunting-for-sql-2008-sparse-column-easter-eggs\/#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/www.sqlskills.com\/blogs\/conor\/hunting-for-sql-2008-sparse-column-easter-eggs\/\"]}]},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/www.sqlskills.com\/blogs\/conor\/hunting-for-sql-2008-sparse-column-easter-eggs\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\/\/www.sqlskills.com\/blogs\/conor\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Hunting for SQL 2008 SPARSE Column Easter Eggs\"}]},{\"@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":"Hunting for SQL 2008 SPARSE Column Easter Eggs - 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\/hunting-for-sql-2008-sparse-column-easter-eggs\/","og_locale":"en_US","og_type":"article","og_title":"Hunting for SQL 2008 SPARSE Column Easter Eggs - Conor Cunningham","og_description":"So I was playing today with the sparse column implementation in the latest SQL 2008 CTP, and I was specifically looking to see how this thing would work with the query processor.&nbsp; I wanted to know how much information is exposed for the QP to make smart decisions when this functionality is used in complex [&hellip;]","og_url":"https:\/\/www.sqlskills.com\/blogs\/conor\/hunting-for-sql-2008-sparse-column-easter-eggs\/","og_site_name":"Conor Cunningham","article_published_time":"2008-03-23T19:35:01+00:00","author":"Conor Cunningham","twitter_misc":{"Written by":"Conor Cunningham","Est. reading time":"9 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"WebPage","@id":"https:\/\/www.sqlskills.com\/blogs\/conor\/hunting-for-sql-2008-sparse-column-easter-eggs\/","url":"https:\/\/www.sqlskills.com\/blogs\/conor\/hunting-for-sql-2008-sparse-column-easter-eggs\/","name":"Hunting for SQL 2008 SPARSE Column Easter Eggs - Conor Cunningham","isPartOf":{"@id":"https:\/\/www.sqlskills.com\/blogs\/conor\/#website"},"datePublished":"2008-03-23T19:35:01+00:00","dateModified":"2008-03-23T19:35:01+00:00","author":{"@id":"https:\/\/www.sqlskills.com\/blogs\/conor\/#\/schema\/person\/f9106e03423de6b5157295891b8c3ae3"},"breadcrumb":{"@id":"https:\/\/www.sqlskills.com\/blogs\/conor\/hunting-for-sql-2008-sparse-column-easter-eggs\/#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/www.sqlskills.com\/blogs\/conor\/hunting-for-sql-2008-sparse-column-easter-eggs\/"]}]},{"@type":"BreadcrumbList","@id":"https:\/\/www.sqlskills.com\/blogs\/conor\/hunting-for-sql-2008-sparse-column-easter-eggs\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/www.sqlskills.com\/blogs\/conor\/"},{"@type":"ListItem","position":2,"name":"Hunting for SQL 2008 SPARSE Column Easter Eggs"}]},{"@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\/402","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=402"}],"version-history":[{"count":0,"href":"https:\/\/www.sqlskills.com\/blogs\/conor\/wp-json\/wp\/v2\/posts\/402\/revisions"}],"wp:attachment":[{"href":"https:\/\/www.sqlskills.com\/blogs\/conor\/wp-json\/wp\/v2\/media?parent=402"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.sqlskills.com\/blogs\/conor\/wp-json\/wp\/v2\/categories?post=402"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.sqlskills.com\/blogs\/conor\/wp-json\/wp\/v2\/tags?post=402"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}