Search results matching tag 'Indexed Views'http://sqlblog.com/search/SearchResults.aspx?o=DateDescending&tag=Indexed+Views&orTags=0Search results matching tag 'Indexed Views'en-USCommunityServer 2.1 SP2 (Build: 61129.1)NOEXPAND query hint returns wrong results – CU fix now availablehttp://sqlblog.com/blogs/jamie_thomson/archive/2013/02/04/noexpand-query-hint-returns-wrong-results-cu-fix-now-available.aspxMon, 04 Feb 2013 10:24:00 GMT21093a07-8b3d-42db-8cbf-3350fcbf5496:47466jamiet<p>Three days ago at my current gig we stumbled across a problem where use of the <a href="http://msdn.microsoft.com/en-gb/library/ms181714.aspx" target="_blank">NOEXPAND query hint</a> was causing different results compared to the same query <em>without</em> NOEXPAND. For those that do not know (which, until three days ago, included me) NOEXPAND governs the use of indexed views:</p> <blockquote> <p><em>The indexed view is not expanded only if the view is directly referenced in the SELECT part of the query and WITH (NOEXPAND) or WITH (NOEXPAND, INDEX(index_value [ <strong>,</strong>...n ] ) ) is specified. <br></em><a title="http://msdn.microsoft.com/en-gb/library/ms181714.aspx" href="http://msdn.microsoft.com/en-gb/library/ms181714.aspx"><em>http://msdn.microsoft.com/en-gb/library/ms181714.aspx</em></a></p> </blockquote> <p>This screenshot demonstrates the problem:</p> <p><a href="http://sqlblog.com/blogs/jamie_thomson/image_09EE107F.png"><img style="border:0px currentColor;padding-top:0px;padding-right:0px;padding-left:0px;display:inline;background-image:none;" title="image" border="0" alt="image" src="http://sqlblog.com/blogs/jamie_thomson/image_thumb_5645A420.png" width="936" height="225"></a></p> <p>Clearly this isn’t good. <a href="https://twitter.com/jamiet/status/297384398972657664" target="_blank">I put a comment out on Twitter</a> hoping someone knew something about it and thankfully <a href="https://twitter.com/gonsalu">Gonçalo Ferreira</a> was reading. He <a href="https://twitter.com/gonsalu/status/298140026796138496" target="_blank">pointed me</a> toward a Knowledge Base article entitled <a href="http://support.microsoft.com/kb/2756471" target="_blank">FIX: Incorrect result is returned when you query an indexed view by using the NOEXPAND hint in SQL Server 2008, in SQL Server 2008 R2 or in SQL Server 2012</a> that confirmed this as being a bug in SQL Server. The article is dated 24th January 2013 (that is only 11 days ago folks) and describes the bug rearing its head under these circumstances:</p> <blockquote> <ul> <li><font color="#c0504d">You create an indexed view for two tables that have a foreign key reference in Microsoft SQL Server 2008, in Microsoft SQL Server 2012 or in Microsoft SQL Server 2008 R2. </font></li> <li><font color="#c0504d">You update the base tables of the indexed view. </font></li> <li><font color="#c0504d">You run a query against the indexed view that uses the NOEXPAND hint.</font></li> </ul> <font color="#c0504d">In this scenario, you receive an incorrect result.</font></blockquote> <p>I don’t know about you but I read that as “If you’re using Indexed Views with NOEXPAND, they’re most likely giving you the wrong answer”. The latest cumulative update (CU) for:</p> <ul> <li>SQL Server 2012</li> <li>SQL Server 2012 SP1</li> <li>SQL Server 2008 R2 SP2</li> <li>SQL Server 2008 R2 SP1</li> <li>SQL Server 2008 SP3</li> </ul> <p>fixes the problem. If you’re using indexed views you may want to install the CU, sharpish!</p> <p><a href="http://twitter.com/jamiet" target="_blank">@Jamiet</a></p><p><span style="text-transform:none;text-indent:0px;letter-spacing:normal;word-spacing:0px;float:none;white-space:normal;orphans:2;widows:2;font-size-adjust:none;font-stretch:normal;-webkit-text-size-adjust:auto;-webkit-text-stroke-width:0px;">UPDATE. Paul White has posted a fantastic blog post where he explains how to reproduce the problem: <a href="http://sqlblog.com/blogs/paul_white/archive/2013/02/06/incorrect-results-with-indexed-views.aspx" target="_blank">Incorrect Results with Indexed Views</a>&nbsp;(N.B. It occurs if you are using the MERGE statement) and Aaron Bertrand has followed with&nbsp;the equally readable <a href="http://www.sqlperformance.com/2013/02/t-sql-queries/another-merge-bug" target="_blank">If you are using indexed views and MERGE, please read this!</a> where he offers some considered opinions and advice surrounding the issue. I highly recommend reading both.</span></p>Connect Digest : 2011-06-27http://sqlblog.com/blogs/aaron_bertrand/archive/2011/06/27/connect-digest-2011-06-27.aspxMon, 27 Jun 2011 17:35:00 GMT21093a07-8b3d-42db-8cbf-3350fcbf5496:35785AaronBertrand<p>Sorry I have fallen off the Connect Digest wagon for the past few weeks; been a little swamped since returning from SQLCruise Alaska. Not sure I'll be able to assemble a digest every week, but I'll certainly try to keep a steady pace.<br></p><p>This week I wanted to highlight a few suggestions around indexed views. With the coming of SQL Server code-named "Denali" we will be pushed toward the new columnstore index as an alternative to indexed views. But this won't be for all cases, and it likely won't be available to all editions, either. So I hope that these requests don't start to get discarded with the simple workaround of upgrading to Denali, possibly upgrading to Enterprise Edition, and converting to the read-only columnstore index.</p><p>==================== <br></p><p>Ralf Dietrich suggests relaxing some of the restrictions on indexed view
creation; for example, the fact that an index can't be created on a
view with non-deterministic functions. He doesn't want it to just
suddenly start working, but to require a hint to make work. <br></p><blockquote><p><a href="http://connect.microsoft.com/SQLServer/feedback/details/577305/indexed-view-improvements" title="http://connect.microsoft.com/SQLServer/feedback/details/577305/indexed-view-improvements" target="_blank">#577305 : indexed view improvements<br></a></p></blockquote><p>==================== <br>
</p><p>Razvan Socol has a similar suggestion, that views with one or more unions could be indexed. His specific use case is not just for performance but also that he'd like to define a foreign key against the view.<br></p><blockquote><p><a href="http://connect.microsoft.com/SQLServer/feedback/details/125968/indexes-on-views-that-contain-union-s" title="http://connect.microsoft.com/SQLServer/feedback/details/125968/indexes-on-views-that-contain-union-s" target="_blank">#125968 : Indexes on views that contain UNION-s</a><br>
</p></blockquote><p>==================== <br>
</p><p>xor88 is asking for stacked (or "nested") indexed views. Essentially he wants to be able to create an index on a view that references another indexed view, in order to support persist aggregations over existing persisted aggregations. <br></p><blockquote><p><a href="http://connect.microsoft.com/SQLServer/feedback/details/641364" title="http://connect.microsoft.com/SQLServer/feedback/details/641364" target="_blank">#641364 : stacked indexed views</a></p></blockquote><p>==================== <br>
</p><p>Back in 2007, I asked for more aggregation support in indexed views - in addition to counts, for example, it would be helpful to have min/max support. More work for the engine, of course, but it could dramatically<br></p><blockquote><p><a href="http://connect.microsoft.com/SQLServer/feedback/details/267516/expand-aggregate-support-in-indexed-views-min-max" title="http://connect.microsoft.com/SQLServer/feedback/details/267516/expand-aggregate-support-in-indexed-views-min-max" target="_blank">#267516 : Expand aggregate support in indexed views (MIN/MAX)</a><br></p></blockquote><p>==================== <br>
</p><p>Adam Machanic (<a href="http://twitter.com/AdamMachanic" title="http://twitter.com/AdamMachanic" target="_blank">@AdamMachanic</a>) filed this bug, concerning the fact that indexed view creation takes a schema modification lock on the base table (resulting in blocking both readers and writers), when it would be possible to take only a schema stability and shared table lock (blocking only writers).<br></p><blockquote><p><a href="http://connect.microsoft.com/SQLServer/feedback/details/581056/indexed-view-creation-causes-queries-on-the-base-table-to-block" title="http://connect.microsoft.com/SQLServer/feedback/details/581056/indexed-view-creation-causes-queries-on-the-base-table-to-block" target="_blank">#581056 : Indexed view creation causes queries on the base table to block</a> <br></p></blockquote><p>==================== <br>
</p>A comment on the above item from Paul White (<a href="http://twitter.com/SQL_Kiwi" title="http://twitter.com/SQL_Kiwi" target="_blank">@SQL_Kiwi</a>) made me think of this suggestion, to delay index creation so that it could be done in the background and have less impact on real-time operations:<br><blockquote><p><a href="http://connect.microsoft.com/SQLServer/feedback/details/593032/indexed-views-option-for-delayed-seperate-index-creation-process-akin-to-full-text-indexing" title="http://connect.microsoft.com/SQLServer/feedback/details/593032/indexed-views-option-for-delayed-seperate-index-creation-process-akin-to-full-text-indexing" target="_blank">#593032 : Indexed Views - Option for Delayed Seperate Index creation Process - Akin to Full Text Indexing</a><br></p></blockquote><p>==================== </p><p>That's it for this week's digest. Please let me know if you have suggestions for future focus areas! <br></p><p>&nbsp; <br></p>Connect Digest : 2011-05-23http://sqlblog.com/blogs/aaron_bertrand/archive/2011/05/23/connect-digest-2011-05-23.aspxMon, 23 May 2011 12:28:00 GMT21093a07-8b3d-42db-8cbf-3350fcbf5496:35396AaronBertrand<p>Next week I'll be up in Alaska for <a href="http://www.sqlcruise.com/" title="http://www.sqlcruise.com/" target="_blank">SQL Cruise</a>, so I'll be skipping at least one digest. This week I gathered a few engine-related issues, two involving what the engine does while an index is being created. The most important one, though, at least IMHO, is the first one listed, involving a hashing algorithm fix that is in SQL Server 2008 R2 - but with no plans to fix it for SQL Server 2008. I wonder if someone should file a separate, 2008-specific bug?<br></p><blockquote><p><a href="http://connect.microsoft.com/SQLServer/feedback/details/458091/change-lock-resource-hashing-algorithm-to-reduce-likelihood-of-collisions" title="http://connect.microsoft.com/SQLServer/feedback/details/458091/change-lock-resource-hashing-algorithm-to-reduce-likelihood-of-collisions" target="_blank">#458091 : Change Lock Resource Hashing Algorithm to Reduce Likelihood of Collisions</a></p><p><a href="http://connect.microsoft.com/SQLServer/feedback/details/581056/indexed-view-creation-causes-queries-on-the-base-table-to-block" title="http://connect.microsoft.com/SQLServer/feedback/details/581056/indexed-view-creation-causes-queries-on-the-base-table-to-block" target="_blank">#581056 : Indexed view creation causes queries on the base table to block</a></p><p><a href="http://connect.microsoft.com/SQLServer/feedback/details/478568/ssms-gets-blocked-and-locks-up-during-index-creation" title="http://connect.microsoft.com/SQLServer/feedback/details/478568/ssms-gets-blocked-and-locks-up-during-index-creation" target="_blank">#478568 : SSMS Gets Blocked and Locks Up During Index Creation</a></p><p><a href="http://connect.microsoft.com/SQLServer/feedback/details/125549/poor-execution-plan-with-cte" title="http://connect.microsoft.com/SQLServer/feedback/details/125549/poor-execution-plan-with-cte" target="_blank">#125549 : Poor execution plan with CTE</a> <br></p></blockquote><p>And finally, this issue ("<span>high degrees of parallelism cause incorrect results to be observed more frequently" according to <a href="http://support.microsoft.com/kb/981502" title="http://support.microsoft.com/kb/981502" target="_blank">KB #981502</a>)</span> is one that has been fixed in SP1 for SQL Server 2008 R2, but it requires a trace flag (in Denali, it will be on by default, so you won't need a trace flag). I'm not telling you about this so you'll go and vote for this issue, but rather just to make you aware of it. The KB article states that the fix is in various CUs for 2005, 2008 and 2008 R2, but as the Connect item implies, the fix may not actually work in all cases, and requires the trace flag for others.<br></p><blockquote><p><a href="http://connect.microsoft.com/SQLServer/feedback/details/642790" title="http://connect.microsoft.com/SQLServer/feedback/details/642790" target="_blank">#642790 : Parallel query plan returns different results every time that you run the query</a></p><p>&nbsp; <br></p></blockquote>Connect Digest : 2011-05-16http://sqlblog.com/blogs/aaron_bertrand/archive/2011/05/16/connect-digest-2011-05-16.aspxMon, 16 May 2011 11:19:00 GMT21093a07-8b3d-42db-8cbf-3350fcbf5496:35372AaronBertrand<p>This week we're going to take a look at issues with filtered indexes. This feature was a very welcome addition in SQL Server 2008 and has enjoyed widespread usage if conversations on twitter and forums are any indication. But their implementation is not perfect - bugs in SSMS and numerous restrictions on their use have certainly reduced their impact, or at least made much more work for folks using them.<br></p><br><b>SSMS failures</b><br><blockquote><p>Several people have reported various cases where Management Studio trips over filtered index functionality. I would only expect these bugs to be fixed in either 2008 R2 SSMS or Denali (in some cases both) - don't hold out for your 2005 or even 2008 versions of SSMS to be fixed for filtered index or other "new feature" issues.<br></p><p><a href="http://connect.microsoft.com/SQLServer/feedback/details/526911/ssms-table-designer-fails-to-correctly-script-filtered-index" title="http://connect.microsoft.com/SQLServer/feedback/details/526911/ssms-table-designer-fails-to-correctly-script-filtered-index" target="_blank">#526911 : Table designer fails to correctly script filtered index</a><br><br><a href="http://connect.microsoft.com/SQLServer/feedback/details/462053/the-filter-expression-of-a-filtered-index-is-lost-when-a-table-is-modified-by-the-table-designer" title="http://connect.microsoft.com/SQLServer/feedback/details/462053/the-filter-expression-of-a-filtered-index-is-lost-when-a-table-is-modified-by-the-table-designer" target="_blank">#462053 : The filter expression of a filtered index is lost when a table is modified by the Table Designer</a><br>
<br><a href="http://connect.microsoft.com/SQLServer/feedback/details/362699/2008-rtm-ssms-engine-table-designer-doesnt-script-where-clause-in-filtered-indexes" title="http://connect.microsoft.com/SQLServer/feedback/details/362699/2008-rtm-ssms-engine-table-designer-doesnt-script-where-clause-in-filtered-indexes" target="_blank">#362699 : Table designer doesn't script WHERE clause in filtered indexes</a><br>&nbsp;
<br><a href="http://connect.microsoft.com/SQLServer/feedback/details/330238/msft-bicoe-csat-there-is-no-way-of-creating-filtered-index-from-ssms-while-designing-a-new-table" title="http://connect.microsoft.com/SQLServer/feedback/details/330238/msft-bicoe-csat-there-is-no-way-of-creating-filtered-index-from-ssms-while-designing-a-new-table" target="_blank">#330238 : There is no way of creating filtered index from SSMS while designing a new table</a><br>
<br><a href="http://connect.microsoft.com/SQLServer/feedback/details/521333/view-dependencies-fails-with-transact-sql-error-515" title="http://connect.microsoft.com/SQLServer/feedback/details/521333/view-dependencies-fails-with-transact-sql-error-515" target="_blank">#521333 : View Dependencies fails with Transact-SQL Error 515</a></p></blockquote><p><br><b>Engine failures</b></p><blockquote><p>Well, there is only one of these outstanding at this time, at least that I could find. I suspect this has to do with the testing matrix - they added both filtered indexes and MERGE in SQL Server 2008, but did not thoroughly test them *together.* There is a <a href="http://social.technet.microsoft.com/Forums/en-US/transactsql/thread/32727b47-fd03-4033-8140-510876ccada2" title="http://social.technet.microsoft.com/Forums/en-US/transactsql/thread/32727b47-fd03-4033-8140-510876ccada2" target="_blank">discussion thread on the MSDN forums</a> about this specific issue:<br></p><p><a href="http://connect.microsoft.com/SQLServer/feedback/details/596086/merge-statement-bug-when-insert-delete-used-and-filtered-index" title="http://connect.microsoft.com/SQLServer/feedback/details/596086/merge-statement-bug-when-insert-delete-used-and-filtered-index" target="_blank">#596086 : MERGE statement bug when INSERT/DELETE used and filtered index</a><br></p></blockquote><p><br><b>Missing functionality</b></p><blockquote><p>Some of these items are categorized as bugs, but I believe they simply represent scope creep. One of the items actually says something along the lines of, "It's not a bug; it's a known gap in functionality." Personally I think they got the bare minimum into SQL Server 2008 (supporting unique constraints the ANSI standard way, where more than one row can be NULL), and had no intention of supporting all of the other things that could make filtered indexes even more useful. If you find any of these suggestions valuable, please comment and vote - hopefully we can convince them to invest some time into this feature area for the next version after Denali! </p><p><a href="http://connect.microsoft.com/SQLServer/feedback/details/454744/filtered-index-not-used-and-key-lookup-with-no-output" title="http://connect.microsoft.com/SQLServer/feedback/details/454744/filtered-index-not-used-and-key-lookup-with-no-output" target="_blank">#454744 : Filtered index not used and key lookup with no output</a><a href="http://connect.microsoft.com/SQLServer/feedback/details/341891/filtered-index-creation-failed-with-not-in-clause" title="http://connect.microsoft.com/SQLServer/feedback/details/341891/filtered-index-creation-failed-with-not-in-clause" target="_blank"><br><br>#341891 : Filtered index creation failed with NOT IN clause</a><a href="http://connect.microsoft.com/SQLServer/feedback/details/341737/filtered-index-does-not-allow-filters-on-disjunctions%20" title="http://connect.microsoft.com/SQLServer/feedback/details/341737/filtered-index-does-not-allow-filters-on-disjunctions " target="_blank"><br><br>#341737 : Filtered index does not allow filters on disjunctions</a><a href="http://connect.microsoft.com/SQLServer/feedback/details/648551/" title="http://connect.microsoft.com/SQLServer/feedback/details/648551/" target="_blank"><br><br>#648551 : filtered index on indexed view</a><a href="http://connect.microsoft.com/SQLServer/feedback/details/666238/support-for-more-complex-where-clause-in-filtered-indexes" title="http://connect.microsoft.com/SQLServer/feedback/details/666238/support-for-more-complex-where-clause-in-filtered-indexes" target="_blank"><br><br>#666238 : Support for more complex WHERE clause in filtered indexes</a><a href="http://connect.microsoft.com/SQLServer/feedback/details/518328/should-be-possible-to-create-a-filtered-index-on-a-deterministic-persisted-computed-column" title="http://connect.microsoft.com/SQLServer/feedback/details/518328/should-be-possible-to-create-a-filtered-index-on-a-deterministic-persisted-computed-column" target="_blank"><br><br>#518328 : Should be possible to create a filtered index on a deterministic persisted computed column</a><a href="http://connect.microsoft.com/SQLServer/feedback/details/498009/allow-filtered-unique-index-to-be-a-canditate-key-for-a-foreign-key" title="http://connect.microsoft.com/SQLServer/feedback/details/498009/allow-filtered-unique-index-to-be-a-canditate-key-for-a-foreign-key" target="_blank"><br><br>#498009 : Allow filtered unique index to be a canditate key for a foreign key</a><a href="http://connect.microsoft.com/SQLServer/feedback/details/329805/enhancements-to-the-missing-indexes-dmvs-for-filtered-indexes" title="http://connect.microsoft.com/SQLServer/feedback/details/329805/enhancements-to-the-missing-indexes-dmvs-for-filtered-indexes" target="_blank"><br><br>#329805 : Enhancements to the missing indexes DMV's for filtered indexes</a><br></p></blockquote><br>&nbsp;<br>Mixing OLTP and reporting using indexed viewshttp://sqlblog.com/blogs/aaron_bertrand/archive/2009/12/29/mixing-oltp-and-reporting-using-indexed-views.aspxTue, 29 Dec 2009 14:40:00 GMT21093a07-8b3d-42db-8cbf-3350fcbf5496:20314AaronBertrand<p>I'm not a big fan of denormalizing, nor of repeating redundant <strike>redundant</strike> information in a database when those facts can already be derived from other information.&nbsp; A classic example of the latter is when I see questions on newsgroups, forums or StackOverflow that ask how they can update a table's rank column to reflect the current rank based on some other criteria in the table.&nbsp; The problem with this is that you have to run the update every time any DML operation touches any row in the table, and if you can calculate that rank in an UPDATE query, you can also calculate that rank in a SELECT query, so why not just figure it out in real time?&nbsp; An example of the former is when we want to maintain a second table with aggregates from another table.&nbsp; This is where I want to spend my time today.<br></p>
<p>In reality, many of us deal with OLTP systems that must also serve as the reporting source, so it's not always feasible to calculate aggregates in real time against a constantly moving target.&nbsp; In several of these scenarios I've used indexed views, where we have tables that are inserted often, read often for statistics, but rarely or never updated... sure, you pay a little hit up front on the insert, but the benefit achieved during real-time reporting was worth it.&nbsp; Let me give you an idea of what I was dealing with initially, and how I have fixed it over time to perform even better.</p>
<p>One of our systems is an SaaS e-mail platform, where we offer the ability for our customers to distribute messaging to their customers (mostly via e-mail, but we also support fax, SMS, widgets/gadgets, and social media).&nbsp; For brevity, let's call these people "recipients."&nbsp; If you stripped the schema down to the bare essentials for metadata, you'd see this (I'm going to leave out the foreign key and other constraints, as they are either obvious or irrelevant):</p>
<blockquote>
<table bgcolor="#eeeeee" cellpadding="0" cellspacing="0">
<tr>
<td>
<pre style="padding:10px 20px;-moz-background-clip:border;-moz-background-origin:padding;font-size:12px;font-family:consolas,lucida console,courier new,courier;-moz-background-inline-policy:continuous;"><font color="blue">CREATE&nbsp;TABLE&nbsp;</font><font color="black">dbo.Domains<br></font><font color="gray">(<br>&nbsp;&nbsp;</font><font color="black">DomainID&nbsp;</font><font color="blue">INT&nbsp;PRIMARY&nbsp;KEY</font><font color="gray">,<br>&nbsp;&nbsp;</font><font color="black">DomainName&nbsp;</font><font color="blue">VARCHAR</font><font color="gray">(</font><font color="black">255</font><font color="gray">)</font><font color="blue"><br></font><font color="gray">);<br><br></font><font color="blue">CREATE&nbsp;TABLE&nbsp;</font><font color="black">dbo.Recipients<br></font><font color="gray">(<br>&nbsp;&nbsp;</font><font color="black">RecipientID&nbsp;</font><font color="blue">INT&nbsp;PRIMARY&nbsp;KEY</font><font color="gray">,<br>&nbsp;&nbsp;</font><font color="black">DomainID&nbsp;</font><font color="blue">INT</font><font color="gray">,<br>&nbsp;&nbsp;</font><font color="black">LocalPart&nbsp;</font><font color="blue">VARCHAR</font><font color="gray">(</font><font color="black">64</font><font color="gray">)<br>&nbsp;&nbsp;</font><font color="green">/*&nbsp;,&nbsp;...&nbsp;other&nbsp;columns&nbsp;...&nbsp;*/<br></font><font color="gray">);&nbsp;</font></pre></td>
</tr>
</table>
</blockquote>
<p>Obviously we store many other details about recipients, such as demographic information, preferences, opt-in status, subscriptions, etc.&nbsp; But for this discussion, the above is sufficient.&nbsp; Notice that for space savings we don't actually store the e-mail address of the recipient, though it is required and is enforced to be unique via a constraint on (DomainID, LocalPart); this information can easily be derived using a view (you can even make this an indexed view if you want quicker access to e-mail addresses, but I'll leave that for another day): <br></p>
<blockquote>
<table bgcolor="#eeeeee" cellpadding="0" cellspacing="0">
<tr>
<td>
<pre style="padding:10px 20px;-moz-background-clip:border;-moz-background-origin:padding;font-size:12px;font-family:consolas,lucida console,courier new,courier;-moz-background-inline-policy:continuous;"><font color="blue">CREATE&nbsp;VIEW&nbsp;</font><font color="black">dbo.vRecipientDetails<br></font><font color="blue">AS<br>&nbsp;&nbsp;&nbsp;&nbsp;SELECT<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</font><font color="black">r.RecipientID</font><font color="gray">,<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</font><font color="black">EmailAddress = </font><font color="blue"></font><font color="black">r.LocalPart&nbsp;</font><font color="gray">+&nbsp;</font><font color="red">'@'&nbsp;</font><font color="gray">+&nbsp;</font><font color="black">d.DomainName<br>&nbsp;&nbsp;&nbsp;&nbsp;</font><font color="blue">FROM<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</font><font color="black">dbo.Recipients&nbsp;</font><font color="blue">AS&nbsp;</font><font color="black">r<br>&nbsp;&nbsp;&nbsp;&nbsp;</font><font color="blue">INNER&nbsp;JOIN<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</font><font color="black">dbo.Domains&nbsp;</font><font color="blue">AS&nbsp;</font><font color="black">d<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</font><font color="blue">ON&nbsp;</font><font color="black">r.DomainID&nbsp;</font><font color="blue">=&nbsp;</font><font color="black">d.DomainID</font><font color="gray">;</font></pre></td>
</tr>
</table>
</blockquote>
<p>RecipientID is the primary key because it is used in a lot of related tables, most importantly, stats tables.&nbsp; We record every single transaction for a recipient: when a message is attempted to be sent to them; when a successful delivery occurs; when a bounce occurs; when the recipient opens the message or clicks on a link in the message; when the recipient changes their preferences; or, when the user declines to receive further communications from our customer.&nbsp; The thought of storing LocalPart + DomainID (or the fully composed e-mail address) in all of these other tables that would grow and grow over time made a surrogate representation a very easy choice for the primary key.&nbsp; I didn't want to make the discussion revolve around this, but I've seen several raised eyebrows in the past about surrogate primary keys and wanted to assure you that, in this system, I think it is the best choice.&nbsp; (If you want to debate that, let's have a different discussion, as long as it doesn't turn religious.)<br></p>
<p>Okay, so I mentioned this is an OLTP system, and I mentioned that we record all of the individual transactions for a recipient in various stats tables.&nbsp; Let's take a quick look at how one of these stats tables looked for the first, oh, 5 years of their existence: <br></p>
<blockquote>
<table bgcolor="#eeeeee" cellpadding="0" cellspacing="0">
<tr>
<td>
<pre style="padding:10px 20px;-moz-background-clip:border;-moz-background-origin:padding;font-size:12px;font-family:consolas,lucida console,courier new,courier;-moz-background-inline-policy:continuous;"><font color="blue">CREATE&nbsp;TABLE&nbsp;</font><font color="black">dbo.Deliveries<br></font><font color="gray">(<br>&nbsp;&nbsp;</font><font color="black">MessageID&nbsp;</font><font color="blue">INT</font><font color="gray">,&nbsp;</font><font color="green">--&nbsp;references&nbsp;a&nbsp;table&nbsp;dbo.Messages<br>&nbsp;&nbsp;</font><font color="black">RecipientID&nbsp;</font><font color="blue">INT</font><font color="gray">,<br>&nbsp;&nbsp;</font><font color="black">EventDate </font><font><font color="blue">SMALLDATETIME</font></font><font color="black"><br>&nbsp;&nbsp;</font><font color="green">/*&nbsp;,&nbsp;...&nbsp;other&nbsp;columns...&nbsp;*/<br></font><font color="gray">);</font></pre></td>
</tr>
</table>
</blockquote>
<p>So a customer would send out a message targeted to, say, 50,000 recipients.&nbsp; During the next few hours we would fill up this table with successful deliveries (and other similar tables with bounces, opens, clicks, etc).&nbsp; Soon after inception of the system, we found that our customers wanted to pull real-time statistics on how the messages were going... mostly on the lookout for high bounce rates (well, low delivery rates too, I guess).&nbsp; Of course this was very contentious if they pulled reports while the tables were loading.&nbsp; So in order to give a much more satisfactory experience to the user pulling reports in our web UI, we created indexed views that would automatically maintain the number of deliveries, bounces, etc.:<br></p>
<blockquote>
<table bgcolor="#eeeeee" cellpadding="0" cellspacing="0">
<tr>
<td>
<pre style="padding:10px 20px;-moz-background-clip:border;-moz-background-origin:padding;font-size:12px;font-family:consolas,lucida console,courier new,courier;-moz-background-inline-policy:continuous;"><font color="blue">CREATE&nbsp;VIEW&nbsp;</font><font color="black">dbo.vMessageDeliveries<br></font><font color="blue">WITH SCHEMABINDING</font><font color="black"><br></font><font color="blue">AS<br>&nbsp;&nbsp;SELECT<br>&nbsp;&nbsp;&nbsp;&nbsp;</font><font color="black">MessageID</font><font color="gray">,<br>&nbsp;&nbsp;&nbsp; </font><font color="black">c = <font color="magenta">COUNT_BIG</font></font><font color="gray">(*)<br>&nbsp;&nbsp;</font><font color="blue">FROM<br>&nbsp;&nbsp;&nbsp;&nbsp;</font><font color="black">dbo.Deliveries<br>&nbsp;&nbsp;</font><font color="blue">GROUP&nbsp;BY<br>&nbsp;&nbsp;&nbsp;&nbsp;</font><font color="black">MessageID</font><font color="gray">;<br></font><font><font color="blue">GO</font></font><br><font color="black"><br></font><font color="blue">CREATE&nbsp;UNIQUE&nbsp;CLUSTERED&nbsp;INDEX&nbsp;</font><font color="black">m<br>&nbsp;&nbsp;</font><font color="blue">ON&nbsp;</font><font color="black">dbo.vMessageDeliveries</font><font color="gray">(</font><font color="black">MessageID</font><font color="gray">);</font></pre></td>
</tr>
</table>
</blockquote>
<p>So now the inserts were a little more expensive but, as mentioned above, this was much better for overall system performance (at least perceived performance) and, more importantly, customer happiness.</p>
<p>Then customers wanted more details on their statistics.&nbsp; They wanted to see how their messages were getting into, say, Hotmail compared to AOL.&nbsp; We have this information, but it was quite expensive to retrieve.&nbsp; Remember earlier I noted that we don't typically store redundant information, but this means that in cases like this, we need to do a lot of joins.&nbsp; So for example, to provide a responsive report that showed real-time message status per domain, we created indexed views like this: <br></p>
<blockquote>
<table bgcolor="#eeeeee" cellpadding="0" cellspacing="0">
<tr>
<td>
<pre style="padding:10px 20px;-moz-background-clip:border;-moz-background-origin:padding;font-size:12px;font-family:consolas,lucida console,courier new,courier;-moz-background-inline-policy:continuous;"><font color="blue">CREATE&nbsp;VIEW&nbsp;</font><font color="black">dbo.vMessageDomainDeliveries<br></font><font color="blue">WITH SCHEMABINDING</font><font color="black"><br></font><font color="blue">AS<br>&nbsp;&nbsp;SELECT<br>&nbsp;&nbsp;&nbsp;&nbsp;</font><font color="black">d.MessageID</font><font color="gray">,<br>&nbsp;&nbsp;&nbsp;&nbsp;</font><font color="black">r.DomainID</font><font color="gray">,<br>&nbsp;&nbsp;&nbsp; </font><font color="black">c = <font color="magenta">COUNT_BIG</font></font><font color="gray">(*)<br>&nbsp;&nbsp;</font><font color="blue">FROM<br>&nbsp;&nbsp;&nbsp;&nbsp;</font><font color="black">dbo.Deliveries&nbsp;</font><font color="blue">AS&nbsp;</font><font color="black">d<br>&nbsp;&nbsp;</font><font color="blue">INNER&nbsp;JOIN<br>&nbsp;&nbsp;&nbsp;&nbsp;</font><font color="black">dbo.Recipients&nbsp;</font><font color="blue">AS&nbsp;</font><font color="black">r<br>&nbsp;&nbsp;&nbsp;&nbsp;</font><font color="blue">ON&nbsp;</font><font color="black">r.RecipientID&nbsp;</font><font color="blue">=&nbsp;</font><font color="black">d.RecipientID<br>&nbsp;&nbsp;</font><font color="blue">GROUP&nbsp;BY<br>&nbsp;&nbsp;&nbsp;&nbsp;</font><font color="black">d.MessageID</font><font color="gray">,<br>&nbsp;&nbsp;&nbsp;&nbsp;</font><font color="black">r.DomainID</font><font color="gray">;<br></font><font><font><font><font color="blue">GO</font></font></font></font><br><font color="black"><br></font><font color="blue">CREATE&nbsp;UNIQUE&nbsp;CLUSTERED&nbsp;INDEX&nbsp;</font><font color="black">md<br>&nbsp;&nbsp;</font><font color="blue">ON&nbsp;</font><font color="black">dbo.vMessageDomainDeliveries</font><font color="gray">(</font><font color="black">MessageID</font><font color="gray">,&nbsp;</font><font color="black">DomainID</font><font color="gray">);</font></pre></td>
</tr>
</table>
</blockquote>
<p>Of course this again added more strain to the insert process on the deliveries table, since it now had to maintain two indexed views.&nbsp; In isolation, this still yielded better overall performance than essentially expanding that view and trying to get at the domain aggregates in real time.&nbsp; But it introduced a new problem to the system: severe blocking.&nbsp; Picture the case where deliveries are being recorded while a recipient is trying to update their preferences or change their e-mail address.&nbsp; Since both processes need to lock and potentially update the index on the indexed view, one has to wait for the other.&nbsp; This can lead to a sad face on an end user.</p>
<p>What I ended up doing was to recant my disdain for storing redundant information, and store the domain information in the deliveries table.&nbsp; Yes, this was an extra 4 bytes, but as we were moving the whole system to a new 2008 cluster with a much faster I/O subsystem, we would get much more than that back with page compression.&nbsp; So starting over, we still have the same Domains and Recipients tables, but now domain-based reports are driven from objects that look like these:</p>
<blockquote>
<table bgcolor="#eeeeee" cellpadding="0" cellspacing="0">
<tr>
<td>
<pre style="padding:10px 20px;-moz-background-clip:border;-moz-background-origin:padding;font-size:12px;font-family:consolas,lucida console,courier new,courier;-moz-background-inline-policy:continuous;"><font color="blue">CREATE&nbsp;TABLE&nbsp;</font><font color="black">dbo.Deliveries<br></font><font color="gray">(<br>&nbsp;&nbsp;</font><font color="black">MessageID&nbsp;</font><font color="blue">INT</font><font color="gray">,<br>&nbsp;&nbsp;</font><font color="black">RecipientID&nbsp;</font><font color="blue">INT</font><font color="gray">,<br>&nbsp;&nbsp;</font><font color="black">DomainID&nbsp;</font><font color="blue">INT</font><font color="gray">,<br>&nbsp;&nbsp;</font><font color="black">EventDate </font><font color="blue">SMALLDATETIME</font><font color="gray">,<br>&nbsp;&nbsp;</font><font color="green">/*&nbsp;,&nbsp;...&nbsp;other&nbsp;columns&nbsp;...&nbsp;*/<br></font><font color="gray">);<br></font><font color="blue">GO<br><br>CREATE&nbsp;VIEW&nbsp;</font><font color="black">dbo.vMessageDomainDeliveries<br></font><font color="blue">WITH SCHEMABINDING</font><font color="black"><br></font><font color="blue">AS<br>&nbsp;&nbsp;SELECT<br>&nbsp;&nbsp;&nbsp;&nbsp;</font><font color="black">MessageID</font><font color="gray">,<br>&nbsp;&nbsp;&nbsp;&nbsp;</font><font color="black">DomainID</font><font color="gray">,<br>&nbsp;&nbsp;&nbsp; </font><font color="black">c = <font color="magenta">COUNT_BIG</font></font><font color="gray">(*)<br>&nbsp;&nbsp;</font><font color="blue">FROM<br>&nbsp;&nbsp;&nbsp;&nbsp;</font><font color="black">dbo.Deliveries<br>&nbsp;&nbsp;</font><font color="blue">GROUP&nbsp;BY<br>&nbsp;&nbsp;&nbsp;&nbsp;</font><font color="black">MessageID</font><font color="gray">,<br>&nbsp;&nbsp;&nbsp;&nbsp;</font><font color="black">DomainID</font><font color="gray">;<br></font><font><font><font><font color="blue">GO</font></font></font></font><br><font color="black"><br></font><font color="blue">CREATE&nbsp;UNIQUE&nbsp;CLUSTERED&nbsp;INDEX&nbsp;</font><font color="black">md<br>&nbsp;&nbsp;</font><font color="blue">ON&nbsp;</font><font color="black">dbo.vMessageDomainDeliveries</font><font color="gray">(</font><font color="black">MessageID</font><font color="gray">,&nbsp;</font><font color="black">DomainID</font><font color="gray">);</font></pre></td>
</tr>
</table>
</blockquote>
<p>The up-front cost of calculating the domain of each recipient during insert is likely very equivalent to the system doing the same lookup while maintaining the indexed view (except we can use snapshot isolation in our query, but can't really enforce the same during clustered index updates).&nbsp; And in addition to no longer causing blocking on updates to the recipients table, we also get a much more accurate representation of history: the delivery is marked with the domain the recipient had on that day, since their e-mail address could have changed 20 times since then.&nbsp; All in all it has turned out that storing the domain information multiple times has helped contribute to making a very busy system suddenly seem almost idle.<br></p>
<p>The primary lesson learned here: indexed views can be a very helpful tool in your arsenal, but they can bring you down if you try to over-use them.&nbsp; Another lesson learned: do not assume that redundancy is the devil.&nbsp; It can help out in many scenarios, even if it feels a little dirty.<br></p>Connect Digest : 2009-10-30http://sqlblog.com/blogs/aaron_bertrand/archive/2009/10/30/connect-digest-2009-10-30.aspxFri, 30 Oct 2009 15:50:00 GMT21093a07-8b3d-42db-8cbf-3350fcbf5496:18245AaronBertrand<p>Light helping this week.&nbsp; I am busy closing out a few projects (or at least chapters within projects) in preparation for PASS next week.</p><p><br>========================================<br>
<br>
<a href="http://connect.microsoft.com/SQL/feedback/ViewFeedback.aspx?FeedbackID=506453" title="http://connect.microsoft.com/SQL/feedback/ViewFeedback.aspx?FeedbackID=506453" target="_blank">#506453 : Lock escalation no longer kicks in for INSERTs in SQL Server 2008</a> </p><p>Adam
found a nasty lock escalation bug in SQL Server 2008.&nbsp; This one is
actually serious enough that it has me really hesitant about my plans
for moving to the new version in production, as we process a lot of data in bulk operations like this (loading millions of rows into staging tables, then inserting/updating existing tables). <br><br></p>
<p>========================================<br><br><a href="http://connect.microsoft.com/SQL/feedback/ViewFeedback.aspx?FeedbackID=503207" title="http://connect.microsoft.com/SQL/feedback/ViewFeedback.aspx?FeedbackID=503207" target="_blank">#503207 : Bad query plan leads to wrong results (incorrectly uses indexed view)</a> </p><p>In this one there is an indexed view matching problem which can produce incorrect results.&nbsp; The fix is in SP1 CU1, so this isn't a "go vote for this issue" mention, but rather encouragement to get up to SP1 + the latest CU (which was <a href="http://support.microsoft.com/default.aspx/kb/973602" title="http://support.microsoft.com/default.aspx/kb/973602" target="_blank">Cumulative Update 4</a> at the time of writing) if you're currently sitting at SP1 or lower.</p><p><br>
========================================<br>
<br><a href="http://connect.microsoft.com/SQL/feedback/ViewFeedback.aspx?FeedbackID=504631" title="http://connect.microsoft.com/SQL/feedback/ViewFeedback.aspx?FeedbackID=504631" target="_blank">#504631 : add RAISE syntax for propagating exceptions</a></p><p><a href="http://connect.microsoft.com/SQL/feedback/ViewFeedback.aspx?FeedbackID=127228" title="http://connect.microsoft.com/SQL/feedback/ViewFeedback.aspx?FeedbackID=127228" target="_blank">#127228 : Rethrow last error</a></p><p><a href="http://connect.microsoft.com/SQL/feedback/ViewFeedback.aspx?FeedbackID=125719" title="http://connect.microsoft.com/SQL/feedback/ViewFeedback.aspx?FeedbackID=125719" target="_blank">#125719 : Support Reraising Of Original Error In Catch In T-SQL</a></p><p>A lot of people want RERAISE() and/or RETHROW() among other enhancements to error
handling.&nbsp; They are definitely looking at these changes for future
versions of SQL Server, so have your opinion heard!</p>
<p>&nbsp;</p>Connect digest : 2009-07-04http://sqlblog.com/blogs/aaron_bertrand/archive/2009/07/04/connect-digest-2009-07-04.aspxSat, 04 Jul 2009 14:07:00 GMT21093a07-8b3d-42db-8cbf-3350fcbf5496:15084AaronBertrand<p>I know most readers are out polishing their grills, buying hamburger buns and preparing potato and macaroni salads.&nbsp; I am recovering from a pre-4th party, so here I am, groggy and online, finally not the host of Independence Day madness.&nbsp; :-) </p><p>This week I only have a few entries for the Connect digest:</p><p>=================================</p><p><a href="http://connect.microsoft.com/SQLServer/feedback/ViewFeedback.aspx?FeedbackID=471201" title="http://connect.microsoft.com/SQLServer/feedback/ViewFeedback.aspx?FeedbackID=471201" target="_blank">#471201 : Incorrect query results with Enterprise edition indexed view substitution and NULL usage.<br></a> </p><p>This is a new one filed this past week, which Microsoft believes they have fixed, in spite of the fact that several other people, myself included, have confirmed that it still exists in current builds.&nbsp; It is also an issue in SQL Server 2005, which they fail to acknowledge.</p><p>=================================</p><p><a href="http://connect.microsoft.com/SQLServer/feedback/ViewFeedback.aspx?FeedbackID=472439" title="http://connect.microsoft.com/SQLServer/feedback/ViewFeedback.aspx?FeedbackID=472439" target="_blank">#472439 : Indexed View create via CREATE SCHEMA statement can produce error 8646 during updates</a> <br></p><p>Another indexed view issue filed yesterday, and also involving NULLs, in this item the submitter shows how a simple indexed view with two rows can succumb to the 8646 error (unable to find index entry) and the ominous:</p><blockquote><p><font face="courier new,courier">Msg 0, Level 20, State 0, Line 0<br>A severe error occurred on the current command. The results, if any, should be discarded. <br></font></p></blockquote><p>=================================</p><p><a href="http://connect.microsoft.com/SQLServer/feedback/ViewFeedback.aspx?FeedbackID=257502%20" title="http://connect.microsoft.com/SQLServer/feedback/ViewFeedback.aspx?FeedbackID=257502 " target="_blank">#257502 : Deprecation of sysprocesses - DMV's doesn't fully replace all columns</a></p><p>This is one that many of us have been complaining about since 2005 was released : because sys.dm_exec_requests only shows *active* sessions, and it is the primary place to obtain database context, it is very cumbersome to get database context for sessions that are not currently performing work.&nbsp; You also can't easily get the open transaction count from the new DMVs.&nbsp; This information was readily available in sysprocesses, of course, though we have been told that we should stop using this system table (actually now a backward-compatibility view) in favor of the new views.&nbsp; But Microsoft simply can't deprecate sysprocesses until they duplicate its functionality elsewhere.<br></p><p>=================================</p>
<p><a href="http://connect.microsoft.com/SQLServer/feedback/ViewFeedback.aspx?FeedbackID=472266" title="http://connect.microsoft.com/SQLServer/feedback/ViewFeedback.aspx?FeedbackID=472266" target="_blank">#472266 : Install fails when choosing a path for tempdb two folders deep</a> <br></p><p>This was an interesting one filed this week, where installation of SQL Server 2008 fails if you specify a folder for tempdb that contains the name "tempdb" (which is certainly a valid approach).&nbsp; The submitter did not get the symptom right; they think it has something to do with the number of folders in the path, but it is still an issue that Microsoft should look at.<br></p><p>=================================</p>
<p><a href="http://connect.microsoft.com/SQLServer/feedback/ViewFeedback.aspx?FeedbackID=472518" title="http://connect.microsoft.com/SQLServer/feedback/ViewFeedback.aspx?FeedbackID=472518" target="_blank">SQL Management Studio 2008 - enter Data Type of datetimeoffset(0) reverts to datetimeoffset(7)</a></p><p>More issues with the table designer... it's like a never-ending stream of scenarios that weren't tested or were deemed "good enough."&nbsp; This time someone noticed that when you tab out of a DATETIMEOFFSET column's data type, after having changed the precision to 0, it reverts to 7.&nbsp; Some related Connect items: </p><p><a href="http://connect.microsoft.com/SQLServer/feedback/ViewFeedback.aspx?FeedbackID=333445" title="http://connect.microsoft.com/SQLServer/feedback/ViewFeedback.aspx?FeedbackID=333445" target="_blank">#333445 : SSMS 2005 - Bug Table Designer substitues its own length , loses user input on changing data types </a></p><a href="http://connect.microsoft.com/SQLServer/feedback/ViewFeedback.aspx?FeedbackID=366117" title="http://connect.microsoft.com/SQLServer/feedback/ViewFeedback.aspx?FeedbackID=366117" target="_blank">#366117 : RTM, SSMS: changing precision on datetime2 from default in table designer reverts back to 7 </a><br><p><a href="http://connect.microsoft.com/SQLServer/feedback/ViewFeedback.aspx?FeedbackID=124872%20" title="http://connect.microsoft.com/SQLServer/feedback/ViewFeedback.aspx?FeedbackID=124872 " target="_blank">#124872 : Table Designer does not support newSequentialId()</a></p><a href="http://connect.microsoft.com/SQLServer/feedback/ViewFeedback.aspx?FeedbackID=362699" title="http://connect.microsoft.com/SQLServer/feedback/ViewFeedback.aspx?FeedbackID=362699" target="_blank">#362699 : 2008 RTM, SSMS/Engine: Table designer doesn't script WHERE clause in filtered indexes</a><p><a href="http://connect.microsoft.com/SQLServer/feedback/ViewFeedback.aspx?FeedbackID=124658" title="http://connect.microsoft.com/SQLServer/feedback/ViewFeedback.aspx?FeedbackID=124658" target="_blank">#124658 : SSMS Table Designer adds unwanted default to uniqueidentifier column<br></a> </p><p>=================================</p><p>Sorry I couldn't collate more this week, but as New England is seeing the sun for the first time in weeks, I really feel the need to go out and enjoy the holiday!<br></p>