<?xml version="1.0" encoding="UTF-8" ?>
<?xml-stylesheet type="text/xsl" href="http://sqlblog.com/utility/FeedStylesheets/rss.xsl" media="screen"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/" xmlns:wfw="http://wellformedweb.org/CommentAPI/"><channel><title>Search results matching tags 'Architecture' and 'Performance'</title><link>http://sqlblog.com/search/SearchResults.aspx?o=DateDescending&amp;tag=Architecture,Performance&amp;orTags=0</link><description>Search results matching tags 'Architecture' and 'Performance'</description><dc:language>en-US</dc:language><generator>CommunityServer 2.1 SP2 (Build: 61129.1)</generator><item><title>Accelerate OLTP with HP and Microsoft's New High Performance Reference Architecture</title><link>http://sqlblog.com/blogs/kevin_kline/archive/2012/03/06/accelerate-oltp-with-hp-and-microsoft-s-new-high-performance-reference-architecture.aspx</link><pubDate>Tue, 06 Mar 2012 15:25:00 GMT</pubDate><guid isPermaLink="false">21093a07-8b3d-42db-8cbf-3350fcbf5496:42126</guid><dc:creator>KKline</dc:creator><description>&lt;p&gt;If you haven't started to read Shashank Pawar (&lt;a title="Shashank Pawar's Blog" href="http://blogs.technet.com/b/sqlman/"&gt;blog&lt;/a&gt;), you're missing out.  Shashank is part of Microsoft Australia and has been writing some very good content lately.  Here's an example from the Reference Architecture for High Performance SQL Server:&lt;/p&gt;&lt;p style="padding-left:30px;"&gt;&lt;span&gt;HP and Microsoft engineering teams have worked together to create a reference architecture to Accelerate Online Transaction Processing (OLTP) database workloads with a fully-flash based HP/Microsoft architecture and achieve significant performance increases, simplified database manageability, and industry leading TCO.&lt;/span&gt;&lt;/p&gt;&lt;p&gt;The details come in a torrent after that leading paragraph with lots of pretty pictures and charts to help explain.  This is great stuff, especially for competitive platforms such as Oracle Exadata. &lt;/p&gt;&lt;p&gt;Read more about the new &lt;a title="High Performance SQL Server 2012" href="http://blogs.technet.com/b/sqlman/archive/2012/02/16/reference-architecture-for-high-performance-sql-server.aspx"&gt;HP High Performance Reference Architecture for SQL Server 2012&lt;/a&gt; here.&lt;/p&gt;&lt;p&gt;And just out of curiousity, are any of you using high performance architectures such as Oracle Exadata, IBM Netezza, or Teradata?  I'd love to hear your feedback, questions, and comments.&lt;/p&gt;&lt;p&gt;Enjoy,&lt;/p&gt;&lt;p&gt;-Kev &lt;/p&gt;&lt;p&gt;-&lt;a title="Kevin Kline's Twitter Feed" href="http://twitter.com/kekline"&gt;Follow me on Twitter&lt;/a&gt;&lt;/p&gt;&lt;p&gt;-&lt;a title="Kevin Kline's Blog" href="http://KevinEKline.com"&gt;More on my KevinEKline.com&lt;/a&gt;&lt;/p&gt;</description></item><item><title>Bad database practices: moving data to procedures vs. moving procedures to data</title><link>http://sqlblog.com/blogs/linchi_shea/archive/2009/10/30/database-bad-practices-moving-data-to-the-procedures-vs-moving-procedures-to-data.aspx</link><pubDate>Fri, 30 Oct 2009 04:00:00 GMT</pubDate><guid isPermaLink="false">21093a07-8b3d-42db-8cbf-3350fcbf5496:18365</guid><dc:creator>Linchi Shea</dc:creator><description>&lt;P class=MsoNormal style="MARGIN:0in 0in 0pt;"&gt;&lt;SPAN style="FONT-WEIGHT:normal;"&gt;&lt;FONT size=3&gt;&lt;FONT face="Times New Roman"&gt;Is it better to move data to procedures or move procedures to data? &lt;o:p&gt;&lt;/o:p&gt;&lt;/FONT&gt;&lt;/FONT&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN:0in 0in 0pt;"&gt;&lt;SPAN style="FONT-WEIGHT:normal;"&gt;&lt;o:p&gt;&lt;FONT face="Times New Roman" size=3&gt;&amp;nbsp;&lt;/FONT&gt;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN:0in 0in 0pt;"&gt;&lt;SPAN style="FONT-WEIGHT:normal;"&gt;&lt;FONT size=3&gt;&lt;FONT face="Times New Roman"&gt;The answer is, of course, “it depends.” Let’s consider a scenario where you have two SQL Server instances: ServerA and ServerB, and you have a procedure on ServerB (call it procB), but need to access data on ServerA. &lt;o:p&gt;&lt;/o:p&gt;&lt;/FONT&gt;&lt;/FONT&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN:0in 0in 0pt;"&gt;&lt;SPAN style="FONT-WEIGHT:normal;"&gt;&lt;o:p&gt;&lt;FONT face="Times New Roman" size=3&gt;&amp;nbsp;&lt;/FONT&gt;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN:0in 0in 0pt;"&gt;&lt;SPAN style="FONT-WEIGHT:normal;"&gt;&lt;FONT size=3&gt;&lt;FONT face="Times New Roman"&gt;Three database solutions are common:&lt;o:p&gt;&lt;/o:p&gt;&lt;/FONT&gt;&lt;/FONT&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN:0in 0in 0pt;"&gt;&lt;SPAN style="FONT-WEIGHT:normal;"&gt;&lt;o:p&gt;&lt;FONT face="Times New Roman" size=3&gt;&amp;nbsp;&lt;/FONT&gt;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=MsoList3 style="MARGIN:0in 0in 0pt 63pt;TEXT-INDENT:-63pt;tab-stops:.5in;mso-list:none;"&gt;&lt;FONT size=3&gt;&lt;FONT face="Times New Roman"&gt;&lt;STRONG&gt;Solution 1&lt;/STRONG&gt;&lt;SPAN style="FONT-WEIGHT:normal;"&gt;: &lt;SPAN style="mso-tab-count:1;"&gt;&amp;nbsp;&amp;nbsp; &lt;/SPAN&gt;You can first copy the data from ServerA to ServerB (using any number of technologies such as replication or a simple query via a linked server), and then have procB access the data locally on ServerB, however many times.&lt;o:p&gt;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/FONT&gt;&lt;/FONT&gt;&lt;/P&gt;
&lt;P class=MsoList3 style="MARGIN:0in 0in 0pt 63pt;TEXT-INDENT:-63pt;tab-stops:.5in;mso-list:none;"&gt;&lt;FONT size=3&gt;&lt;FONT face="Times New Roman"&gt;&lt;STRONG&gt;Solution 2: &lt;SPAN style="mso-tab-count:1;"&gt;&amp;nbsp; &lt;/SPAN&gt;&lt;/STRONG&gt;&lt;SPAN style="FONT-WEIGHT:normal;"&gt;You can have procB access the data on ServerA from ServerB through a linked server using a mechanism such as a distributed join or an openquery.&lt;o:p&gt;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/FONT&gt;&lt;/FONT&gt;&lt;/P&gt;
&lt;P class=MsoList3 style="MARGIN:0in 0in 0pt 63pt;TEXT-INDENT:-63pt;tab-stops:.5in;mso-list:none;"&gt;&lt;FONT size=3&gt;&lt;FONT face="Times New Roman"&gt;&lt;STRONG&gt;Solution 3&lt;/STRONG&gt;&lt;SPAN style="FONT-WEIGHT:normal;"&gt;: &lt;SPAN style="mso-tab-count:1;"&gt;&amp;nbsp;&amp;nbsp; &lt;/SPAN&gt;You can create a procedure procA on ServerA, and have procB make a remote procedure call to procA to retrieve its result, and then further process the returned data in procB on ServerB, if necessary.&lt;o:p&gt;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/FONT&gt;&lt;/FONT&gt;&lt;/P&gt;
&lt;P class=MsoList3 style="MARGIN:0in 0in 0pt 63pt;TEXT-INDENT:-63pt;tab-stops:.5in;mso-list:none;"&gt;&lt;SPAN style="FONT-WEIGHT:normal;"&gt;&lt;FONT size=3&gt;&lt;FONT face="Times New Roman"&gt;&lt;SPAN style="mso-spacerun:yes;"&gt;&amp;nbsp;&lt;/SPAN&gt;&lt;o:p&gt;&lt;/o:p&gt;&lt;/FONT&gt;&lt;/FONT&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=MsoList3 style="MARGIN:0in 0in 0pt;TEXT-INDENT:0in;tab-stops:.5in;mso-list:none;"&gt;&lt;SPAN style="FONT-WEIGHT:normal;"&gt;&lt;FONT size=3&gt;&lt;FONT face="Times New Roman"&gt;In a given application, any of these three solutions may be optimal, depending on the characteristics of that application. In particular, it depends on (1) the amount of data the procedure needs to access, (2) how current the data must be, and (3) how often the procedure needs to access the data. &lt;o:p&gt;&lt;/o:p&gt;&lt;/FONT&gt;&lt;/FONT&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=MsoList3 style="MARGIN:0in 0in 0pt;TEXT-INDENT:0in;tab-stops:.5in;mso-list:none;"&gt;&lt;SPAN style="FONT-WEIGHT:normal;"&gt;&lt;o:p&gt;&lt;FONT face="Times New Roman" size=3&gt;&amp;nbsp;&lt;/FONT&gt;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=MsoList3 style="MARGIN:0in 0in 0pt;TEXT-INDENT:0in;tab-stops:.5in;mso-list:none;"&gt;&lt;SPAN style="FONT-WEIGHT:normal;"&gt;&lt;FONT size=3&gt;&lt;FONT face="Times New Roman"&gt;For instance, if the amount of data on ServerA is large, it is okay for procB to read from a copy of the data that may be slightly or even significantly lagging behind its source, and (3) it needs to be accessed frequently in procB, then some version of Solution 1 can be a good choice.&lt;o:p&gt;&lt;/o:p&gt;&lt;/FONT&gt;&lt;/FONT&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=MsoList3 style="MARGIN:0in 0in 0pt;TEXT-INDENT:0in;tab-stops:.5in;mso-list:none;"&gt;&lt;SPAN style="FONT-WEIGHT:normal;"&gt;&lt;o:p&gt;&lt;FONT face="Times New Roman" size=3&gt;&amp;nbsp;&lt;/FONT&gt;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=MsoList3 style="MARGIN:0in 0in 0pt;TEXT-INDENT:0in;tab-stops:.5in;mso-list:none;"&gt;&lt;SPAN style="FONT-WEIGHT:normal;"&gt;&lt;FONT size=3&gt;&lt;FONT face="Times New Roman"&gt;The key consideration is that moving data, especially a large amount of data, across servers can be expensive, and moving a piece of code is cheap. &lt;o:p&gt;&lt;/o:p&gt;&lt;/FONT&gt;&lt;/FONT&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=MsoList3 style="MARGIN:0in 0in 0pt;TEXT-INDENT:0in;tab-stops:.5in;mso-list:none;"&gt;&lt;SPAN style="FONT-WEIGHT:normal;"&gt;&lt;o:p&gt;&lt;FONT face="Times New Roman" size=3&gt;&amp;nbsp;&lt;/FONT&gt;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=MsoList3 style="MARGIN:0in 0in 0pt;TEXT-INDENT:0in;tab-stops:.5in;mso-list:none;"&gt;&lt;SPAN style="FONT-WEIGHT:normal;"&gt;&lt;FONT size=3&gt;&lt;FONT face="Times New Roman"&gt;It follows that if a process involves moving a large amount of data across server boundary for processing on a different server, it &lt;I style="mso-bidi-font-style:normal;"&gt;&lt;U&gt;may&lt;/U&gt;&lt;/I&gt; be cheaper to move the code, or a relevant part of it, to where the data resides, and only send back the result, if the result is significantly smaller than the amount of data needed for processing.&lt;o:p&gt;&lt;/o:p&gt;&lt;/FONT&gt;&lt;/FONT&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=MsoList3 style="MARGIN:0in 0in 0pt;TEXT-INDENT:0in;tab-stops:.5in;mso-list:none;"&gt;&lt;SPAN style="FONT-WEIGHT:normal;"&gt;&lt;o:p&gt;&lt;FONT face="Times New Roman" size=3&gt;&amp;nbsp;&lt;/FONT&gt;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=MsoList3 style="MARGIN:0in 0in 0pt;TEXT-INDENT:0in;tab-stops:.5in;mso-list:none;"&gt;&lt;SPAN style="FONT-WEIGHT:normal;"&gt;&lt;FONT size=3&gt;&lt;FONT face="Times New Roman"&gt;Note that what is considered ‘a large amount’ should not be measured in terms of how many gigabytes, or even how many megabytes. Rather, it should be measured by the relative cost of moving the data versus that of the total process. So for instance, your procedure may be moving only 128KB of data from server A to server B, which generally speaking should be inexpensive, and the whole procedure executes quickly. But if this procedure is called very frequently, moving that 128KB constantly effectively ends up moving ‘a large amount’ of data, and can become rather expensive.&lt;o:p&gt;&lt;/o:p&gt;&lt;/FONT&gt;&lt;/FONT&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=MsoList3 style="MARGIN:0in 0in 0pt;TEXT-INDENT:0in;tab-stops:.5in;mso-list:none;"&gt;&lt;SPAN style="FONT-WEIGHT:normal;"&gt;&lt;o:p&gt;&lt;FONT face="Times New Roman" size=3&gt;&amp;nbsp;&lt;/FONT&gt;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=MsoList3 style="MARGIN:0in 0in 0pt;TEXT-INDENT:0in;tab-stops:.5in;mso-list:none;"&gt;&lt;SPAN style="FONT-WEIGHT:normal;"&gt;&lt;FONT size=3&gt;&lt;FONT face="Times New Roman"&gt;This sounds trivial. But unfortunately, I have seen too many cases in the real world where little thought is given to these considerations when creating procedures that access data across multiple servers.&lt;o:p&gt;&lt;/o:p&gt;&lt;/FONT&gt;&lt;/FONT&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=MsoList3 style="MARGIN:0in 0in 0pt;TEXT-INDENT:0in;tab-stops:.5in;mso-list:none;"&gt;&lt;SPAN style="FONT-WEIGHT:normal;"&gt;&lt;o:p&gt;&lt;FONT face="Times New Roman" size=3&gt;&amp;nbsp;&lt;/FONT&gt;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=MsoList3 style="MARGIN:0in 0in 0pt;TEXT-INDENT:0in;tab-stops:.5in;mso-list:none;"&gt;&lt;SPAN style="FONT-WEIGHT:normal;"&gt;&lt;FONT size=3&gt;&lt;FONT face="Times New Roman"&gt;In some instances, it is a case of abstraction abuse. By that, I mean people sometimes are too easy getting carried away with using other stored procedures and views (i.e. abstraction units), not knowing that they are incurring expensive cross-server data operations, or not exploring alternative options to cut down moving data across servers.&lt;o:p&gt;&lt;/o:p&gt;&lt;/FONT&gt;&lt;/FONT&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=MsoList3 style="MARGIN:0in 0in 0pt;TEXT-INDENT:0in;tab-stops:.5in;mso-list:none;"&gt;&lt;SPAN style="FONT-WEIGHT:normal;"&gt;&lt;o:p&gt;&lt;FONT face="Times New Roman" size=3&gt;&amp;nbsp;&lt;/FONT&gt;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=MsoList3 style="MARGIN:0in 0in 0pt;TEXT-INDENT:0in;tab-stops:.5in;mso-list:none;"&gt;&lt;SPAN style="FONT-WEIGHT:normal;"&gt;&lt;FONT size=3&gt;&lt;FONT face="Times New Roman"&gt;In other cases, programming convenience ends up trumping application performance. For instance, I have seen a case that goes something like the following:&lt;o:p&gt;&lt;/o:p&gt;&lt;/FONT&gt;&lt;/FONT&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=MsoList3 style="MARGIN:0in 0in 0pt;TEXT-INDENT:0in;tab-stops:.5in;mso-list:none;"&gt;&lt;SPAN style="FONT-WEIGHT:normal;"&gt;&lt;o:p&gt;&lt;FONT face="Times New Roman" size=3&gt;&amp;nbsp;&lt;/FONT&gt;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=MsoList3 style="MARGIN:0in 0in 0pt 0.5in;TEXT-INDENT:0in;tab-stops:.5in;mso-list:none;"&gt;&lt;SPAN style="FONT-WEIGHT:normal;"&gt;&lt;FONT size=3&gt;&lt;FONT face="Times New Roman"&gt;The procedure has a three table join that includes two tables from a linked server and a very small local table. The result is inserted into a temp table. The temp table is further processed in a series of update statements. Some of the updates use yet another very small local table. The final result set from these updates is small, and is used in the rest of the procedure.&lt;o:p&gt;&lt;/o:p&gt;&lt;/FONT&gt;&lt;/FONT&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=MsoList3 style="MARGIN:0in 0in 0pt 0.5in;TEXT-INDENT:0in;tab-stops:.5in;mso-list:none;"&gt;&lt;SPAN style="FONT-WEIGHT:normal;"&gt;&lt;o:p&gt;&lt;FONT face="Times New Roman" size=3&gt;&amp;nbsp;&lt;/FONT&gt;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=MsoList3 style="MARGIN:0in 0in 0pt 0.5in;TEXT-INDENT:0in;tab-stops:.5in;mso-list:none;"&gt;&lt;SPAN style="FONT-WEIGHT:normal;"&gt;&lt;FONT size=3&gt;&lt;FONT face="Times New Roman"&gt;It turns out that the two remote tables are relative large, and this procedure is called frequently. In addition, since the distribution join touches a significant proportion of each table of these two remote tables, basically all the rows are copied over locally to process the join. &lt;o:p&gt;&lt;/o:p&gt;&lt;/FONT&gt;&lt;/FONT&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=MsoList3 style="MARGIN:0in 0in 0pt 0.5in;TEXT-INDENT:0in;tab-stops:.5in;mso-list:none;"&gt;&lt;SPAN style="FONT-WEIGHT:normal;"&gt;&lt;o:p&gt;&lt;FONT face="Times New Roman" size=3&gt;&amp;nbsp;&lt;/FONT&gt;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=MsoList3 style="MARGIN:0in 0in 0pt;TEXT-INDENT:0in;tab-stops:.5in;mso-list:none;"&gt;&lt;SPAN style="FONT-WEIGHT:normal;"&gt;&lt;FONT size=3&gt;&lt;FONT face="Times New Roman"&gt;One way to optimize this procedure is to move the code segment mentioned above to the remote server and wrap it in a procedure there. This basically moves the entire process of preparing the final result set into the procedure on the remote server, and the local procedure now just needs to make a call to this new procedure, gets the small result set and move on with the rest of its processing. This is a perfect example where you want to move your procedures to your data instead of moving your data to your procedures.&lt;o:p&gt;&lt;/o:p&gt;&lt;/FONT&gt;&lt;/FONT&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=MsoList3 style="MARGIN:0in 0in 0pt;TEXT-INDENT:0in;tab-stops:.5in;mso-list:none;"&gt;&lt;SPAN style="FONT-WEIGHT:normal;"&gt;&lt;o:p&gt;&lt;FONT face="Times New Roman" size=3&gt;&amp;nbsp;&lt;/FONT&gt;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P class=MsoList3 style="MARGIN:0in 0in 0pt;TEXT-INDENT:0in;tab-stops:.5in;mso-list:none;"&gt;&lt;SPAN style="FONT-WEIGHT:normal;"&gt;&lt;FONT size=3&gt;&lt;FONT face="Times New Roman"&gt;So the bad practice is to not think through the trade-offs between moving data to procedures and moving procedures to data when architecting cross server solutions, and end up moving an unnecessary amount of data.&lt;SPAN style="mso-spacerun:yes;"&gt;&amp;nbsp; &lt;/SPAN&gt;&lt;/FONT&gt;&lt;/FONT&gt;&lt;o:p&gt;&lt;/o:p&gt;&lt;/SPAN&gt;&lt;/P&gt;</description></item><item><title>Amdahl’s Law</title><link>http://sqlblog.com/blogs/linchi_shea/archive/2008/02/11/amdahl-s-law.aspx</link><pubDate>Tue, 12 Feb 2008 04:43:00 GMT</pubDate><guid isPermaLink="false">21093a07-8b3d-42db-8cbf-3350fcbf5496:5042</guid><dc:creator>Linchi Shea</dc:creator><description>&lt;P class=MsoNormal&gt;For the past few weeks, I’ve been working on a diverse array of issues ranging from studying SQL Server performance on various multi-core processors, pondering the implications of many-core processors, troubleshooting SQL Server performance problems, looking at the scalability of Oracle RAC and Sybase shared-disk clusters, and so on.&lt;/P&gt;
&lt;P class=MsoNormal&gt;&amp;nbsp;&lt;/P&gt;
&lt;P class=MsoNormal&gt;Everywhere I turned, I bumped into the signs of Amdahl’s law, in particular Amdahl’s parallelism law. If you studied the computer architecture in school, you almost certainly have come across Amdahl’s various laws including his parallelism law below:&lt;/P&gt;
&lt;P class=MsoNormal&gt;&amp;nbsp;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN-LEFT:0.25in;"&gt;&lt;I&gt;If a computation has a serial component S, a parallel component P, and there are N processors, then the maximum speedup is as follows&lt;/I&gt;&lt;/P&gt;
&lt;P&gt;
&lt;TABLE class=MsoTableGrid style="BORDER-TOP-WIDTH:0px;BORDER-LEFT-WIDTH:0px;BORDER-BOTTOM-WIDTH:0px;MARGIN-LEFT:23.4pt;BORDER-COLLAPSE:collapse;BORDER-RIGHT-WIDTH:0px;" cellSpacing=0 cellPadding=0 class="MsoTableGrid"&gt;

&lt;TR&gt;
&lt;TD class="" style="BORDER-RIGHT:medium none;PADDING-RIGHT:5.4pt;BORDER-TOP:medium none;PADDING-LEFT:5.4pt;PADDING-BOTTOM:0in;BORDER-LEFT:medium none;WIDTH:80px;PADDING-TOP:0in;BORDER-BOTTOM:medium none;" rowSpan=2&gt;
&lt;P class=MsoNormal&gt;&lt;I&gt;Speedup =&lt;/I&gt;&lt;/P&gt;&lt;/TD&gt;
&lt;TD class="" style="BORDER-RIGHT:medium none;PADDING-RIGHT:5.4pt;BORDER-TOP:medium none;PADDING-LEFT:5.4pt;PADDING-BOTTOM:0in;BORDER-LEFT:medium none;WIDTH:136px;PADDING-TOP:0in;BORDER-BOTTOM:windowtext;"&gt;
&lt;P class=MsoNormal style="TEXT-ALIGN:center;" align=center&gt;&lt;I&gt;S + P&lt;/I&gt;&lt;/P&gt;&lt;/TD&gt;&lt;/TR&gt;
&lt;TR&gt;
&lt;TD class="" style="BORDER-RIGHT:medium none;PADDING-RIGHT:5.4pt;BORDER-TOP:medium none;PADDING-LEFT:5.4pt;PADDING-BOTTOM:0in;BORDER-LEFT:medium none;WIDTH:136px;PADDING-TOP:0in;BORDER-BOTTOM:medium none;"&gt;
&lt;DIV style="BORDER-TOP:1px solid;PADDING-TOP:1px;"&gt;
&lt;P class=MsoNormal style="TEXT-ALIGN:center;" align=center&gt;&lt;I&gt;S + P / N&lt;/I&gt;&lt;/P&gt;&lt;/DIV&gt;&lt;/TD&gt;&lt;/TR&gt;&lt;/TABLE&gt;&lt;I&gt;&lt;/I&gt;&lt;/P&gt;
&lt;P class=MsoNormal&gt;This law is often stated in various alternative but equivalent forms. The following is an alternative expression of Amdahl’s law in terms of the fraction of the computation that must be done serially (F):&lt;/P&gt;
&lt;P&gt;
&lt;TABLE class=MsoTableGrid style="BORDER-TOP-WIDTH:0px;BORDER-LEFT-WIDTH:0px;BORDER-BOTTOM-WIDTH:0px;MARGIN-LEFT:23.4pt;BORDER-COLLAPSE:collapse;BORDER-RIGHT-WIDTH:0px;" cellSpacing=0 cellPadding=0 class="MsoTableGrid"&gt;

&lt;TR&gt;
&lt;TD class="" style="BORDER-RIGHT:medium none;PADDING-RIGHT:5.4pt;BORDER-TOP:medium none;PADDING-LEFT:5.4pt;PADDING-BOTTOM:0in;BORDER-LEFT:medium none;WIDTH:79px;PADDING-TOP:0in;BORDER-BOTTOM:medium none;" rowSpan=2&gt;
&lt;P class=MsoNormal&gt;&lt;I&gt;Speedup =&lt;/I&gt;&lt;/P&gt;&lt;/TD&gt;
&lt;TD class="" style="BORDER-RIGHT:medium none;PADDING-RIGHT:5.4pt;BORDER-TOP:medium none;PADDING-LEFT:5.4pt;PADDING-BOTTOM:0in;BORDER-LEFT:medium none;WIDTH:137px;PADDING-TOP:0in;BORDER-BOTTOM:windowtext;"&gt;
&lt;P class=MsoNormal style="TEXT-ALIGN:center;" align=center&gt;&lt;I&gt;1&lt;/I&gt;&lt;/P&gt;&lt;/TD&gt;&lt;/TR&gt;
&lt;TR&gt;
&lt;TD class="" style="BORDER-RIGHT:medium none;PADDING-RIGHT:5.4pt;BORDER-TOP:medium none;PADDING-LEFT:5.4pt;PADDING-BOTTOM:0in;BORDER-LEFT:medium none;WIDTH:137px;PADDING-TOP:0in;BORDER-BOTTOM:medium none;"&gt;
&lt;DIV style="BORDER-TOP:1px solid;PADDING-TOP:1px;"&gt;
&lt;P class=MsoNormal style="TEXT-ALIGN:center;" align=center&gt;&lt;I&gt;F + (1 – F)&amp;nbsp; /&amp;nbsp; N&lt;/I&gt;&lt;/P&gt;&lt;/DIV&gt;&lt;/TD&gt;&lt;/TR&gt;&lt;/TABLE&gt;&lt;/P&gt;
&lt;P class=MsoNormal&gt;One of many important implications of this law is that even if the number of processors goes to infinity, the maximum speedup you may get is:&lt;/P&gt;
&lt;P&gt;
&lt;TABLE class=MsoTableGrid style="BORDER-TOP-WIDTH:0px;BORDER-LEFT-WIDTH:0px;BORDER-BOTTOM-WIDTH:0px;MARGIN-LEFT:23.4pt;BORDER-COLLAPSE:collapse;BORDER-RIGHT-WIDTH:0px;" cellSpacing=0 cellPadding=0 class="MsoTableGrid"&gt;

&lt;TR&gt;
&lt;TD class="" style="BORDER-RIGHT:medium none;PADDING-RIGHT:5.4pt;BORDER-TOP:medium none;PADDING-LEFT:5.4pt;PADDING-BOTTOM:0in;BORDER-LEFT:medium none;WIDTH:106px;PADDING-TOP:0in;BORDER-BOTTOM:medium none;" rowSpan=2&gt;
&lt;P class=MsoNormal&gt;&lt;I&gt;Max Speedup =&lt;/I&gt;&lt;/P&gt;&lt;/TD&gt;
&lt;TD class="" style="BORDER-RIGHT:medium none;PADDING-RIGHT:5.4pt;BORDER-TOP:medium none;PADDING-LEFT:5.4pt;PADDING-BOTTOM:0in;BORDER-LEFT:medium none;WIDTH:113px;PADDING-TOP:0in;BORDER-BOTTOM:windowtext;"&gt;
&lt;P class=MsoNormal style="TEXT-ALIGN:center;" align=center&gt;&lt;I&gt;S + P&lt;/I&gt;&lt;/P&gt;&lt;/TD&gt;&lt;/TR&gt;
&lt;TR&gt;
&lt;TD class="" style="BORDER-RIGHT:medium none;PADDING-RIGHT:5.4pt;BORDER-TOP:medium none;PADDING-LEFT:5.4pt;PADDING-BOTTOM:0in;BORDER-LEFT:medium none;WIDTH:113px;PADDING-TOP:0in;BORDER-BOTTOM:medium none;"&gt;
&lt;DIV style="BORDER-TOP:1px solid;PADDING-TOP:1px;"&gt;
&lt;P class=MsoNormal style="TEXT-ALIGN:center;" align=center&gt;&lt;I&gt;S&lt;/I&gt;&lt;/P&gt;&lt;/DIV&gt;&lt;/TD&gt;&lt;/TR&gt;&lt;/TABLE&gt;&lt;/P&gt;
&lt;P class=MsoNormal&gt;Therefore, if the serial component is a large fraction of the whole computation, you won’t get much speedup no matter how many processors you may have. On the other hand, if the parallel component is large, the speedup can be significant.&lt;/P&gt;
&lt;P class=MsoNormal&gt;&amp;nbsp;&lt;/P&gt;
&lt;P class=MsoNormal&gt;So, if your system has a shared global state, for consistency it must be accessed (updated) in a serialized fashion. This law suggests that if your workload is seriously bottlenecked on this global state, you won’t get much performance improvement by focusing your efforts on getting better processors, more processors, or improving any other hardware or software parallel components.&lt;/P&gt;
&lt;P class=MsoNormal&gt;&amp;nbsp;&lt;/P&gt;
&lt;P class=MsoNormal&gt;In the case of using the Intel multi-core processors, if your workload happens to bottleneck on its front-side bus, it most likely would not help to switch to faster processors. For an interesting discussion of the implications of Amdahl's law on multicore computing, I highly recommend reading this article: "&lt;A href="http://www.cs.wisc.edu/multifacet/papers/tr1593_amdahl_multicore.pdf"&gt;Amdahl's Law in the Multicore Era&lt;/A&gt;".&lt;/P&gt;
&lt;P class=MsoNormal&gt;&amp;nbsp;&lt;/P&gt;
&lt;P class=MsoNormal&gt;And if you are designing a database application, but the design relies heavily on the shared tempdb, thus causing significant serialization on the tempdb system data structures, the scalability of your app will suffer.&lt;/P&gt;
&lt;P class=MsoNormal&gt;&amp;nbsp;&lt;/P&gt;
&lt;P class=MsoNormal&gt;In the case of RAC and Sybase shared-disk clusters, if your workload is such that it cause a huge amount of inter-node traffic to maintain cache coherency, your app will not scale well with more nodes in the cluster.&lt;/P&gt;
&lt;P class=MsoNormal&gt;&amp;nbsp;&lt;/P&gt;
&lt;P class=MsoNormal&gt;These observations are intuitive, and are easy to understand without you ever being exposed to Amdahl’s law. However, Amdahl’s law elegantly highlights the common thread to you, and is worth revisiting from time to time.&lt;/P&gt;</description></item><item><title>Where does Time Go?</title><link>http://sqlblog.com/blogs/linchi_shea/archive/2008/01/02/where-does-time-go.aspx</link><pubDate>Wed, 02 Jan 2008 05:02:00 GMT</pubDate><guid isPermaLink="false">21093a07-8b3d-42db-8cbf-3350fcbf5496:4312</guid><dc:creator>Linchi Shea</dc:creator><description>&lt;FONT face="Times New Roman" size=3&gt;
&lt;P class=MsoNormal style="MARGIN:0pt;"&gt;A significant part of my job is to evaluate how SQL Server (and sometimes other DBMSs) performs on various hardware platforms, in particular on the processors and its related chipsets as they are being released. So naturally, I’ve been paying attention to performance analysis of DBMSs. &lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN:0pt;"&gt;&lt;o:p&gt;&amp;nbsp;&lt;/o:p&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN:0pt;"&gt;One of the papers at the top of my reference list&amp;nbsp;for&amp;nbsp;this area&amp;nbsp;was written by a group of researchers from University of&amp;nbsp;Wisconsin: “&lt;A href="http://www.cs.wisc.edu/multifacet/papers/vldb99_dbms_eval.pdf"&gt;DBMSs On A Modern Processor: Where Does Time Go?&lt;/A&gt;” This paper was published in 1999, but its findings and methodology are still interesting.&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN:0pt;"&gt;&lt;o:p&gt;&amp;nbsp;&lt;/o:p&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN:0pt;"&gt;The key conclusion of the paper is that when data is memory resident, the main bottleneck to the DBMS performance is L2 data cache misses, and to some extent L1 instruction cache misses. This conclusion appears to be valid even with today’s commercial DBMSs, given that the conclusion is still being quoted in recently published studies.&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN:0pt;"&gt;&lt;o:p&gt;&amp;nbsp;&lt;/o:p&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN:0pt;"&gt;If you are interested in the DBMS performance with respect to processors and memory hierarchies, I highly recommend this paper.&lt;/P&gt;&lt;/FONT&gt;</description></item><item><title>Best and Worst Checkpoint Performance</title><link>http://sqlblog.com/blogs/linchi_shea/archive/2007/11/01/best-and-worst-checkpoint-performance.aspx</link><pubDate>Thu, 01 Nov 2007 04:05:00 GMT</pubDate><guid isPermaLink="false">21093a07-8b3d-42db-8cbf-3350fcbf5496:3186</guid><dc:creator>Linchi Shea</dc:creator><description>&lt;P&gt;The best documentation on the I/O behavior of SQL Server checkpoints is found in &lt;A href="http://www.microsoft.com/technet/prodtechnol/sql/2000/maintain/sqlIObasics.mspx"&gt;SQL Server 2000 I/O Basics&lt;/A&gt; by Bob Dorr. In particular, you should read the following carefully:&lt;/P&gt;
&lt;BLOCKQUOTE&gt;
&lt;DIV style="BACKGROUND-COLOR:#cccccc;"&gt;&lt;FONT face="Arial Unicode MS" size=2&gt;SQL Server uses the following steps to set up another page for flushing and repeats for up to 16 total pages inclusive of the first page.&lt;/FONT&gt; 
&lt;OL&gt;
&lt;LI&gt;&lt;FONT face="Arial Unicode MS" size=2&gt;Do a hash lookup for the next contiguous page. For example, if the page to be flushed were page 100, SQL Server searches for page 101 in the buffer hash array.&lt;/FONT&gt;&lt;/LI&gt;
&lt;LI&gt;&lt;FONT face="Arial Unicode MS" size=2&gt;If the page is not located, then the end of contiguous I/O block is established and the I/O is posted.&lt;/FONT&gt;&lt;/LI&gt;
&lt;LI&gt;&lt;FONT face="Arial Unicode MS" size=2&gt;If the page is located, acquire latch to prevent further modifications if the page may be dirty.&lt;/FONT&gt;&lt;/LI&gt;
&lt;LI&gt;&lt;FONT face="Arial Unicode MS" size=2&gt;Check to ensure that the page is dirty and needs to be written. If not, release the latch and consider the end of contiguous I/O block as established and submit the asynchronous I/O request.&lt;/FONT&gt;&lt;/LI&gt;
&lt;LI&gt;&lt;FONT face="Arial Unicode MS" size=2&gt;If dirty, follow the steps preceding these that tell you how to flush a single page.&lt;/FONT&gt;&lt;/LI&gt;&lt;/OL&gt;
&lt;P&gt;&lt;FONT face="Arial Unicode MS" size=2&gt;After the set of pages to be flushed is determined, the &lt;B&gt;WriteFileGather&lt;/B&gt; function is invoked to post (Async / OVERLAPPED) the I/O request with the associated callback function to complete the I/O operation.&lt;/FONT&gt;&lt;/P&gt;&lt;/DIV&gt;&lt;/BLOCKQUOTE&gt;
&lt;P&gt;In &lt;A href="http://www.microsoft.com/technet/prodtechnol/sql/2005/iobasics.mspx"&gt;SQL Server I/O Basics, Chapter 2&lt;/A&gt;, Bob Dorr further states that, "SQL Server 7.0 and 2000 could issue a WriteMultiple operation for up to 16 pages (128 KB). SQL Server 2005 extends the capability of WriteMultiple up to 32 pages (256 KB)."&lt;/P&gt;
&lt;P&gt;For most disk I/O subsystems, large I/Os--such as 256K writes--are much more efficient than small I/Os such as 8K writes. It thus follows that if SQL Server checkpoints are forced to issue single-page I/Os, checkpoint performance would seriously suffer.&lt;/P&gt;
&lt;P&gt;To see this for myself, I conducted a little test on SQL Server 2005. First, I created a heap table that is one page per row, and populated it with 1,000,000 rows, resulting in ~8GB of used space. The table schema is as follows:&lt;/P&gt;
&lt;DIV style="BACKGROUND-COLOR:#cccccc;"&gt;&lt;PRE&gt;CREATE TABLE tbTest(
    i      int NOT NULL, 
    filler char(8000) NOT NULL
)&lt;/PRE&gt;&lt;/DIV&gt;
&lt;P&gt;And the table was populated with the following script:&lt;/P&gt;
&lt;DIV style="BACKGROUND-COLOR:#cccccc;"&gt;&lt;PRE&gt;SET IMPLICIT_TRANSACTIONS ON

DECLARE @batch_size int
SET @batch_size = 100000  

DECLARE @i int
SET @i = 1
WHILE @i &amp;lt;= 1000000
BEGIN
    INSERT tbTest(i, filler) VALUES (@i, ' ')

    IF (@i % @batch_size = 0)
    BEGIN
       COMMIT TRAN 
    END
    SET @i = @i + 1
END
if @@trancount &amp;gt; 0
   commit tran

SET IMPLICIT_TRANSACTIONS OFF&lt;/PRE&gt;&lt;/DIV&gt;
&lt;P&gt;Since the test server had 12GB physical memory allocated to the SQL Server buffer pool, all the pages of this table could be cached in memory. I then disabled automatic checkpoints with trace flag 3502 so that I could control when I wanted to checkpoint the database, and study the checkpoint performance.&lt;/P&gt;
&lt;P&gt;I examined two extreme SQL Server 2005 database checkpoint scenarios: &lt;/P&gt;
&lt;UL&gt;
&lt;LI&gt;&lt;B&gt;The Best Scenario&lt;/B&gt; where all the dirty pages were contiguous and all the checkpoint I/Os should be 256K writes, and&lt;/LI&gt;
&lt;LI&gt;&lt;B&gt;The Worst Scenario&lt;/B&gt; where no two dirty pages were contiguous and all the checkpoint I/Os should therefore be 8K writes&lt;/LI&gt;&lt;/UL&gt;
&lt;P&gt;The best scenario was accomplished with the following code:&lt;/P&gt;
&lt;DIV style="BACKGROUND-COLOR:#cccccc;"&gt;&lt;PRE&gt;update tbTest
   set filler = 'xyz'
 where i &amp;lt; 500000

declare @dt datetime
set @dt = getdate()
  checkpoint
select datediff(second, @dt, getdate())&lt;/PRE&gt;&lt;/DIV&gt;
&lt;P&gt;And the worst scenario was produced with this code:&lt;/P&gt;
&lt;DIV style="BACKGROUND-COLOR:#cccccc;"&gt;&lt;PRE&gt;update tbTest
   set filler = '123'
 where i % 2 = 0

declare @dt datetime
set @dt = getdate()
  checkpoint
select datediff(second, @dt, getdate())&lt;/PRE&gt;&lt;/DIV&gt;
&lt;P&gt;There was no difference in execution duration between the two UPDATE statements. After all, they both performed a full table scan and updated the same number of rows (i.e. 500,000). However, note that the first UPDATE statement modified the first 500,000 consecutive rows, thus 500,000 contiguous pages, whereas the second UPDATE statement modified every other row, leaving no two dirty pages contiguous.&lt;/P&gt;
&lt;P&gt;The following chart shows a significant contrast between the checkpoint durations of these two rather extreme scenarios:&lt;/P&gt;
&lt;P&gt;&lt;IMG src="http://sqlblog.com/blogs/linchi_shea/attachment/3186.ashx"&gt; &lt;/P&gt;
&lt;P&gt;In this test, it took about &lt;I&gt;&lt;B&gt;four times as long &lt;/B&gt;&lt;/I&gt;to checkpoint the same number of dirty pages in the worst scenario (duration =~ 80 seconds) as it did in the best scenario (duration =~ 20 seconds). The performance difference was staggering.&lt;/P&gt;
&lt;P&gt;How did I know that SQL Server checkpoints were doing large or small I/Os? That was through observing the disk counters while the checkpoints were in progress. In the&amp;nbsp;&lt;U&gt;worst&lt;/U&gt; scenario, the Avg. Disk Bytes/Write counter remained almost a constant of 8192 bytes = 8KB, while in the&amp;nbsp;&lt;U&gt;best&lt;/U&gt; scenario, the same disk counter hovered around 260,000 bytes =~ 254KB. [Thanks, Andrew Kelly, for pointing out a mistake here--the two underlined words were incorrectly transposed. Updated 2007/11/01.]&lt;/P&gt;
&lt;P&gt;It is also interesting to note that in the best scenario, the Current Disk Queue Length counter was about 20 for most of the time, whereas in the worst scenario the Current Disk Queue Length spiked to 100 or even higher. Apparently, SQL Server was trying to push the I/Os harder with more outstanding async I/O write requests when it detected that a checkpoint was issuing small block sized I/Os. This didn't quite do the trick as that pushed up the I/O latency--as measured by Avg Disk sec/Write--to greater than 48ms per write in the worst scenario as compared to ~20ms per write in the best scenario, even when the former was doing smaller I/Os.&lt;/P&gt;
&lt;P&gt;So besides confirming what Bob Dorr described with a concrete example, are there any practical implications or can we apply the discussions to anything practical? Of course, but I'll leave that as an exercise for you to ponder.&lt;/P&gt;</description></item><item><title>Hash Partition as a Design Method</title><link>http://sqlblog.com/blogs/linchi_shea/archive/2007/01/22/hash-partition-as-a-design-method.aspx</link><pubDate>Tue, 23 Jan 2007 02:35:00 GMT</pubDate><guid isPermaLink="false">21093a07-8b3d-42db-8cbf-3350fcbf5496:616</guid><dc:creator>Linchi Shea</dc:creator><description>&lt;P&gt;SQL Server 2005 does not support hash partition as a product feature. But that doesn't mean you can't apply the concept of hash partition in your application or database design. &lt;/P&gt;
&lt;P&gt;Recently, I worked on an application that used SQL Server to persist the customized ASP.NET session state information, similar to &lt;A href="http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cpguide/html/cpconsessionstate.asp"&gt;&lt;SPAN style="TEXT-DECORATION:none;"&gt;the SQL Server mode for ASP.NET session state management&lt;/SPAN&gt;&lt;/A&gt;. The table schema and the stored procedures were similar to those found in the Microsoft KB article &lt;A href="http://support.microsoft.com/kb/311209/en-us"&gt;&lt;SPAN style="TEXT-DECORATION:none;"&gt;311209&lt;/SPAN&gt;&lt;/A&gt;. The most salient points to note are:&lt;/P&gt;
&lt;UL&gt;
&lt;LI&gt;The session state information was stored in a single table,&lt;/LI&gt;
&lt;LI&gt;Every row was associated with a GUID session state id, and&lt;/LI&gt;
&lt;LI&gt;Every stored procedure had a session state id parameter&lt;/LI&gt;&lt;/UL&gt;
&lt;P&gt;In studying the scalability and capacity of the session state database, I found that under stress load the database transaction throughput was not constrained by CPU, memory, disk I/Os, or network. But rather, the transaction throughput was limited by contentions inside SQL Server. More precisely, almost all the SQL Server resource waits were found to be on page latches. Therefore, reducing these page-level contentions should lead to higher transaction throughput. &lt;/P&gt;
&lt;P&gt;One potential solution was to split the single table into multiple tables. There were several different ways to split the table. One approach was to split the table into multiple tables of the identical structure in the same database. &lt;/P&gt;
&lt;P&gt;For the load test, I took a different, but simpler, route to split the table into multiple identically-structured databases. As a matter of fact, I created 20 clones of the original database. These 20 databases differed only in their names. I kept the original database name, but removed all the user tables, and replaced all the stored procedures that were directly called by the application with their corresponding wrapper stored procedures. The only function of a wrapper stored procedure was to route the calls from the application to the 20 newly created databases, which were not directly visible to the application.&lt;/P&gt;
&lt;P&gt;So how would the incoming stored procedure calls be routed? Hash partition was the answer.&lt;/P&gt;
&lt;P&gt;Basically, since each call contained a GUID session state id, we could map that GUID to a number corresponding to one of the 20 databases, and route the call to that database. The following is a sample procedure for routing a call to store procedure ExecCommand:&lt;/P&gt;
&lt;DIV class=O&gt;
&lt;DIV style="BACKGROUND-COLOR:#f0eef0;"&gt;
&lt;DIV style="mso-line-spacing:'80 20 0';mso-margin-left-alt:216;mso-char-wrap:1;mso-kinsoku-overflow:1;"&gt;&lt;SPAN style="FONT-FAMILY:Courier New;mso-ascii-font-family:Garamond;mso-bidi-font-family:Arial;text-shadow:auto;"&gt;&lt;FONT size=2&gt;CREATE PROCEDURE dbo.ExecCommand &lt;/FONT&gt;&lt;/SPAN&gt;&lt;/DIV&gt;
&lt;DIV style="mso-line-spacing:'80 20 0';mso-margin-left-alt:216;mso-char-wrap:1;mso-kinsoku-overflow:1;"&gt;&lt;SPAN style="FONT-FAMILY:Courier New;mso-ascii-font-family:Garamond;mso-bidi-font-family:Arial;text-shadow:auto;"&gt;&lt;SPAN style="mso-spacerun:yes;"&gt;&lt;FONT size=2&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/FONT&gt;&lt;/SPAN&gt;&lt;FONT size=2&gt;@id&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;SPAN style="mso-spacerun:yes;"&gt;varchar(32)&lt;/SPAN&gt;, &lt;/FONT&gt;&lt;/SPAN&gt;&lt;/DIV&gt;
&lt;DIV style="mso-line-spacing:'80 20 0';mso-margin-left-alt:216;mso-char-wrap:1;mso-kinsoku-overflow:1;"&gt;&lt;SPAN style="FONT-FAMILY:Courier New;mso-ascii-font-family:Garamond;mso-bidi-font-family:Arial;text-shadow:auto;"&gt;&lt;SPAN style="mso-spacerun:yes;"&gt;&lt;FONT size=2&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/FONT&gt;&lt;/SPAN&gt;&lt;FONT size=2&gt;@param1&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; varchar(128),&lt;/FONT&gt;&lt;SPAN style="mso-spacerun:yes;"&gt;&lt;FONT size=2&gt;&amp;nbsp;&amp;nbsp; &lt;/FONT&gt;&lt;/SPAN&gt;&lt;/SPAN&gt;&lt;/DIV&gt;
&lt;DIV style="mso-line-spacing:'80 20 0';mso-margin-left-alt:216;mso-char-wrap:1;mso-kinsoku-overflow:1;"&gt;&lt;SPAN style="FONT-FAMILY:Courier New;mso-ascii-font-family:Garamond;mso-bidi-font-family:Arial;text-shadow:auto;"&gt;&lt;SPAN style="mso-spacerun:yes;"&gt;&lt;FONT size=2&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/FONT&gt;&lt;/SPAN&gt;&lt;FONT size=2&gt;{ other parameters } &lt;/FONT&gt;&lt;/SPAN&gt;&lt;/DIV&gt;
&lt;DIV style="mso-line-spacing:'80 20 0';mso-margin-left-alt:216;mso-char-wrap:1;mso-kinsoku-overflow:1;"&gt;&lt;SPAN style="FONT-FAMILY:Courier New;mso-ascii-font-family:Garamond;mso-bidi-font-family:Arial;text-shadow:auto;"&gt;&lt;FONT size=2&gt;AS &lt;/FONT&gt;&lt;/SPAN&gt;&lt;/DIV&gt;
&lt;DIV style="mso-line-spacing:'80 20 0';mso-margin-left-alt:216;mso-char-wrap:1;mso-kinsoku-overflow:1;"&gt;&lt;SPAN style="FONT-FAMILY:Courier New;mso-ascii-font-family:Garamond;mso-bidi-font-family:Arial;text-shadow:auto;"&gt;&lt;FONT size=2&gt;&amp;nbsp;&amp;nbsp; SET NOCOUNT ON &lt;/FONT&gt;&lt;/SPAN&gt;&lt;/DIV&gt;
&lt;DIV style="mso-line-spacing:'80 20 0';mso-margin-left-alt:216;mso-char-wrap:1;mso-kinsoku-overflow:1;"&gt;&lt;SPAN style="FONT-FAMILY:Courier New;mso-ascii-font-family:Garamond;mso-bidi-font-family:Arial;text-shadow:auto;"&gt;&lt;FONT size=2&gt;&amp;nbsp;&amp;nbsp; DECLARE @hash int &lt;/FONT&gt;&lt;/SPAN&gt;&lt;/DIV&gt;
&lt;DIV style="mso-line-spacing:'80 20 0';mso-margin-left-alt:216;mso-char-wrap:1;mso-kinsoku-overflow:1;"&gt;&lt;SPAN style="FONT-FAMILY:Courier New;mso-ascii-font-family:Garamond;mso-bidi-font-family:Arial;text-shadow:auto;"&gt;&lt;FONT size=2&gt;&amp;nbsp;&amp;nbsp; SET @hash = (abs(checksum(@id)) % 20) + 1 &lt;BR&gt;&amp;nbsp;&lt;/FONT&gt;&lt;/SPAN&gt;&lt;/DIV&gt;
&lt;DIV style="mso-line-spacing:'80 20 0';mso-margin-left-alt:216;mso-char-wrap:1;mso-kinsoku-overflow:1;"&gt;&lt;SPAN style="FONT-FAMILY:Courier New;mso-ascii-font-family:Garamond;mso-bidi-font-family:Arial;text-shadow:auto;"&gt;&lt;SPAN style="mso-spacerun:yes;"&gt;&lt;FONT size=2&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/FONT&gt;&lt;/SPAN&gt;&lt;FONT size=2&gt;IF (@hash = 1) &lt;/FONT&gt;&lt;/SPAN&gt;&lt;/DIV&gt;
&lt;DIV style="mso-line-spacing:'80 20 0';mso-margin-left-alt:216;mso-char-wrap:1;mso-kinsoku-overflow:1;"&gt;&lt;SPAN style="FONT-FAMILY:Courier New;mso-ascii-font-family:Garamond;mso-bidi-font-family:Arial;text-shadow:auto;"&gt;&lt;SPAN style="mso-spacerun:yes;"&gt;&lt;FONT size=2&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/FONT&gt;&lt;/SPAN&gt;&lt;FONT size=2&gt;EXECUTE sessionDB1.dbo.ExecCommand &lt;/FONT&gt;&lt;/SPAN&gt;&lt;/DIV&gt;
&lt;DIV style="mso-line-spacing:'80 20 0';mso-margin-left-alt:216;mso-char-wrap:1;mso-kinsoku-overflow:1;"&gt;&lt;SPAN style="FONT-FAMILY:Courier New;mso-ascii-font-family:Garamond;mso-bidi-font-family:Arial;text-shadow:auto;"&gt;&lt;SPAN style="mso-spacerun:yes;"&gt;&lt;FONT size=2&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/FONT&gt;&lt;/SPAN&gt;&lt;FONT size=2&gt;@id, &lt;/FONT&gt;&lt;/SPAN&gt;&lt;/DIV&gt;
&lt;DIV style="mso-line-spacing:'80 20 0';mso-margin-left-alt:216;mso-char-wrap:1;mso-kinsoku-overflow:1;"&gt;&lt;SPAN style="FONT-FAMILY:Courier New;mso-ascii-font-family:Garamond;mso-bidi-font-family:Arial;text-shadow:auto;"&gt;&lt;SPAN style="mso-spacerun:yes;"&gt;&lt;FONT size=2&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/FONT&gt;&lt;/SPAN&gt;&lt;FONT size=2&gt;@param1, &lt;/FONT&gt;&lt;/SPAN&gt;&lt;/DIV&gt;
&lt;DIV style="mso-line-spacing:'80 20 0';mso-margin-left-alt:216;mso-char-wrap:1;mso-kinsoku-overflow:1;"&gt;&lt;SPAN style="FONT-FAMILY:Courier New;mso-ascii-font-family:Garamond;mso-bidi-font-family:Arial;text-shadow:auto;"&gt;&lt;SPAN style="mso-spacerun:yes;"&gt;&lt;FONT size=2&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/FONT&gt;&lt;/SPAN&gt;&lt;FONT size=2&gt;{ other parameters go here } &lt;/FONT&gt;&lt;/SPAN&gt;&lt;/DIV&gt;
&lt;DIV style="mso-line-spacing:'80 20 0';mso-margin-left-alt:216;mso-char-wrap:1;mso-kinsoku-overflow:1;"&gt;
&lt;DIV class=O&gt;
&lt;DIV style="mso-line-spacing:'80 20 0';mso-margin-left-alt:216;mso-char-wrap:1;mso-kinsoku-overflow:1;"&gt;&lt;SPAN style="FONT-FAMILY:Courier New;mso-ascii-font-family:Garamond;mso-bidi-font-family:Arial;text-shadow:auto;mso-spacerun:yes;"&gt;&lt;FONT size=2&gt;&amp;nbsp;&amp;nbsp; ELSE &lt;/FONT&gt;&lt;/SPAN&gt;&lt;/DIV&gt;
&lt;DIV style="mso-line-spacing:'80 20 0';mso-margin-left-alt:216;mso-char-wrap:1;mso-kinsoku-overflow:1;"&gt;&lt;SPAN style="FONT-FAMILY:Courier New;mso-ascii-font-family:Garamond;mso-bidi-font-family:Arial;text-shadow:auto;mso-spacerun:yes;"&gt;&lt;FONT size=2&gt;&lt;SPAN style="FONT-FAMILY:Courier New;mso-ascii-font-family:Garamond;mso-bidi-font-family:Arial;text-shadow:auto;mso-spacerun:yes;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/SPAN&gt;IF (@hash = 2) &lt;/FONT&gt;&lt;/SPAN&gt;&lt;/DIV&gt;
&lt;DIV style="mso-line-spacing:'80 20 0';mso-margin-left-alt:216;mso-char-wrap:1;mso-kinsoku-overflow:1;"&gt;&lt;SPAN style="FONT-FAMILY:Courier New;mso-ascii-font-family:Garamond;mso-bidi-font-family:Arial;text-shadow:auto;mso-spacerun:yes;"&gt;&lt;FONT size=2&gt;&lt;SPAN style="FONT-FAMILY:Courier New;mso-ascii-font-family:Garamond;mso-bidi-font-family:Arial;text-shadow:auto;mso-spacerun:yes;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/SPAN&gt;EXECUTE sessionDB2.dbo.ExecCommand &lt;/FONT&gt;&lt;/SPAN&gt;&lt;/DIV&gt;
&lt;DIV style="mso-line-spacing:'80 20 0';mso-margin-left-alt:216;mso-char-wrap:1;mso-kinsoku-overflow:1;"&gt;&lt;SPAN style="FONT-FAMILY:Courier New;mso-ascii-font-family:Garamond;mso-bidi-font-family:Arial;text-shadow:auto;mso-spacerun:yes;"&gt;&lt;FONT size=2&gt;&lt;SPAN style="FONT-FAMILY:Courier New;mso-ascii-font-family:Garamond;mso-bidi-font-family:Arial;text-shadow:auto;mso-spacerun:yes;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/SPAN&gt;@id, &lt;/FONT&gt;&lt;/SPAN&gt;&lt;/DIV&gt;
&lt;DIV style="mso-line-spacing:'80 20 0';mso-margin-left-alt:216;mso-char-wrap:1;mso-kinsoku-overflow:1;"&gt;&lt;SPAN style="FONT-FAMILY:Courier New;mso-ascii-font-family:Garamond;mso-bidi-font-family:Arial;text-shadow:auto;mso-spacerun:yes;"&gt;&lt;FONT size=2&gt;&lt;SPAN style="FONT-FAMILY:Courier New;mso-ascii-font-family:Garamond;mso-bidi-font-family:Arial;text-shadow:auto;mso-spacerun:yes;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/SPAN&gt;@param1, &lt;/FONT&gt;&lt;/SPAN&gt;&lt;/DIV&gt;
&lt;DIV style="mso-line-spacing:'80 20 0';mso-margin-left-alt:216;mso-char-wrap:1;mso-kinsoku-overflow:1;"&gt;&lt;SPAN style="FONT-FAMILY:Courier New;mso-ascii-font-family:Garamond;mso-bidi-font-family:Arial;text-shadow:auto;mso-spacerun:yes;"&gt;&lt;FONT size=2&gt;&lt;SPAN style="FONT-FAMILY:Courier New;mso-ascii-font-family:Garamond;mso-bidi-font-family:Arial;text-shadow:auto;mso-spacerun:yes;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; { other parameters go here }&lt;/SPAN&gt; &lt;/FONT&gt;&lt;/SPAN&gt;&lt;/DIV&gt;
&lt;DIV style="mso-line-spacing:'80 20 0';mso-margin-left-alt:216;mso-char-wrap:1;mso-kinsoku-overflow:1;"&gt;&lt;SPAN style="FONT-FAMILY:Courier New;mso-ascii-font-family:Garamond;mso-bidi-font-family:Arial;text-shadow:auto;mso-spacerun:yes;"&gt;&lt;FONT size=2&gt;&lt;SPAN style="FONT-FAMILY:Courier New;mso-ascii-font-family:Garamond;mso-bidi-font-family:Arial;text-shadow:auto;mso-spacerun:yes;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/SPAN&gt;ELSE &lt;BR&gt;&amp;nbsp;&lt;/FONT&gt;&lt;/SPAN&gt;&lt;/DIV&gt;
&lt;DIV style="mso-line-spacing:'80 20 0';mso-margin-left-alt:216;mso-char-wrap:1;mso-kinsoku-overflow:1;"&gt;&lt;SPAN style="FONT-FAMILY:Courier New;mso-ascii-font-family:Garamond;mso-bidi-font-family:Arial;text-shadow:auto;mso-spacerun:yes;"&gt;&lt;FONT size=2&gt;&lt;SPAN style="FONT-FAMILY:Courier New;mso-ascii-font-family:Garamond;mso-bidi-font-family:Arial;text-shadow:auto;mso-spacerun:yes;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; ...&amp;nbsp; &lt;/SPAN&gt;-- other @hash values &lt;BR&gt;&amp;nbsp;&lt;/FONT&gt;&lt;/SPAN&gt;&lt;/DIV&gt;
&lt;DIV style="mso-line-spacing:'80 20 0';mso-margin-left-alt:216;mso-char-wrap:1;mso-kinsoku-overflow:1;"&gt;
&lt;DIV style="mso-line-spacing:'80 20 0';mso-margin-left-alt:216;mso-char-wrap:1;mso-kinsoku-overflow:1;"&gt;&lt;SPAN style="FONT-FAMILY:Courier New;mso-ascii-font-family:Garamond;mso-bidi-font-family:Arial;text-shadow:auto;mso-spacerun:yes;"&gt;&lt;FONT size=2&gt;&amp;nbsp;&amp;nbsp; ELSE &lt;/FONT&gt;&lt;/SPAN&gt;&lt;/DIV&gt;
&lt;DIV style="mso-line-spacing:'80 20 0';mso-margin-left-alt:216;mso-char-wrap:1;mso-kinsoku-overflow:1;"&gt;&lt;SPAN style="FONT-FAMILY:Courier New;mso-ascii-font-family:Garamond;mso-bidi-font-family:Arial;text-shadow:auto;mso-spacerun:yes;"&gt;&lt;FONT size=2&gt;&lt;SPAN style="FONT-FAMILY:Courier New;mso-ascii-font-family:Garamond;mso-bidi-font-family:Arial;text-shadow:auto;mso-spacerun:yes;"&gt;&amp;nbsp;&amp;nbsp; &lt;/SPAN&gt;IF (@hash = 20) &lt;/FONT&gt;&lt;/SPAN&gt;&lt;/DIV&gt;
&lt;DIV style="mso-line-spacing:'80 20 0';mso-margin-left-alt:216;mso-char-wrap:1;mso-kinsoku-overflow:1;"&gt;&lt;SPAN style="FONT-FAMILY:Courier New;mso-ascii-font-family:Garamond;mso-bidi-font-family:Arial;text-shadow:auto;mso-spacerun:yes;"&gt;&lt;FONT size=2&gt;&lt;SPAN style="FONT-FAMILY:Courier New;mso-ascii-font-family:Garamond;mso-bidi-font-family:Arial;text-shadow:auto;mso-spacerun:yes;"&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &lt;/SPAN&gt;EXECUTE sessionDB20.dbo.ExecCommand &lt;/FONT&gt;&lt;/SPAN&gt;&lt;/DIV&gt;
&lt;DIV style="mso-line-spacing:'80 20 0';mso-margin-left-alt:216;mso-char-wrap:1;mso-kinsoku-overflow:1;"&gt;&lt;SPAN style="FONT-FAMILY:Courier New;mso-ascii-font-family:Garamond;mso-bidi-font-family:Arial;text-shadow:auto;mso-spacerun:yes;"&gt;&lt;FONT size=2&gt;&lt;SPAN style="FONT-FAMILY:Courier New;mso-ascii-font-family:Garamond;mso-bidi-font-family:Arial;text-shadow:auto;mso-spacerun:yes;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &lt;/SPAN&gt;@id, &lt;/FONT&gt;&lt;/SPAN&gt;&lt;/DIV&gt;
&lt;DIV style="mso-line-spacing:'80 20 0';mso-margin-left-alt:216;mso-char-wrap:1;mso-kinsoku-overflow:1;"&gt;&lt;SPAN style="FONT-FAMILY:Courier New;mso-ascii-font-family:Garamond;mso-bidi-font-family:Arial;text-shadow:auto;mso-spacerun:yes;"&gt;&lt;FONT size=2&gt;&lt;SPAN style="FONT-FAMILY:Courier New;mso-ascii-font-family:Garamond;mso-bidi-font-family:Arial;text-shadow:auto;mso-spacerun:yes;"&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/SPAN&gt;@param1, &lt;/FONT&gt;&lt;/SPAN&gt;&lt;/DIV&gt;
&lt;DIV style="mso-line-spacing:'80 20 0';mso-margin-left-alt:216;mso-char-wrap:1;mso-kinsoku-overflow:1;"&gt;&lt;SPAN style="FONT-FAMILY:Courier New;mso-ascii-font-family:Garamond;mso-bidi-font-family:Arial;text-shadow:auto;mso-spacerun:yes;"&gt;&lt;FONT size=2&gt;&lt;SPAN style="FONT-FAMILY:Courier New;mso-ascii-font-family:Garamond;mso-bidi-font-family:Arial;text-shadow:auto;mso-spacerun:yes;"&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; { other parameters go here }&lt;/SPAN&gt; &lt;/FONT&gt;&lt;/SPAN&gt;&lt;/DIV&gt;&lt;SPAN style="FONT-FAMILY:Courier New;mso-ascii-font-family:Garamond;mso-bidi-font-family:Arial;text-shadow:auto;mso-spacerun:yes;"&gt;&lt;FONT size=2&gt;&lt;SPAN style="FONT-FAMILY:Courier New;mso-ascii-font-family:Garamond;mso-bidi-font-family:Arial;text-shadow:auto;mso-spacerun:yes;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/SPAN&gt;ELSE &lt;/FONT&gt;&lt;/SPAN&gt;&lt;/DIV&gt;
&lt;DIV style="mso-line-spacing:'80 20 0';mso-margin-left-alt:216;mso-char-wrap:1;mso-kinsoku-overflow:1;"&gt;&lt;SPAN style="FONT-FAMILY:Courier New;mso-ascii-font-family:Garamond;mso-bidi-font-family:Arial;text-shadow:auto;mso-spacerun:yes;"&gt;&lt;FONT size=2&gt;&lt;SPAN style="FONT-FAMILY:Courier New;mso-ascii-font-family:Garamond;mso-bidi-font-family:Arial;text-shadow:auto;mso-spacerun:yes;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/SPAN&gt;-- Should never reach here &lt;/FONT&gt;&lt;/SPAN&gt;&lt;/DIV&gt;
&lt;DIV style="mso-line-spacing:'80 20 0';mso-margin-left-alt:216;mso-char-wrap:1;mso-kinsoku-overflow:1;"&gt;&lt;SPAN style="FONT-FAMILY:Courier New;mso-ascii-font-family:Garamond;mso-bidi-font-family:Arial;text-shadow:auto;mso-spacerun:yes;"&gt;&lt;FONT size=2&gt;&lt;SPAN style="FONT-FAMILY:Courier New;mso-ascii-font-family:Garamond;mso-bidi-font-family:Arial;text-shadow:auto;mso-spacerun:yes;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/SPAN&gt;RAISERROR('Invalid code path for session&amp;nbsp;id %s and hash %d', 17, -1, @id, @hash) &lt;/FONT&gt;&lt;/SPAN&gt;&lt;/DIV&gt;
&lt;DIV style="mso-line-spacing:'80 20 0';mso-margin-left-alt:216;mso-char-wrap:1;mso-kinsoku-overflow:1;"&gt;&lt;SPAN style="FONT-FAMILY:Courier New;mso-ascii-font-family:Garamond;mso-bidi-font-family:Arial;text-shadow:auto;mso-spacerun:yes;"&gt;&lt;FONT size=2&gt;RETURN&lt;/FONT&gt;&lt;/SPAN&gt;&lt;/DIV&gt;&lt;/DIV&gt;&lt;/DIV&gt;&lt;/DIV&gt;&lt;/DIV&gt;
&lt;P&gt;The hash function in the above code snippet is &lt;FONT face="Courier New"&gt;(abs(checksum(@id)) % 20) + 1&lt;/FONT&gt;. This function maps a GUID to an integer between 1 and 20, inclusive, and the mapping is relatively evenly distributed.&lt;/P&gt;
&lt;P&gt;So how effective was this hash partition strategy? I ran the same stress load test after the hash partition was applied with everything else being the same. The following chart shows the performance difference:&lt;/P&gt;
&lt;P&gt;&lt;IMG src="http://sqlblog.com/blogs/linchi_shea/attachment/616.ashx"&gt; &lt;/P&gt;
&lt;P&gt;With the original single-table design, the chart shows that after 50 simulated users the database was stressed out, and the transaction throughput actually started to drop significantly as even more users were added. However, when the table was hash partitioned into 20 databases, it took more than 200 simulated users to saturate the databases. For 100 users, the transaction throughput improved by about 50%, and for 200 users, the throughput improved by about 65%. &lt;/P&gt;
&lt;P&gt;Not bad for a simple backend change that does not require any change on the application side!&lt;/P&gt;</description></item><item><title>32-bit vs. x64</title><link>http://sqlblog.com/blogs/linchi_shea/archive/2007/01/02/32-bit-vs-x64.aspx</link><pubDate>Tue, 02 Jan 2007 16:02:00 GMT</pubDate><guid isPermaLink="false">21093a07-8b3d-42db-8cbf-3350fcbf5496:490</guid><dc:creator>Linchi Shea</dc:creator><description>&lt;P&gt;When it comes to the issue of 32-bit vs. x64, you can easily walk away from a presentation with an impression that going to x64 will most likely give your SQL Server application a performance boost. I'd like to show you--with some test data--that you should be careful with that impression.&lt;/P&gt;
&lt;P&gt;The following three environments were tested on exactly the same hardware (an HP DL 585 with four dual-core sockets and16GB of physical memory):&lt;/P&gt;
&lt;UL&gt;
&lt;LI&gt;&lt;B&gt;SQL2005 x86x86&lt;/B&gt;. In this test environment, SQL Server 2005 32-bit Enterprise Edition (RTM) runs on Windows Server 2003 32-bit Enterprise Edition SP1.&lt;/LI&gt;
&lt;LI&gt;&lt;B&gt;SQL2005 x86x64&lt;/B&gt;. In this test environment, SQL Server 2005 32-bit Enterprise Edition (RTM) runs on Windows Server 2003 x64 Enterprise Edition SP1.&lt;/LI&gt;
&lt;LI&gt;&lt;B&gt;SQL2005 x64x64&lt;/B&gt;. In this test environment, SQL Server 2005 x64 Enterprise Edition (RTM) runs on Windows Server 2003 x64 Enterprise Edition SP1.&lt;/LI&gt;&lt;/UL&gt;
&lt;P class=MsoNormal&gt;The following four different types of workloads--generated with a custom-built TPC-C-&lt;I&gt;like&lt;/I&gt; test kit--were applied to each of the above three test environments:&lt;/P&gt;
&lt;UL&gt;
&lt;LI&gt;
&lt;P class=MsoNormal&gt;&lt;B&gt;Memory-bound read-only workloads&lt;/B&gt;. The test database was scaled for &lt;A href="http://www.tpc.org/tpcc/spec/tpcc_current.pdf"&gt;100 warehouses&lt;/A&gt; with the database populated at about 8.5GB. 4GB out of the 16GB physical memory was allocated to the SQL Server 2005 instance. Read only stored procedures (doing work similar to the &lt;A href="http://www.tpc.org/tpcc/spec/tpcc_current.pdf"&gt;TPC-C OrderStatus and StockLevel transactions&lt;/A&gt; in a 50/50 mix) were submitted from 200 threads (simulating 200 users) running on a separate computer.&lt;/P&gt;&lt;/LI&gt;
&lt;LI&gt;
&lt;P class=List2A&gt;&lt;B&gt;In-memory read-only workloads&lt;/B&gt;. The test database was scaled for 100 warehouses with the database populated at about 8.5GB. 12GB out of the 16GB physical memory was allocated to the SQL Server 2005 instance. Read only stored procedures (doing work similar to the TPC-C OrderStatus and StockLevel transactions in a 50/50 mix) were submitted from 200 threads running on a separate computer.&lt;/P&gt;&lt;/LI&gt;
&lt;LI&gt;
&lt;P class=List2A&gt;&lt;B&gt;I/O-bound read-only workloads&lt;/B&gt;. The test database was scaled for 1000 warehouses with the database populated at about 79GB. 12GB out of the 16GB physical memory was allocated to the SQL Server 2005 instance. Read only stored procedures (doing work similar to the TPC-C OrderStatus and StockLevel transactions in a 50/50 mix) were submitted from 200 threads running on a separate computer.&lt;/P&gt;&lt;/LI&gt;
&lt;LI&gt;
&lt;P class=List2A&gt;&lt;B&gt;I/O-bound read-write workloads&lt;/B&gt;. The test database was scaled for 1000 warehouses with the database populated at about 79GB. 12GB out of the 16GB physical memory was allocated to the SQL Server instance. Both read-only and read-write stored procedures (all five TPC-C transactions--NewOrder, OrderStatus, Payment, StockLevel, and Delivery--in the standard mix) were submitted from a workload driver on a separate computer.&lt;/P&gt;&lt;/LI&gt;&lt;/UL&gt;
&lt;DIV style="mso-element:footnote-list;"&gt;The test results are shown in the following four tables. The transaction throughput was measured as the number of StockLevel transactions per second for the read-only workloads and the number of NewOrder transactions per second for the read-write workloads. 
&lt;P&gt;&lt;BR&gt;&lt;B&gt;Table 1. Memory-bound Read-only Workloads&lt;/B&gt;&lt;/P&gt;
&lt;TABLE class="" style="BORDER-COLLAPSE:collapse;"&gt;

&lt;TR&gt;
&lt;TD class="" style="BORDER-RIGHT:1px solid;BORDER-TOP:1px solid;BORDER-LEFT:1px solid;BORDER-BOTTOM:1px solid;"&gt;&lt;B&gt;Environment&lt;/B&gt;&lt;/TD&gt;
&lt;TD class="" style="BORDER-RIGHT:1px solid;BORDER-TOP:1px solid;BORDER-LEFT:1px solid;BORDER-BOTTOM:1px solid;"&gt;&lt;B&gt;Transaction per second (StockLevel)&lt;/B&gt;&lt;/TD&gt;&lt;/TR&gt;
&lt;TR&gt;
&lt;TD class="" style="BORDER-RIGHT:1px solid;BORDER-TOP:1px solid;BORDER-LEFT:1px solid;BORDER-BOTTOM:1px solid;"&gt;SQL2005 x86x86&lt;/TD&gt;
&lt;TD class="" style="BORDER-RIGHT:1px solid;BORDER-TOP:1px solid;BORDER-LEFT:1px solid;BORDER-BOTTOM:1px solid;" align=right&gt;2800&lt;/TD&gt;&lt;/TR&gt;
&lt;TR&gt;
&lt;TD class="" style="BORDER-RIGHT:1px solid;BORDER-TOP:1px solid;BORDER-LEFT:1px solid;BORDER-BOTTOM:1px solid;"&gt;SQL2005 x86x64&lt;/TD&gt;
&lt;TD class="" style="BORDER-RIGHT:1px solid;BORDER-TOP:1px solid;BORDER-LEFT:1px solid;BORDER-BOTTOM:1px solid;" align=right&gt;2700&lt;/TD&gt;&lt;/TR&gt;
&lt;TR&gt;
&lt;TD class="" style="BORDER-RIGHT:1px solid;BORDER-TOP:1px solid;BORDER-LEFT:1px solid;BORDER-BOTTOM:1px solid;"&gt;SQL2005 x64x64&lt;/TD&gt;
&lt;TD class="" style="BORDER-RIGHT:1px solid;BORDER-TOP:1px solid;BORDER-LEFT:1px solid;BORDER-BOTTOM:1px solid;" align=right&gt;2600&lt;/TD&gt;&lt;/TR&gt;&lt;/TABLE&gt;&lt;/DIV&gt;
&lt;P&gt;&lt;B&gt;Table 2. In-memory Read-only Workloads&lt;/B&gt;&lt;/P&gt;
&lt;P&gt;
&lt;TABLE class="" style="BORDER-COLLAPSE:collapse;"&gt;

&lt;TR&gt;
&lt;TD class="" style="BORDER-RIGHT:1px solid;BORDER-TOP:1px solid;BORDER-LEFT:1px solid;BORDER-BOTTOM:1px solid;"&gt;&lt;B&gt;Environment&lt;/B&gt;&lt;/TD&gt;
&lt;TD class="" style="BORDER-RIGHT:1px solid;BORDER-TOP:1px solid;BORDER-LEFT:1px solid;BORDER-BOTTOM:1px solid;"&gt;&lt;B&gt;Transaction per second (StockLevel)&lt;/B&gt;&lt;/TD&gt;&lt;/TR&gt;
&lt;TR&gt;
&lt;TD class="" style="BORDER-RIGHT:1px solid;BORDER-TOP:1px solid;BORDER-LEFT:1px solid;BORDER-BOTTOM:1px solid;"&gt;SQL2005 x86x86&lt;/TD&gt;
&lt;TD class="" style="BORDER-RIGHT:1px solid;BORDER-TOP:1px solid;BORDER-LEFT:1px solid;BORDER-BOTTOM:1px solid;" align=right&gt;3600&lt;/TD&gt;&lt;/TR&gt;
&lt;TR&gt;
&lt;TD class="" style="BORDER-RIGHT:1px solid;BORDER-TOP:1px solid;BORDER-LEFT:1px solid;BORDER-BOTTOM:1px solid;"&gt;SQL2005 x86x64&lt;/TD&gt;
&lt;TD class="" style="BORDER-RIGHT:1px solid;BORDER-TOP:1px solid;BORDER-LEFT:1px solid;BORDER-BOTTOM:1px solid;" align=right&gt;3680&lt;/TD&gt;&lt;/TR&gt;
&lt;TR&gt;
&lt;TD class="" style="BORDER-RIGHT:1px solid;BORDER-TOP:1px solid;BORDER-LEFT:1px solid;BORDER-BOTTOM:1px solid;"&gt;SQL2005 x64x64&lt;/TD&gt;
&lt;TD class="" style="BORDER-RIGHT:1px solid;BORDER-TOP:1px solid;BORDER-LEFT:1px solid;BORDER-BOTTOM:1px solid;" align=right&gt;2800&lt;/TD&gt;&lt;/TR&gt;&lt;/TABLE&gt;&lt;/P&gt;
&lt;P&gt;&lt;B&gt;Table 3. I/O-bound Read-only Workloads&lt;/B&gt;&lt;/P&gt;
&lt;P&gt;
&lt;TABLE class="" style="BORDER-COLLAPSE:collapse;"&gt;

&lt;TR&gt;
&lt;TD class="" style="BORDER-RIGHT:1px solid;BORDER-TOP:1px solid;BORDER-LEFT:1px solid;BORDER-BOTTOM:1px solid;"&gt;&lt;B&gt;Environment&lt;/B&gt;&lt;/TD&gt;
&lt;TD class="" style="BORDER-RIGHT:1px solid;BORDER-TOP:1px solid;BORDER-LEFT:1px solid;BORDER-BOTTOM:1px solid;"&gt;&lt;B&gt;Transaction per second (StockLevel)&lt;/B&gt;&lt;/TD&gt;&lt;/TR&gt;
&lt;TR&gt;
&lt;TD class="" style="BORDER-RIGHT:1px solid;BORDER-TOP:1px solid;BORDER-LEFT:1px solid;BORDER-BOTTOM:1px solid;"&gt;SQL2005 x86x86&lt;/TD&gt;
&lt;TD class="" style="BORDER-RIGHT:1px solid;BORDER-TOP:1px solid;BORDER-LEFT:1px solid;BORDER-BOTTOM:1px solid;" align=right&gt;220&lt;/TD&gt;&lt;/TR&gt;
&lt;TR&gt;
&lt;TD class="" style="BORDER-RIGHT:1px solid;BORDER-TOP:1px solid;BORDER-LEFT:1px solid;BORDER-BOTTOM:1px solid;"&gt;SQL2005 x86x64&lt;/TD&gt;
&lt;TD class="" style="BORDER-RIGHT:1px solid;BORDER-TOP:1px solid;BORDER-LEFT:1px solid;BORDER-BOTTOM:1px solid;" align=right&gt;215&lt;/TD&gt;&lt;/TR&gt;
&lt;TR&gt;
&lt;TD class="" style="BORDER-RIGHT:1px solid;BORDER-TOP:1px solid;BORDER-LEFT:1px solid;BORDER-BOTTOM:1px solid;"&gt;SQL2005 x64x64&lt;/TD&gt;
&lt;TD class="" style="BORDER-RIGHT:1px solid;BORDER-TOP:1px solid;BORDER-LEFT:1px solid;BORDER-BOTTOM:1px solid;" align=right&gt;225&lt;/TD&gt;&lt;/TR&gt;&lt;/TABLE&gt;&lt;/P&gt;
&lt;P&gt;&lt;B&gt;Table 4. I/O-bound Read-write Workloads&lt;/B&gt;&lt;/P&gt;
&lt;P&gt;
&lt;TABLE class="" style="BORDER-COLLAPSE:collapse;"&gt;

&lt;TR&gt;
&lt;TD class="" style="BORDER-RIGHT:1px solid;BORDER-TOP:1px solid;BORDER-LEFT:1px solid;BORDER-BOTTOM:1px solid;"&gt;&lt;B&gt;Environment&lt;/B&gt;&lt;/TD&gt;
&lt;TD class="" style="BORDER-RIGHT:1px solid;BORDER-TOP:1px solid;BORDER-LEFT:1px solid;BORDER-BOTTOM:1px solid;"&gt;&lt;B&gt;Transaction per second (NewOrder)&lt;/B&gt;&lt;/TD&gt;&lt;/TR&gt;
&lt;TR&gt;
&lt;TD class="" style="BORDER-RIGHT:1px solid;BORDER-TOP:1px solid;BORDER-LEFT:1px solid;BORDER-BOTTOM:1px solid;"&gt;SQL2005 x86x86&lt;/TD&gt;
&lt;TD class="" style="BORDER-RIGHT:1px solid;BORDER-TOP:1px solid;BORDER-LEFT:1px solid;BORDER-BOTTOM:1px solid;" align=right&gt;450&lt;/TD&gt;&lt;/TR&gt;
&lt;TR&gt;
&lt;TD class="" style="BORDER-RIGHT:1px solid;BORDER-TOP:1px solid;BORDER-LEFT:1px solid;BORDER-BOTTOM:1px solid;"&gt;SQL2005 x86x64&lt;/TD&gt;
&lt;TD class="" style="BORDER-RIGHT:1px solid;BORDER-TOP:1px solid;BORDER-LEFT:1px solid;BORDER-BOTTOM:1px solid;" align=right&gt;440&lt;/TD&gt;&lt;/TR&gt;
&lt;TR&gt;
&lt;TD class="" style="BORDER-RIGHT:1px solid;BORDER-TOP:1px solid;BORDER-LEFT:1px solid;BORDER-BOTTOM:1px solid;"&gt;SQL2005 x64x64&lt;/TD&gt;
&lt;TD class="" style="BORDER-RIGHT:1px solid;BORDER-TOP:1px solid;BORDER-LEFT:1px solid;BORDER-BOTTOM:1px solid;" align=right&gt;440&lt;/TD&gt;&lt;/TR&gt;&lt;/TABLE&gt;&lt;/P&gt;
&lt;P&gt;The absolute number of transactions per second is not important. Instead, you should focus on their relative values among the three test environments. The test results tell us the following:&lt;/P&gt;
&lt;UL&gt;
&lt;LI&gt;For the in-memory read-only workload, x64 SQL2005 on x64 Win2K3 (i.e. SQL2005 x64x64) achieved about 2800 transactions per second, while both SQL2005 x86x64 and SQL2005 x86x86 reached about 3600, which is 29% better than SQL2005 x64x64.&lt;/LI&gt;
&lt;LI&gt;For the other workloads, there was little or no significant difference in transaction throughput whether it's 32-bit or x64.&lt;/LI&gt;&lt;/UL&gt;
&lt;P&gt;Now, these test results do not suggest in any way or shape that you should not go to x64. But rather, after you walk away from a seminar that touts x64 and before you commit your application to x64, do some tests yourself with your application workload! Of course, you may decide to move to x64, not for any immediate performance gain, but for future growth and scalability. That--coupled with a hardware upgrade--can be a good reason in itself indeed!&lt;/P&gt;</description></item><item><title>Rowset string concatenation: Which method is best?</title><link>http://sqlblog.com/blogs/adam_machanic/archive/2006/07/12/rowset-string-concatenation-which-method-is-best.aspx</link><pubDate>Thu, 13 Jul 2006 01:15:00 GMT</pubDate><guid isPermaLink="false">21093a07-8b3d-42db-8cbf-3350fcbf5496:77</guid><dc:creator>Adam Machanic</dc:creator><description>&lt;p&gt;Yeah, yeah, yeah, let's get this out of the way right from the start:
Don't concatenate rows into delimited strings in SQL Server. Do it
client side.
&lt;/p&gt;&lt;p&gt;
Except if you really have to create delimited strings in SQL Server.  In which case you should read on.
&lt;/p&gt;&lt;p&gt;
There was a little &lt;a href="http://www.sqlteam.com/forums/topic.asp?TOPIC_ID=42415" target="#"&gt;discussion&lt;/a&gt; on &lt;a href="http://www.sqlteam.com/" target="#"&gt;SQLTeam&lt;/a&gt;
about the best way to concatenate. I recommended a scalar UDF solution,
whereas Rob Volk recommended a solution involving a temp table.
&lt;/p&gt;&lt;p&gt;I mentioned my dislike for the temp table solution for a couple
of reasons. First of all, it relies on a clustered index for ordering.
That will probably work in this example, but is not guaranteed to
always work and relying on indexes rather than ORDER BY for ordering is
definitely not a habit I want anyone to get into. The clustered index
as it was described in Rob's example also has another problem that I
didn't even notice until I was writing this entry. But I'll get to that
in a moment. The second reason I dislike the temp table is that I felt
it would be less efficient than the scalar UDF.
&lt;/p&gt;&lt;p&gt;
Rob didn't agree about the efficiency.  And so I set out to prove him wrong...
&lt;/p&gt;&lt;p&gt;
We'll use the Authors table in Pubs.  I want a comma-delimited list, per state, of the last name of each author who lives there.
&lt;/p&gt;&lt;p&gt;
First, the scalar UDF:
&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;pre class="code"&gt;USE pubs&lt;br&gt;GO&lt;br&gt;&lt;br&gt;CREATE FUNCTION dbo.ConcatAuthors(@State CHAR(2))&lt;br&gt;RETURNS VARCHAR(8000)&lt;br&gt;AS&lt;br&gt;BEGIN&lt;br&gt;	DECLARE @Output VARCHAR(8000)&lt;br&gt;	SET @Output = ''&lt;br&gt;&lt;br&gt;	SELECT @Output =	CASE @Output &lt;br&gt;				WHEN '' THEN au_lname &lt;br&gt;				ELSE @Output + ', ' + au_lname &lt;br&gt;				END&lt;br&gt;	FROM Authors&lt;br&gt;	WHERE State = @State&lt;br&gt;	ORDER BY au_lname&lt;br&gt;&lt;br&gt;	RETURN @Output&lt;br&gt;END&lt;br&gt;GO&lt;br&gt;&lt;/pre&gt;
&lt;p&gt;
To find the list I want:
&lt;/p&gt;&lt;pre class="code"&gt;SELECT DISTINCT State, dbo.ConcatAuthors(State)&lt;br&gt;FROM Authors&lt;br&gt;ORDER BY State&lt;br&gt;&lt;/pre&gt;
&lt;p&gt;... And the adaptation of Rob's temp table method... I did change
two things due to problems I discovered during testing. One, I've
altered the au_lname column to VARCHAR(8000); the column in the Authors
table is VARCHAR(40), not large enough for all of the California
authors. What if we were dealing with a much larger dataset? Second, I
added an IDENTITY column, and I'm clustering on that instead of the
actual data to get the ordering. I'm doing so because of the
VARCHAR(8000). Index rows can be a maximum of 900 bytes, so if we had
enough data to exceed that length, this method would fail.
&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;pre class="code"&gt;CREATE TABLE #AuthorConcat&lt;br&gt;(&lt;br&gt;	State CHAR(2) NOT NULL,&lt;br&gt;	au_lname VARCHAR(8000) NOT NULL,&lt;br&gt;	Ident INT IDENTITY(1,1) NOT NULL PRIMARY KEY&lt;br&gt;)&lt;br&gt;&lt;br&gt;INSERT #AuthorConcat &lt;br&gt;(&lt;br&gt;	State,&lt;br&gt;	au_lname&lt;br&gt;)&lt;br&gt;SELECT&lt;br&gt;	State, &lt;br&gt;	au_lname&lt;br&gt;FROM Authors&lt;br&gt;ORDER BY &lt;br&gt;	State, &lt;br&gt;	au_lname&lt;br&gt;&lt;br&gt;DECLARE @Authors VARCHAR(8000)&lt;br&gt;SET @Authors = ''&lt;br&gt;DECLARE @State CHAR(2)&lt;br&gt;SET @State = ''&lt;br&gt;&lt;br&gt;UPDATE #AuthorConcat&lt;br&gt;SET @Authors = au_lname =	CASE &lt;br&gt;				WHEN @State = State THEN @Authors + ', ' + au_lname &lt;br&gt;				ELSE au_lname END,&lt;br&gt;	@State = State&lt;br&gt;&lt;br&gt;SELECT State, MAX(au_lname) &lt;br&gt;FROM #AuthorConcat&lt;br&gt;GROUP BY State&lt;br&gt;&lt;/pre&gt;
&lt;p&gt;Clever, but more complex and harder to read than the scalar UDF
version. Output is identical, but that's not why we're here. Which one
is more efficient?
&lt;/p&gt;&lt;p&gt;
Drumroll, please...
&lt;/p&gt;&lt;p&gt;Results were tabulated using STATISTICS IO, STATISTICS TIME, and
Query Analyzer's Show Execution Plan. DBCC DROPCLEANBUFFERS and DBCC
FREEPROCCACHE were run before each test.
&lt;/p&gt;&lt;p&gt;
&lt;b&gt;Scalar UDF Method&lt;/b&gt;
&lt;br&gt;
Total cost: 0.0492
&lt;br&gt;
Total Scan count: 1
&lt;br&gt;
Total Logical reads: 2
&lt;br&gt;
Total Physical reads: 2
&lt;br&gt;
Total time: 25 ms
&lt;/p&gt;&lt;p&gt;
&lt;b&gt;Temp Table Method&lt;/b&gt;
&lt;br&gt;
Total cost: 0.2131
&lt;br&gt;
Total Scan count: 4
&lt;br&gt;
Total Logical reads: 9
&lt;br&gt;
Total Physical reads: 2
&lt;br&gt;
Total time: 88 ms
&lt;/p&gt;&lt;p&gt;So in conclusion, neither method is incredibly taxing with the
tiny Pubs dataset, but I think I have proven that the UDF is far more
efficient.
&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;hr&gt;Update, February 28, 2005: Modified the adapation of Rob Volk's
method to use a CREATE TABLE instead of SELECT INTO, as the latter is
not necessarily guaranteed to insert rows in the right order for the
sake of this example. Thanks to "PW" on SQLServerCentral for pointing
this problem out. Note that this changed the total costs very slightly
-- for the better -- but the UDF still performs better by quite a large
margin.&lt;br&gt;</description></item></channel></rss>