<?xml version='1.0' encoding='UTF-8'?><?xml-stylesheet href="http://www.blogger.com/styles/atom.css" type="text/css"?><feed xmlns='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/' xmlns:georss='http://www.georss.org/georss' xmlns:gd='http://schemas.google.com/g/2005' xmlns:thr='http://purl.org/syndication/thread/1.0'><id>tag:blogger.com,1999:blog-7516218764458010868</id><updated>2011-11-27T15:46:46.917-08:00</updated><category term='PHP'/><category term='Pivot Query'/><category term='Data Ware House'/><category term='SQL Server 2008'/><category term='Pivot Table'/><category term='SQL Server Tunning'/><category term='Server Tuning'/><category term='DBA Routine'/><category term='SQL Server 2000'/><category term='Replication'/><category term='SQL Server'/><category term='Export / Import'/><category term='Server Monitoring'/><category term='DBA'/><category term='Oracle 11g'/><category term='Firebird'/><category term='more'/><category term='SQL Questions  - Answers'/><category term='Privacy Policy'/><category term='DBA Task'/><category term='Oracle'/><category term='Oracle Installation'/><category term='Maintenance'/><category term='Excel'/><title type='text'>All About Database</title><subtitle type='html'>Database tips and trix, sql server, oracle, mysql, firebird, etc</subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://all-about-database.blogspot.com/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7516218764458010868/posts/default?max-results=100'/><link rel='alternate' type='text/html' href='http://all-about-database.blogspot.com/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><author><name>firman.arrow</name><uri>http://www.blogger.com/profile/14656110271655711161</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>22</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>100</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-7516218764458010868.post-6497815519356571769</id><published>2009-12-27T14:58:00.000-08:00</published><updated>2009-12-27T14:58:12.267-08:00</updated><title type='text'>Bisnis Pulsa Elektrik - Harga Murah Bersahabat</title><content type='html'>"Selamat Datang di Website NataTronik | Natatronik Reloads"&lt;br /&gt;&lt;br /&gt;Type your summary here&lt;br /&gt;&lt;span class="fullpost"&gt;&lt;br /&gt;Type rest of the post here&lt;br /&gt;&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7516218764458010868-6497815519356571769?l=all-about-database.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='related' href='http://natatronik.com/' title='Bisnis Pulsa Elektrik - Harga Murah Bersahabat'/><link rel='replies' type='application/atom+xml' href='http://all-about-database.blogspot.com/feeds/6497815519356571769/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7516218764458010868&amp;postID=6497815519356571769' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7516218764458010868/posts/default/6497815519356571769'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7516218764458010868/posts/default/6497815519356571769'/><link rel='alternate' type='text/html' href='http://all-about-database.blogspot.com/2009/12/bisnis-pulsa-elektrik-harga-murah.html' title='Bisnis Pulsa Elektrik - Harga Murah Bersahabat'/><author><name>firman.arrow</name><uri>http://www.blogger.com/profile/14656110271655711161</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7516218764458010868.post-8925261268148021481</id><published>2008-12-13T01:31:00.000-08:00</published><updated>2008-12-13T01:34:56.076-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Pivot Query'/><category scheme='http://www.blogger.com/atom/ns#' term='more'/><category scheme='http://www.blogger.com/atom/ns#' term='SQL Server 2000'/><category scheme='http://www.blogger.com/atom/ns#' term='Pivot Table'/><title type='text'>Cross Tabs and Pivots, Part 1 – Converting Rows to Columns By Jeff Moden, 2008/08/19</title><content type='html'>Do you get stuck to find the way converting rows to collumns ?? so.. this is the sollutions :&lt;br /&gt;&lt;br /&gt;&lt;h2&gt;Introduction:&lt;/h2&gt;&lt;br /&gt;&lt;p&gt;&lt;em&gt;(This article is dedicated to a good friend and fellow T-SQL warrior, Katrina Wright. We've fought and won many battles together.)&lt;/em&gt;&lt;/p&gt;&lt;br /&gt;&lt;p&gt;I looked for a definition of what a "Cross Tab" actually is and, after a slight modification, couldn't find a better one than what's in SQL Server 2000 Books Online...&lt;/p&gt;&lt;br /&gt;&lt;p&gt;&lt;strong&gt;&lt;em&gt;"Sometimes it is necessary to rotate results so that [the data in] columns are presented horizontally&lt;/em&gt; &lt;em&gt;and [the data in]&lt;/em&gt; &lt;em&gt;rows are&lt;/em&gt; &lt;em&gt;presented vertically. This is known as creating a PivotTable®, creating a&lt;/em&gt; &lt;em&gt;cross-tab report, or rotating&lt;/em&gt; &lt;em&gt;data."&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class="fullpost"&gt;&lt;br /&gt;&lt;p&gt;In other words, you can use a Cross Tab or Pivot to convert or transpose information from rows to columns either for reporting or to convert some special long skinny tables known as EAV's or NVP's into a more typical form data.&lt;/p&gt;&lt;br /&gt;&lt;p&gt;The purpose of this article is to provide an introduction to Cross Tabs and Pivots and how they can be used to "rotate" data...&lt;/p&gt;&lt;br /&gt;&lt;h3&gt;Before you say anything...&lt;/h3&gt;&lt;br /&gt;&lt;p&gt;The reason I'm writing a series of articles on the simple concept of Cross Tabs and Pivots is because of the recent number of requests for this type of information on the SQL Server Central forums... there was a while when not a day went by when two or three such requests were posted each day.&lt;/p&gt;&lt;br /&gt;&lt;p&gt;Also, yes, I aware that a lot of this type of "formatting" should be done in the GUI, reporting tool, or maybe even a Spreadsheet. I'm also aware that using EAV/NVP tables isn't considered to be a "best practice". But, like I said about the number of recent number of posts, folks get forced into a corner by their bosses and, if they have to do such a thing, I thought they could use a little help.&lt;/p&gt;&lt;br /&gt;&lt;h3&gt;Notes of Interest:&lt;/h3&gt;&lt;br /&gt;&lt;p&gt;I wrote all of the example code and data using Temp Tables just to be safe. Sure, I could have used Table Variables, but they don't really allow for people to do partial runs and they don't all people to look and see what's in the Table Variable after each section. Also, some of the data we'll end up using is a wee bit bigger than what I would normally use a table variable for.&lt;/p&gt;&lt;br /&gt;&lt;p&gt;Last but not least, I currently only have SQL Server 2000 and 2005 installed. I indicate which rev each section of code will run on in parenthesis. I'm pretty sure that most of this will work on 2008 and that a good portion of the code for Cross Tabs will also work on 7... but I don't have access to either which means I haven't tested it.&lt;/p&gt;&lt;br /&gt;&lt;p&gt;Also, for your convenience, all of the code has been attached in the "Resources" section near the end of the article.&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;Ok... let's get started...&lt;/p&gt;&lt;br /&gt;&lt;h2&gt;A simple introduction to Cross Tabs:&lt;/h2&gt;&lt;br /&gt;&lt;p&gt;The Cross Tab Report example from Books Online is very simple and easy to understand. I've shamelessly borrowed from it to explain this first shot at a Cross Tab.&lt;/p&gt;&lt;br /&gt;&lt;h3&gt;The Test Data&lt;/h3&gt;&lt;br /&gt;&lt;p&gt;Basically, the table and data looks like this...&lt;/p&gt;&lt;br /&gt;&lt;pre class="inline"&gt;&lt;br&gt;--===== Sample data #1 (#SomeTable1)&lt;br&gt;--===== Create a test table and some data &lt;br&gt; CREATE TABLE #SomeTable1&lt;br&gt;        (&lt;br&gt;        Year    SMALLINT,&lt;br&gt;        Quarter TINYINT, &lt;br&gt;        Amount  DECIMAL(2,1)&lt;br&gt;        )&lt;br&gt;GO&lt;br&gt; INSERT INTO #SomeTable1 &lt;br&gt;        (Year, Quarter, Amount)&lt;br&gt; SELECT 2006, 1, 1.1 UNION ALL&lt;br&gt; SELECT 2006, 2, 1.2 UNION ALL&lt;br&gt; SELECT 2006, 3, 1.3 UNION ALL&lt;br&gt; SELECT 2006, 4, 1.4 UNION ALL&lt;br&gt; SELECT 2007, 1, 2.1 UNION ALL&lt;br&gt; SELECT 2007, 2, 2.2 UNION ALL&lt;br&gt; SELECT 2007, 3, 2.3 UNION ALL&lt;br&gt; SELECT 2007, 4, 2.4 UNION ALL&lt;br&gt; SELECT 2008, 1, 1.5 UNION ALL&lt;br&gt; SELECT 2008, 3, 2.3 UNION ALL&lt;br&gt; SELECT 2008, 4, 1.9&lt;br&gt;GO&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;p&gt;Every row in the code above is unique in that each row contains ALL the information for a given quarter of a given year. Unique data is NOT a requirement for doing Cross Tabs... it just happens to be the condition that the data is in. Also, notice that the 2nd quarter for 2008 is missing.&lt;/p&gt;&lt;br /&gt;&lt;p&gt;The goal is to make the data look more like what you would find in a spreadsheet... 1 row for each year with the amounts laid out in columns for each quarter with a grand total for the year. Kind of like this...&lt;/p&gt;&lt;br /&gt;&lt;p&gt;... and, notice, we've plugged in a "0" for the missing 2nd quarter of 2008.&lt;/p&gt;&lt;br /&gt;&lt;pre class="code"&gt;Year   1st Qtr 2nd Qtr 3rd Qtr 4th Qtr Total &lt;br&gt;------ ------- ------- ------- ------- ----- &lt;br&gt;2006     1.1     1.2     1.3     1.4     5.0&lt;br&gt;2007     2.1     2.2     2.3     2.4     9.0&lt;br&gt;2008     1.5     0.0     2.3     1.9     5.7&lt;br /&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;h3&gt;The KEY to Cross Tabs!&lt;/h3&gt;&lt;br /&gt;&lt;p&gt;Let's start out with the most obvious... we want a Total for each year. This isn't required for Cross Tabs, but it will help demonstrate what the key to making a Cross Tab is.&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;To make the Total, we need to use the SUM aggregate and a GROUP BY... like this...&lt;/p&gt;&lt;br /&gt;&lt;pre class="code"&gt;--===== Simple sum/total for each year&lt;br&gt; SELECT Year,&lt;br&gt;        SUM(Amount) AS Total&lt;br&gt;   FROM #SomeTable1&lt;br&gt;  GROUP BY Year&lt;br&gt;  ORDER BY Year&lt;br /&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;p&gt;And, that returns the following...&lt;/p&gt;&lt;br /&gt;&lt;pre class="code"&gt;Year   Total                                    &lt;br&gt;------ ---------------------------------------- &lt;br&gt;2006   5.0&lt;br&gt;2007   9.0&lt;br&gt;2008   5.7&lt;br /&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;p&gt;Not so difficult and really nothing new there. So, how do we "pivot" the data for the Quarter?&lt;/p&gt;&lt;br /&gt;&lt;p&gt;Let's do this by the numbers...&lt;/p&gt;&lt;br /&gt;&lt;ol&gt;&lt;br /&gt; &lt;li&gt;How many quarters are there per year? Correct, 4.&lt;br&gt;&lt;/li&gt;&lt;br /&gt; &lt;li&gt;How many columns do we need to show the 4 quarters per year? Correct, 4.&lt;br&gt;&lt;/li&gt;&lt;br /&gt; &lt;li&gt;How many times do we need the Quarter column to appear in the SELECT list to make it show up 4 times per year? Correct, 4.&lt;br&gt;&lt;/li&gt;&lt;br /&gt; &lt;li&gt;Now, look at the total column... it gives the GRAND total for each year. What would we have to do to get it to give us, say, the total just for the first quarter for each year? Correct... we need a CASE statement inside the SUM.&lt;/li&gt;&lt;br /&gt;&lt;br /&gt;&lt;/ol&gt;&lt;br /&gt;&lt;p&gt;Number 4 above is the KEY to doing this Cross Tab... It should be a SUM and it MUST have a CASE to identify the quarter even though each quarter only has 1 value. Yes, if each quarter had more than 1 value, this would still work! If any given quarter is missing, a zero will be substituted.&lt;/p&gt;&lt;br /&gt;&lt;p&gt;To emphasize, &lt;strong&gt;each column for each quarter is just like the Total column, but it has a CASE statement to trap info only for the correct data for each quarter's column.&lt;/strong&gt; Here's the code...&lt;/p&gt;&lt;br /&gt;&lt;pre class="code"&gt;--===== Each quarter is just like the total except it has a CASE&lt;br&gt;     -- statement to isolate the amount for each quarter.&lt;br&gt; SELECT Year,&lt;br&gt;        SUM(CASE WHEN Quarter = 1 THEN Amount ELSE 0 END) AS [1st Qtr],&lt;br&gt;        SUM(CASE WHEN Quarter = 2 THEN Amount ELSE 0 END) AS [2nd Qtr],&lt;br&gt;        SUM(CASE WHEN Quarter = 3 THEN Amount ELSE 0 END) AS [3rd Qtr],&lt;br&gt;        SUM(CASE WHEN Quarter = 4 THEN Amount ELSE 0 END) AS [4th Qtr],&lt;br&gt;        SUM(Amount) AS Total&lt;br&gt;   FROM #SomeTable1&lt;br&gt;  GROUP BY Year&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;p&gt;... and that gives us the following result in the text mode (modified so it will fit here)...&lt;/p&gt;&lt;br /&gt;&lt;pre class="code"&gt;Year   1st Qtr 2nd Qtr 3rd Qtr 4th Qtr Total&lt;br&gt;------ ------- ------- ------- ------- -----&lt;br&gt;2006   1.1     1.2     1.3     1.4     5.0&lt;br&gt;2007   2.1     2.2     2.3     2.4     9.0&lt;br&gt;2008   1.5     .0      2.3     1.9     5.7&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;p&gt;Also notice... because there is only one value for each quarter, we could have gotten away with using MAX instead of SUM. Go ahead... try it. We'll use a similar method for normalizing an EAV table in the future.&lt;/p&gt;&lt;br /&gt;&lt;p&gt;For most applications, that's good enough. If it's supposed to represent the final output, we might want to make it a little prettier. The STR function inherently right justifies, so we can use that to make the output a little prettier. Please, no hate mail here! I'll be one of the first that formatting of this nature is supposed to be done in the GUI!&lt;/p&gt;&lt;br /&gt;&lt;pre class="code"&gt;--===== We can use the STR function to right justify data and make it prettier.&lt;br&gt;     -- Note that this should really be done by the GUI or Reporting Tool and&lt;br&gt;     -- not in T-SQL&lt;br&gt; SELECT Year,&lt;br&gt;        STR(SUM(CASE WHEN Quarter = 1 THEN Amount ELSE 0 END),5,1) AS [1st Qtr],&lt;br&gt;        STR(SUM(CASE WHEN Quarter = 2 THEN Amount ELSE 0 END),5,1) AS [2nd Qtr],&lt;br&gt;        STR(SUM(CASE WHEN Quarter = 3 THEN Amount ELSE 0 END),5,1) AS [3rd Qtr],&lt;br&gt;        STR(SUM(CASE WHEN Quarter = 4 THEN Amount ELSE 0 END),5,1) AS [4th Qtr],&lt;br&gt;        STR(SUM(Amount),5,1) AS Total&lt;br&gt;   FROM #SomeTable1&lt;br&gt;  GROUP BY Year &lt;br /&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;p&gt;The code above gives us the final result we were looking for...&lt;/p&gt;&lt;br /&gt;&lt;pre class="code"&gt;Year   1st Qtr 2nd Qtr 3rd Qtr 4th Qtr Total &lt;br&gt;------ ------- ------- ------- ------- ----- &lt;br&gt;2006     1.1     1.2     1.3     1.4     5.0&lt;br&gt;2007     2.1     2.2     2.3     2.4     9.0&lt;br&gt;2008     1.5     0.0     2.3     1.9     5.7&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;p&gt;&lt;strong&gt;Just to emphasize what the very simple KEY to making a Cross Tab is... it's just like making a Total using SUM and Group By, but we've added a CASE statement to isolate the data for each Quarter.&lt;/strong&gt;&lt;/p&gt;&lt;br /&gt;&lt;h2&gt;A simple introduction to Pivots:&lt;/h2&gt;&lt;br /&gt;&lt;p&gt;Microsoft introduced the PIVOT function in SQL Server 2005. It works about the same (has some limitations) as a Cross Tab. Using the same test table we used in the Cross Tab examples above, let's see how to use PIVOT to do the same thing...&lt;/p&gt;&lt;br /&gt;&lt;pre class="code"&gt;--===== Use a Pivot to do the same thing we did with the Cross Tab&lt;br&gt; SELECT Year,             --(4)&lt;br&gt;        [1] AS [1st Qtr], --(3)&lt;br&gt;        [2] AS [2nd Qtr],&lt;br&gt;        [3] AS [3rd Qtr],&lt;br&gt;        [4] AS [4th Qtr],&lt;br&gt;        [1]+[2]+[3]+[4] AS Total --(5)&lt;br&gt;   FROM (SELECT Year, Quarter,Amount FROM #SomeTable1)  AS src --(1)&lt;br&gt;  PIVOT (SUM(Amount) FOR Quarter IN ([1],[2],[3],[4])) AS pvt --(2)&lt;br&gt;  ORDER BY Year&lt;br /&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;p&gt;Ok... let's break that code down and figure out what each part does... the items below have numbers in the code above so you can more easily see what's going on...&lt;/p&gt;&lt;br /&gt;&lt;ol&gt;&lt;br /&gt; &lt;li&gt;The FROM clause is actually a derived table. It very simply contains the columns that we want to use in the cross tab from the source table we want to use the pivot on. It will sometimes work as a normal FROM clause with just the table listed instead of a derived table, but most of the time it will not and is unpredictable when it does work.&lt;br&gt;&lt;/li&gt;&lt;br /&gt; &lt;li&gt;This is the "Pivot" line. It identifies the aggregate to be used, the column to pivot in the FOR clause, and the list of values that we want to pivot in the IN clause... in this case, the quarter number. Also notice that you must treat those as if they were column names. They must either be put in brackets or double quotes (if the quoted identifier setting is ON).&lt;br&gt;&lt;/li&gt;&lt;br /&gt; &lt;li&gt;This is the pivoted SELECT list. Notice that you have to bring everything in the IN clause from (2) up to the SELECT list. Aliasing the column names is optional but usually a good thing to do just to make the output obvious.&lt;br&gt;&lt;/li&gt;&lt;br /&gt; &lt;li&gt;You must also bring Year up as the row identifier in the pivot. Think of this as your "anchor" for the rows.&lt;br&gt;&lt;/li&gt;&lt;br /&gt; &lt;li&gt;Last but not least, if you want a total for each row in the pivot, you can no longer use just an aggregate. Instead, you must add all the columns together.&lt;/li&gt;&lt;br /&gt;&lt;br /&gt;&lt;/ol&gt;&lt;br /&gt;&lt;p&gt;When you run the code, you get this...&lt;/p&gt;&lt;br /&gt;&lt;pre class="code"&gt;Year   1st Qtr 2nd Qtr 3rd Qtr 4th Qtr Total&lt;br&gt;------ ------- ------- ------- ------- -----&lt;br&gt;2006   1.1     1.2     1.3     1.4     5.0&lt;br&gt;2007   2.1     2.2     2.3     2.4     9.0&lt;br&gt;2008   1.5     NULL    2.3     1.9     NULL&lt;br&gt;&lt;/pre&gt;&lt;br /&gt;&lt;p&gt;Notice the NULL's where there are no values or where a NULL has been added into a total. Remember that anything plus a NULL is still a NULL. All of this occurs because the Pivot doesn't do any substitutions like the Case statements we used in the Cross Tab. To fix this little problem, we have to use COALESCE (or ISNULL) on the columns... every bloody column! So, you end up with code that looks like this...&lt;/p&gt;&lt;br /&gt;&lt;pre class="code"&gt;&lt;br /&gt;--===== Converting NULLs to zero's in the Pivot using COALESCE&lt;br&gt; SELECT Year, &lt;br&gt;        COALESCE([1],0) AS [1st Qtr],&lt;br&gt;        COALESCE([2],0) AS [2nd Qtr],&lt;br&gt;        COALESCE([3],0) AS [3rd Qtr],&lt;br&gt;        COALESCE([4],0) AS [4th Qtr],&lt;br&gt;        COALESCE([1],0) + COALESCE([2] ,0) + COALESCE([3],0) + COALESCE([4],0) AS Total&lt;br&gt;   FROM (SELECT Year, Quarter,Amount FROM #SomeTable1)  AS src &lt;br&gt;  PIVOT (SUM(Amount) FOR Quarter IN ([1],[2],[3],[4])) AS pvt &lt;br&gt;  ORDER BY Year&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;p&gt;That &lt;strong&gt;&lt;em&gt;finally&lt;/em&gt;&lt;/strong&gt; gives us the same result as a Cross Tab sans any right hand justification... again, you'd need to add the STR function to the code to do that.&lt;/p&gt;&lt;br /&gt;&lt;pre class="code"&gt;Year   1st Qtr 2nd Qtr 3rd Qtr 4th Qtr Total&lt;br&gt;------ ------- ------- ------- ------- -----&lt;br&gt;2006   1.1     1.2     1.3     1.4     5.0&lt;br&gt;2007   2.1     2.2     2.3     2.4     9.0&lt;br&gt;2008   1.5     .0      2.3     1.9     5.7&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;h2&gt;Readability Comparison&lt;/h2&gt;&lt;br /&gt;&lt;p&gt;Just for grins, here are both the Cross Tab and the Pivot code real close together so that you can do a comparison...&lt;/p&gt;&lt;br /&gt;&lt;pre class="code"&gt;--===== The Cross Tab example &lt;br&gt; SELECT Year,&lt;br&gt;        SUM(CASE WHEN Quarter = 1 THEN Amount ELSE 0 END) AS [1st Qtr],&lt;br&gt;        SUM(CASE WHEN Quarter = 2 THEN Amount ELSE 0 END) AS [2nd Qtr],&lt;br&gt;        SUM(CASE WHEN Quarter = 3 THEN Amount ELSE 0 END) AS [3rd Qtr],&lt;br&gt;        SUM(CASE WHEN Quarter = 4 THEN Amount ELSE 0 END) AS [4th Qtr],&lt;br&gt;        SUM(Amount) AS Total&lt;br&gt;   FROM #SomeTable1&lt;br&gt;  GROUP BY Year&lt;br /&gt;&lt;br /&gt;--===== The Pivot Example&lt;br&gt; SELECT Year, &lt;br&gt;        COALESCE([1],0) AS [1st Qtr],&lt;br&gt;        COALESCE([2],0) AS [2nd Qtr],&lt;br&gt;        COALESCE([3],0) AS [3rd Qtr],&lt;br&gt;        COALESCE([4],0) AS [4th Qtr],&lt;br&gt;        COALESCE([1],0) + COALESCE([2] ,0) + COALESCE([3],0) + COALESCE([4],0) AS Total&lt;br&gt;   FROM (SELECT Year, Quarter,Amount FROM #SomeTable1)  AS src &lt;br&gt;  PIVOT (SUM(Amount) FOR Quarter IN ([1],[2],[3],[4])) AS pvt &lt;br&gt;  ORDER BY Year&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;p&gt;I'm sure that you'll have a preference, but I like the Cross Tab code better for two reasons... the Cross Tab code is simpler, in my eyes... all I have to remember how to do are those very simple Case statements, I only have to list the values of the pivot columns once, and I don't have to use COALESCE anywhere. The second reason is how simple it is to do a row total.&lt;/p&gt;&lt;br /&gt;&lt;p&gt;There's actually several other reasons and one of them is performance. We'll get to performance later, but first let's talk about...&lt;/p&gt;&lt;br /&gt;&lt;h2&gt;Multiple Aggregations In a Cross Tab (or, "The Problem with Pivots")&lt;/h2&gt;&lt;br /&gt;&lt;p&gt;We're going to do this section backwards from what we've been doing... we're going to cover how to Pivot multiple aggregations before we cover the equivalent Cross Tab.&lt;/p&gt;&lt;br /&gt;&lt;p&gt;A "multiple aggregation Pivot" is just that... we want to show two different aggregates in the Pivot something like this (notice both the Qty and Amt columns have been aggregated)...&lt;/p&gt;&lt;br /&gt;&lt;pre class="code"&gt;Company Year   Q1Amt Q1Qty Q2Amt Q2Qty Q3Amt Q3Qty Q4Amt Q4Qty TotalAmt TotalQty&lt;br&gt;------- ------ ----- ----- ----- ----- ----- ----- ----- ----- -------- --------&lt;br&gt;ABC     2006   1.1   2.2   1.2   2.4   1.3   1.3   1.4   4.2   5.0      10.1&lt;br&gt;ABC     2007   2.1   2.3   2.2   3.1   2.3   2.1   2.4   1.5   9.0      9.0&lt;br&gt;ABC     2008   1.5   5.1   0.0   0.0   2.3   3.3   1.9   4.2   5.7      12.6&lt;br&gt;XYZ     2006   2.1   3.6   2.2   1.8   3.3   2.6   2.4   3.7   10.0     11.7&lt;br&gt;XYZ     2007   3.1   1.9   1.2   1.2   3.3   4.2   1.4   4.0   9.0      11.3&lt;br&gt;XYZ     2008   2.5   3.9   3.5   2.1   1.3   3.9   3.9   3.4   11.2     13.3&lt;br&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;This type of Pivot is a common request so that both aggregates can be viewed for the same time period at the same time. Otherwise, you'd have two completely separate Pivots and you'd have to visually scan back and forth to make simple comparisons. As you'll see the "Problem with Pivots" is that each Pivot can only aggregate one column. To do something like this using Pivots, you have two use two Pivots.&lt;/p&gt;&lt;br /&gt;&lt;h3&gt;The Test Data&lt;/h3&gt;&lt;br /&gt;&lt;p&gt;Before we begin, we need some data to test with...&lt;/p&gt;&lt;br /&gt;&lt;pre class="code"&gt;--===== Sample data #2 (#SomeTable2)&lt;br&gt;--===== Create a test table and some data &lt;br&gt; CREATE TABLE #SomeTable2&lt;br&gt;        (&lt;br&gt;        Company  VARCHAR(3),&lt;br&gt;        Year     SMALLINT,&lt;br&gt;        Quarter  TINYINT, &lt;br&gt;        Amount   DECIMAL(2,1),&lt;br&gt;        Quantity DECIMAL(2,1)&lt;br&gt;        )&lt;br&gt;GO&lt;br&gt; INSERT INTO #SomeTable2&lt;br&gt;        (Company,Year, Quarter, Amount, Quantity)&lt;br&gt; SELECT 'ABC', 2006, 1, 1.1, 2.2 UNION ALL&lt;br&gt; SELECT 'ABC', 2006, 2, 1.2, 2.4 UNION ALL&lt;br&gt; SELECT 'ABC', 2006, 3, 1.3, 1.3 UNION ALL&lt;br&gt; SELECT 'ABC', 2006, 4, 1.4, 4.2 UNION ALL&lt;br&gt; SELECT 'ABC', 2007, 1, 2.1, 2.3 UNION ALL&lt;br&gt; SELECT 'ABC', 2007, 2, 2.2, 3.1 UNION ALL&lt;br&gt; SELECT 'ABC', 2007, 3, 2.3, 2.1 UNION ALL&lt;br&gt; SELECT 'ABC', 2007, 4, 2.4, 1.5 UNION ALL&lt;br&gt; SELECT 'ABC', 2008, 1, 1.5, 5.1 UNION ALL&lt;br&gt; SELECT 'ABC', 2008, 3, 2.3, 3.3 UNION ALL&lt;br&gt; SELECT 'ABC', 2008, 4, 1.9, 4.2 UNION ALL&lt;br&gt; SELECT 'XYZ', 2006, 1, 2.1, 3.6 UNION ALL&lt;br&gt; SELECT 'XYZ', 2006, 2, 2.2, 1.8 UNION ALL&lt;br&gt; SELECT 'XYZ', 2006, 3, 3.3, 2.6 UNION ALL&lt;br&gt; SELECT 'XYZ', 2006, 4, 2.4, 3.7 UNION ALL&lt;br&gt; SELECT 'XYZ', 2007, 1, 3.1, 1.9 UNION ALL&lt;br&gt; SELECT 'XYZ', 2007, 2, 1.2, 1.2 UNION ALL&lt;br&gt; SELECT 'XYZ', 2007, 3, 3.3, 4.2 UNION ALL&lt;br&gt; SELECT 'XYZ', 2007, 4, 1.4, 4.0 UNION ALL&lt;br&gt; SELECT 'XYZ', 2008, 1, 2.5, 3.9 UNION ALL&lt;br&gt; SELECT 'XYZ', 2008, 2, 3.5, 2.1 UNION ALL &lt;br&gt; SELECT 'XYZ', 2008, 3, 1.3, 3.9 UNION ALL&lt;br&gt; SELECT 'XYZ', 2008, 4, 3.9, 3.4&lt;br&gt;GO&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;h3&gt;The Multi-Aggregate Pivot&lt;/h3&gt;&lt;br /&gt;&lt;p&gt;Like I said... we'll do the Pivot first this time... then we'll show you how easy it is to do using a Cross Tab.&lt;/p&gt;&lt;br /&gt;&lt;p&gt;In order to do a single Pivot, you have to have a derived table and a Pivot clause. The "Problem with Pivots" is that you can only Pivot one aggregate per Pivot clause. If you want to Pivot two aggregates as shown at the beginning of this section, you have to make two Pivots and join them as well as adding the necessary columns to the Select list. You already know how to use a single Pivot... Here's how we would do a double Pivot using the data above...&lt;/p&gt;&lt;br /&gt;&lt;pre class="code"&gt;--===== The "Problem with Pivots" is you need to do one Pivot for each aggregate.&lt;br&gt;     -- This code Pivots the Amt and Qty values by quarter.&lt;br&gt; SELECT amt.Company,&lt;br&gt;        amt.Year,&lt;br&gt;        COALESCE(amt.[1],0) AS Q1Amt,&lt;br&gt;        COALESCE(qty.[1],0) AS Q1Qty,&lt;br&gt;        COALESCE(amt.[2],0) AS Q2Amt,&lt;br&gt;        COALESCE(qty.[2],0) AS Q2Qty,&lt;br&gt;        COALESCE(amt.[3],0) AS Q3Amt,&lt;br&gt;        COALESCE(qty.[3],0) AS Q3Qty,&lt;br&gt;        COALESCE(amt.[4],0) AS Q4Amt,&lt;br&gt;        COALESCE(qty.[4],0) AS Q4Qty,&lt;br&gt;        COALESCE(amt.[1],0)+COALESCE(amt.[2],0)+COALESCE(amt.[3],0)+COALESCE(amt.[4],0) AS TotalAmt,&lt;br&gt;        COALESCE(qty.[1],0)+COALESCE(qty.[2],0)+COALESCE(qty.[3],0)+COALESCE(qty.[4],0) AS TotalQty&lt;br&gt;   FROM (SELECT Company, Year, Quarter, Amount FROM #SomeTable2) t1&lt;br&gt;        PIVOT (SUM(Amount) FOR Quarter IN ([1], [2], [3], [4])) AS amt&lt;br&gt;  INNER JOIN &lt;br&gt;        (SELECT Company, Year, Quarter, Quantity FROM #SomeTable2) t2&lt;br&gt;        PIVOT (SUM(Quantity) FOR Quarter IN ([1], [2], [3], [4])) AS qty&lt;br&gt;     ON qty.Company = amt.Company &lt;br&gt;    AND qty.Year    = amt.Year         &lt;br&gt;  ORDER BY amt.Company, amt.Year&lt;br /&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;p&gt;I don't know about you, but my personal feeling is that's starting to look a bit complicated and it's starting to be more difficult to read. Certainly, if we tried to convert this to dynamic SQL, you'd have your work cut out for you.&lt;/p&gt;&lt;br /&gt;&lt;p&gt;Notice that the FROM clause has two nearly identical derived tables and the &lt;strong&gt;&lt;em&gt;only&lt;/em&gt;&lt;/strong&gt; difference in the Pivot clauses are the columns being SUMmed. And, take a look at the row totals in the Select list... thank goodness this example only has 4 columns each for Quantity and Amount.&lt;/p&gt;&lt;br /&gt;&lt;h3&gt;The Multi-Aggregate Cross Tab&lt;/h3&gt;&lt;br /&gt;&lt;p&gt;We saw how complicated Multi-Aggregate Pivots can get. And the example above was just for two 4 column aggregates... just image what it might look like for three 12 column aggregates!&lt;/p&gt;&lt;br /&gt;&lt;p&gt;Let's see how complicated it might be in a Cross Tab...ready?&lt;/p&gt;&lt;br /&gt;&lt;pre class="code"&gt;--===== Doing multiple aggregations in Cross Tabs is as simple as CPR&lt;br&gt;     -- (CPR = Cut, Paste, Replace). AND, the table is "dipped" only&lt;br&gt;     -- once instead of twice so there are NO JOINS to worry about!&lt;br&gt; SELECT Company,&lt;br&gt;        Year,&lt;br&gt;        SUM(CASE WHEN Quarter = 1 THEN Amount   ELSE 0 END) AS Q1Amt,&lt;br&gt;        SUM(CASE WHEN Quarter = 1 THEN Quantity ELSE 0 END) AS Q1Qty,&lt;br&gt;        SUM(CASE WHEN Quarter = 2 THEN Amount   ELSE 0 END) AS Q2Amt,&lt;br&gt;        SUM(CASE WHEN Quarter = 2 THEN Quantity ELSE 0 END) AS Q2Qty,&lt;br&gt;        SUM(CASE WHEN Quarter = 3 THEN Amount   ELSE 0 END) AS Q3Amt,&lt;br&gt;        SUM(CASE WHEN Quarter = 3 THEN Quantity ELSE 0 END) AS Q3Qty,&lt;br&gt;        SUM(CASE WHEN Quarter = 4 THEN Amount   ELSE 0 END) AS Q4Amt,&lt;br&gt;        SUM(CASE WHEN Quarter = 4 THEN Quantity ELSE 0 END) AS Q4Qty,&lt;br&gt;        SUM(Amount)   AS TotalAmt,&lt;br&gt;        SUM(Quantity) AS TotalQty&lt;br&gt;   FROM #SomeTable2&lt;br&gt;  GROUP BY Company, Year&lt;br&gt;  ORDER BY Company, Year&lt;br /&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;p&gt;How easy is that!? There're no derived tables... no fancy Pivot clauses... no huge lines of code to make simple row totals... and no joins!. It's a breeze to make using a little CPR (Copy, Paste, Replace).&lt;/p&gt;&lt;br /&gt;&lt;p&gt;Go back and compare the incredible simplicity of this Cross Tab with the relatively complex Pivot code to do the same thing. I don't know about you, but I won't be using Pivot to do such a simple thing.&lt;/p&gt;&lt;br /&gt;&lt;h2&gt;"Pre-Aggregation"&lt;/h2&gt;&lt;br /&gt;&lt;p&gt;I found something very handy in the past... I call it "Pre-Aggregation" and it can be used on either a Cross Tab or a Pivot.&lt;/p&gt;&lt;br /&gt;&lt;p&gt;The general purpose of pre-aggregation is to make it very easy to summarize the data and then format the data for display. Sometimes you'll have some complex aggregations that are a bit difficult or impossible to do when mixed with the rotation in the Select list, so the best thing to do is to do the aggregations as a derived table and &lt;em&gt;then&lt;/em&gt; rotate the results. For example, if you want to aggregate dates by month, you'll find it's much easier to pre-aggregate the data using a formula to convert all dates to the first of the month. We'll cover more on that subject in the next article on Cross Tabs.&lt;/p&gt;&lt;br /&gt;&lt;p&gt;&lt;strong&gt;Pre-aggregation is nothing more than doing the aggregation as part of a derived table and then doing a Cross Tab or Pivot on that result.&lt;/strong&gt; That's all it is.&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;You'll find pre-aggregation code for both Cross Tabs and Pivots in the next section of code where you'll also find another really good reason for doing pre-aggregation even if you don't need it to solve complexity...&lt;/p&gt;&lt;br /&gt;&lt;h2&gt;Performance&lt;/h2&gt;&lt;br /&gt;&lt;p&gt;Ah yes... what about performance? Just because the code looks simple or complex doesn't necessarily mean faster or slower nor fewer or more resources. Here's the full test code I used... I intentionally did NOT calculate Quarters from the date in the Cross Tabs or the Pivots because I wanted to show you just how much of a performance difference a simple tweak here and there can make... the biggest tweaks I made was the use of pre-aggregation and the use of CTE's...&lt;/p&gt;&lt;br /&gt;&lt;pre class="code"&gt;--===== Create and populate a 1,000,000 row test table.&lt;br&gt;     -- Column "RowNum" has a range of 1 to 1,000,000 unique numbers&lt;br&gt;     -- Column "Company" has a range of "AAA" to "BBB" non-unique 3 character strings&lt;br&gt;     -- Column "Amount has a range of 0.0000 to 9999.9900 non-unique numbers&lt;br&gt;     -- Column "Quantity" has a range of 1 to 50,000 non-unique numbers&lt;br&gt;     -- Column "Date" has a range of  &amp;gt;=01/01/2000 and &amp;lt;01/01/2010 non-unique date/times&lt;br&gt;     -- Columns Year and Quarter are the similarly named components of Date&lt;br&gt;     -- Jeff Moden&lt;br /&gt;&lt;br /&gt;&lt;br&gt; SELECT TOP 1000000 --&amp;lt;&amp;lt;Look!  Change this number for testing different size tables&lt;br&gt;        RowNum       = IDENTITY(INT,1,1),&lt;br&gt;        Company      = CHAR(ABS(CHECKSUM(NEWID()))%2+65)&lt;br&gt;                     + CHAR(ABS(CHECKSUM(NEWID()))%2+65)&lt;br&gt;                     + CHAR(ABS(CHECKSUM(NEWID()))%2+65),&lt;br&gt;        Amount       = CAST(ABS(CHECKSUM(NEWID()))%1000000/100.0 AS MONEY),&lt;br&gt;        Quantity     = ABS(CHECKSUM(NEWID()))%50000+1,&lt;br&gt;        Date         = CAST(RAND(CHECKSUM(NEWID()))*3653.0+36524.0 AS DATETIME),&lt;br&gt;        Year         = CAST(NULL AS SMALLINT),&lt;br&gt;        Quarter      = CAST(NULL AS TINYINT)&lt;br&gt;   INTO #SomeTable3&lt;br&gt;   FROM Master.sys.SysColumns t1&lt;br&gt;  CROSS JOIN&lt;br&gt;        Master.sys.SysColumns t2 &lt;br /&gt;&lt;br /&gt;--===== Fill in the Year and Quarter columns from the Date column&lt;br&gt; UPDATE #SomeTable3&lt;br&gt;    SET Year    = DATEPART(yy,Date),&lt;br&gt;        Quarter = DATEPART(qq,Date)&lt;br /&gt;&lt;br /&gt;--===== A table is not properly formed unless a Primary Key has been assigned&lt;br&gt;     -- Takes about 1 second to execute.&lt;br&gt;  ALTER TABLE #SomeTable3&lt;br&gt;        ADD PRIMARY KEY CLUSTERED (RowNum)&lt;br /&gt;CREATE NONCLUSTERED INDEX IX_#SomeTable3_Cover1 &lt;br&gt;    ON dbo.#SomeTable3 (Company, Year)&lt;br&gt;       INCLUDE (Amount, Quantity, Quarter) &lt;br&gt;GO&lt;br&gt;    SET STATISTICS TIME OFF&lt;br&gt;    SET STATISTICS IO OFF&lt;br&gt;GO&lt;br&gt;---------------------------------------------------------------------------------------------------&lt;br&gt;--===== "Normal" Cross Tab&lt;br&gt;  PRINT REPLICATE('=',100)&lt;br&gt;  PRINT '=============== "Normal" Cross Tab ==============='&lt;br&gt;    SET STATISTICS IO ON&lt;br&gt;    SET STATISTICS TIME ON&lt;br /&gt; SELECT Company,&lt;br&gt;        Year,&lt;br&gt;        SUM(CASE WHEN Quarter = 1 THEN Amount   ELSE 0 END) AS Q1Amt,&lt;br&gt;        SUM(CASE WHEN Quarter = 1 THEN Quantity ELSE 0 END) AS Q1Qty,&lt;br&gt;        SUM(CASE WHEN Quarter = 2 THEN Amount   ELSE 0 END) AS Q2Amt,&lt;br&gt;        SUM(CASE WHEN Quarter = 2 THEN Quantity ELSE 0 END) AS Q2Qty,&lt;br&gt;        SUM(CASE WHEN Quarter = 3 THEN Amount   ELSE 0 END) AS Q3Amt,&lt;br&gt;        SUM(CASE WHEN Quarter = 3 THEN Quantity ELSE 0 END) AS Q3Qty,&lt;br&gt;        SUM(CASE WHEN Quarter = 4 THEN Amount   ELSE 0 END) AS Q4Amt,&lt;br&gt;        SUM(CASE WHEN Quarter = 4 THEN Quantity ELSE 0 END) AS Q4Qty,&lt;br&gt;        SUM(Amount)   AS TotalAmt,&lt;br&gt;        SUM(Quantity) AS TotalQty&lt;br&gt;   FROM #SomeTable3&lt;br&gt;  GROUP BY Company, Year&lt;br&gt;  ORDER BY Company, Year&lt;br /&gt;    SET STATISTICS TIME OFF&lt;br&gt;    SET STATISTICS IO OFF&lt;br&gt;---------------------------------------------------------------------------------------------------&lt;br&gt;--===== "Normal" Pivot&lt;br&gt;  PRINT REPLICATE('=',100)&lt;br&gt;  PRINT '=============== "Normal" Pivot ==============='&lt;br&gt;    SET STATISTICS IO ON&lt;br&gt;    SET STATISTICS TIME ON&lt;br /&gt; SELECT amt.Company,&lt;br&gt;        amt.Year,&lt;br&gt;        COALESCE(amt.[1],0) AS Q1Amt,&lt;br&gt;        COALESCE(qty.[1],0) AS Q1Qty,&lt;br&gt;        COALESCE(amt.[2],0) AS Q2Amt,&lt;br&gt;        COALESCE(qty.[2],0) AS Q2Qty,&lt;br&gt;        COALESCE(amt.[3],0) AS Q3Amt,&lt;br&gt;        COALESCE(qty.[3],0) AS Q3Qty,&lt;br&gt;        COALESCE(amt.[4],0) AS Q4Amt,&lt;br&gt;        COALESCE(qty.[4],0) AS Q5Qty,&lt;br&gt;        COALESCE(amt.[1],0)+COALESCE(amt.[2],0)+COALESCE(amt.[3],0)+COALESCE(amt.[4],0) AS TotalAmt,&lt;br&gt;        COALESCE(qty.[1],0)+COALESCE(qty.[2],0)+COALESCE(qty.[3],0)+COALESCE(qty.[4],0) AS TotalQty&lt;br&gt;   FROM (SELECT Company, Year, Quarter, Amount FROM #SomeTable3) t1&lt;br&gt;        PIVOT (SUM(Amount) FOR Quarter IN ([1], [2], [3], [4])) AS amt&lt;br&gt;  INNER JOIN &lt;br&gt;        (SELECT Company, Year, Quarter, Quantity FROM #SomeTable3) t2&lt;br&gt;        PIVOT (SUM(Quantity) FOR Quarter IN ([1], [2], [3], [4])) AS qty&lt;br&gt;     ON qty.Company = amt.Company &lt;br&gt;    AND qty.Year    = amt.Year         &lt;br&gt;  ORDER BY amt.Company, amt.Year&lt;br /&gt;    SET STATISTICS TIME OFF&lt;br&gt;    SET STATISTICS IO OFF&lt;br&gt;---------------------------------------------------------------------------------------------------&lt;br&gt;--===== "Pre-aggregated" Cross Tab&lt;br&gt;  PRINT REPLICATE('=',100)&lt;br&gt;  PRINT '=============== "Pre-aggregated" Cross Tab ==============='&lt;br&gt;    SET STATISTICS IO ON&lt;br&gt;    SET STATISTICS TIME ON&lt;br /&gt; SELECT Company,&lt;br&gt;        Year,&lt;br&gt;        SUM(CASE WHEN Quarter = 1 THEN Amount   ELSE 0 END) AS Q1Amt,&lt;br&gt;        SUM(CASE WHEN Quarter = 1 THEN Quantity ELSE 0 END) AS Q1Qty,&lt;br&gt;        SUM(CASE WHEN Quarter = 2 THEN Amount   ELSE 0 END) AS Q2Amt,&lt;br&gt;        SUM(CASE WHEN Quarter = 2 THEN Quantity ELSE 0 END) AS Q2Qty,&lt;br&gt;        SUM(CASE WHEN Quarter = 3 THEN Amount   ELSE 0 END) AS Q3Amt,&lt;br&gt;        SUM(CASE WHEN Quarter = 3 THEN Quantity ELSE 0 END) AS Q3Qty,&lt;br&gt;        SUM(CASE WHEN Quarter = 4 THEN Amount   ELSE 0 END) AS Q4Amt,&lt;br&gt;        SUM(CASE WHEN Quarter = 4 THEN Quantity ELSE 0 END) AS Q4Qty,&lt;br&gt;        SUM(Amount)   AS TotalAmt,&lt;br&gt;        SUM(Quantity) AS TotalQty&lt;br&gt;   FROM (SELECT Company,Year,Quarter,SUM(Amount) AS Amount,SUM(Quantity) AS Quantity&lt;br&gt;           FROM #SomeTable3 GROUP BY Company,Year,Quarter) d&lt;br&gt;  GROUP BY Company, Year&lt;br&gt;  ORDER BY Company, Year&lt;br /&gt;    SET STATISTICS TIME OFF&lt;br&gt;    SET STATISTICS IO OFF&lt;br&gt;---------------------------------------------------------------------------------------------------&lt;br&gt;--===== "Pre-aggregated" Pivot&lt;br&gt;  PRINT REPLICATE('=',100)&lt;br&gt;  PRINT '=============== "Pre-aggregated" Pivot ==============='&lt;br&gt;    SET STATISTICS IO ON&lt;br&gt;    SET STATISTICS TIME ON&lt;br /&gt; SELECT amt.Company,&lt;br&gt;        amt.Year,&lt;br&gt;        COALESCE(amt.[1],0) AS Q1Amt,&lt;br&gt;        COALESCE(qty.[1],0) AS Q1Qty,&lt;br&gt;        COALESCE(amt.[2],0) AS Q2Amt,&lt;br&gt;        COALESCE(qty.[2],0) AS Q2Qty,&lt;br&gt;        COALESCE(amt.[3],0) AS Q3Amt,&lt;br&gt;        COALESCE(qty.[3],0) AS Q3Qty,&lt;br&gt;        COALESCE(amt.[4],0) AS Q4Amt,&lt;br&gt;        COALESCE(qty.[4],0) AS Q5Qty,&lt;br&gt;        COALESCE(amt.[1],0)+COALESCE(amt.[2],0)+COALESCE(amt.[3],0)+COALESCE(amt.[4],0) AS TotalAmt,&lt;br&gt;        COALESCE(qty.[1],0)+COALESCE(qty.[2],0)+COALESCE(qty.[3],0)+COALESCE(qty.[4],0) AS TotalQty&lt;br&gt;   FROM (SELECT Company, Year, Quarter, SUM(Amount) AS Amount FROM #SomeTable3 GROUP BY Company, Year, Quarter) t1&lt;br&gt;        PIVOT (SUM(Amount) FOR Quarter IN ([1], [2], [3], [4])) AS amt&lt;br&gt;  INNER JOIN &lt;br&gt;        (SELECT Company, Year, Quarter, SUM(Quantity) AS Quantity FROM #SomeTable3 GROUP BY Company, Year, Quarter) t2&lt;br&gt;        PIVOT (SUM(Quantity) FOR Quarter IN ([1], [2], [3], [4])) AS qty&lt;br&gt;     ON qty.Company = amt.Company &lt;br&gt;    AND qty.Year    = amt.Year         &lt;br&gt;  ORDER BY amt.Company, amt.Year&lt;br /&gt;    SET STATISTICS TIME OFF&lt;br&gt;    SET STATISTICS IO OFF&lt;br&gt;---------------------------------------------------------------------------------------------------&lt;br&gt;--===== "Pre-aggregated" Cross Tab with CTE&lt;br&gt;  PRINT REPLICATE('=',100)&lt;br&gt;  PRINT '=============== "Pre-aggregated" Cross Tab with CTE ==============='&lt;br&gt;    SET STATISTICS IO ON&lt;br&gt;    SET STATISTICS TIME ON&lt;br /&gt;;WITH&lt;br&gt;ctePreAgg AS&lt;br&gt;(SELECT Company,Year,Quarter,SUM(Amount) AS Amount,SUM(Quantity) AS Quantity&lt;br&gt;   FROM #SomeTable3 &lt;br&gt;  GROUP BY Company,Year,Quarter&lt;br&gt;)&lt;br&gt; SELECT Company,&lt;br&gt;        Year,&lt;br&gt;        SUM(CASE WHEN Quarter = 1 THEN Amount   ELSE 0 END) AS Q1Amt,&lt;br&gt;        SUM(CASE WHEN Quarter = 1 THEN Quantity ELSE 0 END) AS Q1Qty,&lt;br&gt;        SUM(CASE WHEN Quarter = 2 THEN Amount   ELSE 0 END) AS Q2Amt,&lt;br&gt;        SUM(CASE WHEN Quarter = 2 THEN Quantity ELSE 0 END) AS Q2Qty,&lt;br&gt;        SUM(CASE WHEN Quarter = 3 THEN Amount   ELSE 0 END) AS Q3Amt,&lt;br&gt;        SUM(CASE WHEN Quarter = 3 THEN Quantity ELSE 0 END) AS Q3Qty,&lt;br&gt;        SUM(CASE WHEN Quarter = 4 THEN Amount   ELSE 0 END) AS Q4Amt,&lt;br&gt;        SUM(CASE WHEN Quarter = 4 THEN Quantity ELSE 0 END) AS Q4Qty,&lt;br&gt;        SUM(Amount)   AS TotalAmt,&lt;br&gt;        SUM(Quantity) AS TotalQty&lt;br&gt;   FROM ctePreAgg&lt;br&gt;  GROUP BY Company, Year&lt;br&gt;  ORDER BY Company, Year&lt;br /&gt;    SET STATISTICS TIME OFF&lt;br&gt;    SET STATISTICS IO OFF&lt;br&gt;---------------------------------------------------------------------------------------------------&lt;br&gt;--===== "Pre-aggregated" Pivot with CTE&lt;br&gt;  PRINT REPLICATE('=',100)&lt;br&gt;  PRINT '=============== "Pre-aggregated" Pivot with CTE ==============='&lt;br&gt;    SET STATISTICS IO ON&lt;br&gt;    SET STATISTICS TIME ON&lt;br /&gt;;WITH&lt;br&gt;ctePreAgg AS&lt;br&gt;(SELECT Company,Year,Quarter,SUM(Amount) AS Amount,SUM(Quantity) AS Quantity&lt;br&gt;   FROM #SomeTable3 &lt;br&gt;  GROUP BY Company,Year,Quarter&lt;br&gt;)&lt;br&gt; SELECT amt.Company,&lt;br&gt;        amt.Year,&lt;br&gt;        COALESCE(amt.[1],0) AS Q1Amt,&lt;br&gt;        COALESCE(qty.[1],0) AS Q1Qty,&lt;br&gt;        COALESCE(amt.[2],0) AS Q2Amt,&lt;br&gt;        COALESCE(qty.[2],0) AS Q2Qty,&lt;br&gt;        COALESCE(amt.[3],0) AS Q3Amt,&lt;br&gt;        COALESCE(qty.[3],0) AS Q3Qty,&lt;br&gt;        COALESCE(amt.[4],0) AS Q4Amt,&lt;br&gt;        COALESCE(qty.[4],0) AS Q5Qty,&lt;br&gt;        COALESCE(amt.[1],0)+COALESCE(amt.[2],0)+COALESCE(amt.[3],0)+COALESCE(amt.[4],0) AS TotalAmt,&lt;br&gt;        COALESCE(qty.[1],0)+COALESCE(qty.[2],0)+COALESCE(qty.[3],0)+COALESCE(qty.[4],0) AS TotalQty&lt;br&gt;   FROM (SELECT Company, Year, Quarter, Amount FROM ctePreAgg) AS t1&lt;br&gt;        PIVOT (SUM(Amount) FOR Quarter IN ([1], [2], [3], [4])) AS amt&lt;br&gt;  INNER JOIN &lt;br&gt;        (SELECT Company, Year, Quarter, Quantity FROM ctePreAgg) AS t2&lt;br&gt;        PIVOT (SUM(Quantity) FOR Quarter IN ([1], [2], [3], [4])) AS qty&lt;br&gt;     ON qty.Company = amt.Company &lt;br&gt;    AND qty.Year    = amt.Year         &lt;br&gt;  ORDER BY amt.Company, amt.Year&lt;br /&gt;    SET STATISTICS TIME OFF&lt;br&gt;    SET STATISTICS IO OFF&lt;br&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;The test code was executed 10 times each for 10k, 100k, and 1 million rows both with and without the index created at the beginning of the code. The averaged results, calculated from a profiler table (not included in the code), are fascinating. The light green cells indicate the fastest run times or the least number of reads. The light blue indicate the second place for the same thing...&lt;/p&gt;&lt;br /&gt;&lt;p&gt;&lt;div style="width: 482px;" class="wideImageContainer"&gt;&lt;img style="display: block; max-width: 482px; cursor: -moz-zoom-in;" alt="" src="/Images/1376.gif" border="0"&gt;&lt;div&gt;&lt;img style="margin: 0px 3px;" src="/Resources/Images/zoom.gif" align="bottom"&gt;&lt;a href="javascript:;"&gt;Zoom in&lt;/a&gt;&lt;span&gt;&amp;nbsp;&amp;nbsp;|&amp;nbsp;&amp;nbsp;&lt;/span&gt;&lt;a href="javascript:;"&gt;Open in new window&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/p&gt;&lt;br /&gt;&lt;p&gt;Notice that even for "normal" Cross Tabs and Pivots that &lt;strong&gt;the only place a Pivot wins in any category is in the paltry 10k row test&lt;/strong&gt;. The Cross Tab wins everywhere else. That's good news for SQL Server 2000 users because you won't want to change your code if and when you upgrade to SQL Server 2005. Using CTE's helps a bit but not as much as pre-aggregation on the larger row counts does. Again, that's good news for SQL Server 2000 users.&lt;/p&gt;&lt;br /&gt;&lt;h2&gt;Review&lt;/h2&gt;&lt;br /&gt;&lt;p&gt;In this article, we learned the basis of how to change rows to columns using both Cross Tabs and Pivot. We've discovered that Cross Tabs are nothing more than simple aggregations that have a built in selection condition in the form of a simple Case statement. We've seen how to use a Pivot to do the same thing as a Cross Tab and, in the process, discovered that they're a bit more complicated to create, read, and understand especially when compared to the simplicity of the Cross Tab code. We've been introduced to the concept of "pre-aggregation and the fact that pre-aggregation can make more complex aggregations both easier to read and to contrive. Through testing, we've found that the Cross Tab beats Pivot code in all but the smallest of tables. Through that same testing, we've also found that pre-aggregation adds a substantial performance gain in all but the smallest of tables.&lt;/p&gt;&lt;br /&gt;&lt;p&gt;Last but certainly not least, we've discovered that there's no reason to rewrite properly written Cross Tabs to become Pivots when shifting from SQL Server 2000 to SQL Server 2005. To do so would actually cause a negative impact to performance most of the time.&lt;/p&gt;&lt;br /&gt;&lt;h2&gt;In the Works...&lt;/h2&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;Coming soon to a forum near you... Dynamic Cross Tabs, EAV/NVP conversions, and more on pre-aggregation.&lt;/p&gt;&lt;br /&gt;&lt;p&gt;Thanks for listening folks.&lt;/p&gt;&lt;br /&gt;&lt;p&gt;--Jeff Moden&lt;/p&gt;&lt;br /&gt;&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7516218764458010868-8925261268148021481?l=all-about-database.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://all-about-database.blogspot.com/feeds/8925261268148021481/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7516218764458010868&amp;postID=8925261268148021481' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7516218764458010868/posts/default/8925261268148021481'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7516218764458010868/posts/default/8925261268148021481'/><link rel='alternate' type='text/html' href='http://all-about-database.blogspot.com/2008/12/cross-tabs-and-pivots-part-1-converting.html' title='Cross Tabs and Pivots, Part 1 – Converting Rows to Columns By Jeff Moden, 2008/08/19'/><author><name>firman.arrow</name><uri>http://www.blogger.com/profile/14656110271655711161</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7516218764458010868.post-7170836509564542538</id><published>2008-12-13T01:22:00.000-08:00</published><updated>2008-12-13T01:25:08.399-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='more'/><category scheme='http://www.blogger.com/atom/ns#' term='Server Tuning'/><title type='text'>Tuning SQL Server performance via disk arrays and disk partitioning</title><content type='html'>One of my collections, about MSSQL Server Tunning. Enjoy it...&lt;br /&gt;&lt;br /&gt;Author : Denny Cherry, 08.19.2008&lt;br&gt;&lt;br /&gt;&lt;i&gt;As a DBA, much of your focus is on performance tuning SQL Server. But have you spent time to tune the hardware supporting your SQL Server system? Are you using the optimal disk array configuration? Are the disk partitions aligned? This tip discusses how to get your SQL Server hardware performance in top shape – whether the system is already in operation or it's a new setup.&lt;/i&gt;&lt;p&gt;&lt;/p&gt;&lt;br /&gt;&lt;p&gt;With the massive amount of raw horse power available in today's server class hardware, it's easy to skip over the hardware when it comes to performance tuning the SQL Server database. After all, with so much power available, who cares if something takes a few extra milliseconds to complete? Is anyone really going to notice that extra millisecond?&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class="fullpost"&gt;&lt;br /&gt;&lt;p&gt;But what happens when you perform an operation that takes 10 extra milliseconds to complete; and it needs to perform 100 times an hour, for a year? All of a sudden, that 10 milliseconds turns into 2.4 hours. If you perform that operation 1,000 times an hour -- which isn't all that unheard of in a smaller OLTP database -- you are now looking at more than 24 hours of wasted time.&lt;/p&gt; &lt;br /&gt;&lt;p&gt;In my particular environment, we run the same stored procedure at least 2,000 times per minute. If that stored procedure takes an extra 10 milliseconds to complete, we are looking at eight hours of lost time daily, or 121 days of lost time per year.&lt;/p&gt;&lt;br /&gt;&lt;p&gt;&lt;b&gt;Tune SQL Server by tuning disk arrays&lt;/b&gt;&lt;/p&gt;&lt;br /&gt;&lt;p&gt;There are a few places to check hardware components when tuning your SQL Server system. The easiest components to check are disk arrays. Typically, disk arrays are where the most time is expended  &lt;br /&gt;  &lt;br /&gt;&lt;br /&gt;&lt;table align="left" border="0" cellpadding="0" cellspacing="0"&gt;&lt;br /&gt; &lt;tbody&gt;&lt;tr&gt;&lt;br /&gt;  &lt;td colspan="2"&gt;&lt;img src="http://media.techtarget.com/searchSQLServer/images/spacer.gif" width="1" height="7"&gt;&lt;/td&gt;&lt;br /&gt; &lt;/tr&gt;&lt;br /&gt; &lt;tr&gt;&lt;br /&gt;  &lt;td&gt;&lt;br /&gt;   &lt;table width="200" align="left" border="0" cellpadding="0" cellspacing="0"&gt;&lt;br /&gt;    &lt;tbody&gt;&lt;tr class="color4"&gt;&lt;br /&gt;     &lt;td&gt;&lt;br /&gt;&lt;br /&gt;      &lt;table width="100%" border="0" cellpadding="4" cellspacing="1"&gt;&lt;br /&gt;       &lt;tbody&gt;&lt;tr class="colorback"&gt;&lt;td class="body"&gt;&lt;b&gt;More on tuning SQL Server performance and hardware:&lt;/b&gt;&lt;/td&gt;&lt;/tr&gt;&lt;br /&gt;       &lt;tr class="colorSuperLite"&gt;&lt;td class="body"&gt;&lt;li&gt;&lt;a href="http://searchsqlserver.techtarget.com/tip/0,289483,sid87_gci1289528,00.html" target="_blank"&gt;Determining SQL Server database storage requirements&lt;/a&gt; &lt;p&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://searchsqlserver.techtarget.com/tip/0,289483,sid87_gci1262122,00.html" target="_blank"&gt;Optimize disk configuration in SQL Server&lt;/a&gt; &lt;p&gt;&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://searchsqlserver.techtarget.com/tip/0,289483,sid87_gci1315434,00.html" target="_blank"&gt;Storage area network (SAN) basics every DBA must know&lt;/a&gt;&lt;/li&gt;&lt;p&gt;&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;br /&gt;      &lt;/tbody&gt;&lt;/table&gt;&lt;br /&gt;     &lt;/td&gt;&lt;br /&gt;    &lt;/tr&gt;&lt;br /&gt;&lt;br /&gt;   &lt;/tbody&gt;&lt;/table&gt;&lt;br /&gt;  &lt;/td&gt;&lt;br /&gt;  &lt;td&gt;&lt;img src="http://media.techtarget.com/searchSQLServer/images/spacer.gif" width="7" height="1"&gt;&lt;/td&gt;&lt;br /&gt; &lt;/tr&gt;&lt;br /&gt; &lt;tr&gt;&lt;br /&gt;  &lt;td colspan="2"&gt;&lt;img src="http://media.techtarget.com/searchSQLServer/images/spacer.gif" width="1" height="7"&gt;&lt;/td&gt;&lt;br /&gt; &lt;/tr&gt;&lt;br /&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;br /&gt;  waiting for something to happen. There are a couple of ways to tune the disks to improve SQL Server performance. The first is to make sure your disk array has enough spindles to handle the workload that will be placed on it. Second, make sure the disk arrays are in the correct RAID level to offer the best support level for the database.&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;While it is true that RAID 10 offers better write performance, in most cases, RAID 10 isn't required for the data files. That said, you should use RAID 10 for your transaction logs and tempdb database, as they are mostly write files. The reason I say not to use RAID 10 for all database files is that RAID 10 is very costly to implement in large disk sizes. This is because for each spindle used for data, a second spindle is used for redundancy.&lt;/p&gt;&lt;br /&gt;&lt;p&gt;Finding out if you need more spindles on an existing system is easy. Open &lt;a href="http://searchsqlserver.techtarget.com/generic/0,295582,sid87_gci1302600,00.html" target="_blank"&gt;Performance Monitor&lt;/a&gt; on the server and add the "Physical Disk" object and the "Current Disk Queue Length" counter. Some queuing is OK; however, there is a tipping point.  To find out where the tipping point of "OK queuing" and "too much queuing" is, take the number of disks in the array and multiply it by two. If the result is greater than the maximum value in Performance Monitor, then you have too much queuing. When we talk about the number of disks, we're referring to the number of disks that are actively working with data. If you have a RAID 10 array, this is half the number of disks in the array.&lt;/p&gt;&lt;br /&gt;&lt;p&gt;"Number of Disks" x 2 = Maximum Allowable Queuing&lt;/p&gt;&lt;br /&gt;&lt;p&gt;&lt;b&gt;How to configure the disk array on your new SQL Server system&lt;/b&gt;&lt;/p&gt;&lt;br /&gt;&lt;p&gt;When working on a new system without any load on it, making sure you configure your disk array correctly is a little more challenging. If you have another system with the same amount of load on it, you can use that system as a guide. However, if this is the first large system in your environment, then getting it correct can be a bit harder.&lt;/p&gt;&lt;br /&gt;&lt;p&gt;You'll need to look at your database system and the expected transactions per second, and make an educated guess on how many spindles you'll need. When dealing with high-end drives, expect each drive to give you about 100 IOPs to 120 IOPs per second per disk in an OLTP environment. When dealing with SATA drives, expect each drive to give you about 60 IOPs to 80 IOPs per second per disk in an OLTP environment.  Those numbers will go up when working in an OLAP environment because OLAP databases put a different kind of load on the disk system. It's a more sequential load, whereas OLTP databases put a random load on the disks.&lt;/p&gt;&lt;br /&gt;&lt;p&gt;&lt;b&gt;Disk partition alignment improves SQL Server performance&lt;/b&gt;&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;Once you set up your disk array, you'll want to make sure the partition you create is correctly aligned. By default, when Windows (or any other operating system for that matter) creates the partition on a disk or array, the partition is not correctly aligned for peak performance. Disk drives are made up of 1K blocks. The physical disks like to do all their operations in 64-block chunks called clusters. Conveniently, SQL Server likes to do all its operations in 64 K operations -- there are eight 8K blocks in each extent, and SQL does its reads one extent at a time. When the partition is created, the boot sector is created at the beginning of the partition. This boot sector is 32 K in size, causing the 64K operations to be spread between two 64K clusters. This then causes each logical operation to take twice as many physical operations as needed.&lt;/p&gt;&lt;br /&gt;&lt;p&gt;You can see your current alignment offset by using the diskpart application. Open a command prompt and run diskpart.exe. When you are prompted with a DISKPART&amp;gt; prompt, type SELECT DISK n where n is the disk number you want to look at -- the command LIST DISK will give you a list of disks in the machine. After selecting the disk, type in "LIST PARTITION" to get the partition information, including the offset.&lt;/p&gt;&lt;br /&gt; &lt;br /&gt;&lt;img src="http://media.techtarget.com/digitalguide/images/Misc/perf_hardware_tip1.png" alt="Using disk partition in SQL Server"&gt;&lt;br /&gt;&lt;br&gt;&lt;b&gt;Figure 1:&lt;/b&gt; Run the DISKPART application to view disk alignment.&lt;br /&gt;&lt;br /&gt;&lt;p&gt;In order to create the partition, you'll need to use the CREATE PARTITION command. The full command is CREATE PARTITION PRIMARY ALIGN=64. This creates the new partition with the 64K offset, aligning the partition into the optimum position for maximum performance.&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;Check out part two in this tip series, &lt;a href="http://searchsqlserver.techtarget.com/tip/0,289483,sid87_gci1328928,00.html" target="_blank"&gt;configuring memory and CPU processing for improved SQL Server performance.&lt;/a&gt;&lt;br /&gt;&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7516218764458010868-7170836509564542538?l=all-about-database.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://all-about-database.blogspot.com/feeds/7170836509564542538/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7516218764458010868&amp;postID=7170836509564542538' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7516218764458010868/posts/default/7170836509564542538'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7516218764458010868/posts/default/7170836509564542538'/><link rel='alternate' type='text/html' href='http://all-about-database.blogspot.com/2008/12/one-of-my-collections-about-mssql.html' title='Tuning SQL Server performance via disk arrays and disk partitioning'/><author><name>firman.arrow</name><uri>http://www.blogger.com/profile/14656110271655711161</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7516218764458010868.post-988964036208071428</id><published>2008-12-13T01:11:00.000-08:00</published><updated>2008-12-13T01:21:21.895-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='more'/><category scheme='http://www.blogger.com/atom/ns#' term='Server Tuning'/><category scheme='http://www.blogger.com/atom/ns#' term='SQL Server Tunning'/><title type='text'>Tuning SQL Server performance via memory and CPU processing</title><content type='html'>After a long journey browsing on internet, finally I found this interesting and usefull article for tunning our MSSQL Database Server. So in this place i re posting to share with you guys.... enjoy it ;)&lt;br /&gt;&lt;br /&gt;Author : Denny Cherry, 09.09.2008&lt;br /&gt;In my previous tip, &lt;a href="http://searchsqlserver.techtarget.com/tip/0,289483,sid87_gci1326028,00.html" target="_blank"&gt;Tuning SQL Server performance via disk arrays and disk partitioning&lt;/a&gt;, we talked about how important it is to ensure that your storage was set up correctly to optimize SQL Server performance. However, storage isn't the only part of SQL Server hardware that needs special consideration when designing your infrastructure.&lt;p&gt;&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;SQL Server memory can also impact performance. While having too much memory in a SQL Server system is a waste of money, having too little memory is extremely detrimental to performance.  Unfortunately, determining when you need more memory in the system can be a bit tricky. When memory problems begin, you'll start to see an increase in disk I/O, as well as an increase in disk queuing. You'll also see a decrease in the buffer cache hit ratio and page life expectancy. As memory requirements increase, you may begin to see these error messages in the log file:&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class="fullpost"&gt;&lt;br /&gt;&lt;p&gt;&lt;/p&gt;&lt;li&gt;A significant part of SQL Server process memory has been paged out. This may result in a performance degradation. Duration: &lt;i&gt;%n&lt;/i&gt; seconds. Working set (KB): &lt;i&gt;%w&lt;/i&gt;, committed (KB): &lt;i&gt;%c&lt;/i&gt;, memory utilization: &lt;i&gt;%u&lt;/i&gt;.&lt;/li&gt;&lt;p&gt;&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;&lt;/p&gt;&lt;li&gt;SQL Server has encountered &lt;i&gt;%o&lt;/i&gt; occurrence(s) of IO requests taking longer than 15 seconds to complete on file [&lt;i&gt;%f&lt;/i&gt;] in database [&lt;i&gt;%d&lt;/i&gt;] (&lt;i&gt;%i&lt;/i&gt;). The OS file handle is &lt;i&gt;%h&lt;/i&gt;.  The offset of the latest long IO is: &lt;i&gt;%l&lt;/i&gt;.&lt;/li&gt;&lt;p&gt;&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;Unfortunately, this is not the only time these errors are reported, so you have to use them along with the performance monitor metrics to determine that memory is actually low.&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;When dealing with SQL Server memory issues, there are a few options to resolve the problems. The easiest solution is to increase server memory, which increases the amount of buffer &lt;br /&gt;&lt;br /&gt;&lt;table align="left" border="0" cellpadding="0" cellspacing="0"&gt;&lt;br /&gt;&lt;tbody&gt;&lt;tr&gt;&lt;br /&gt; &lt;td colspan="2"&gt;&lt;img src="http://media.techtarget.com/searchSQLServer/images/spacer.gif" width="1" height="7" /&gt;&lt;/td&gt;&lt;br /&gt;&lt;/tr&gt;&lt;br /&gt;&lt;tr&gt;&lt;br /&gt; &lt;td&gt;&lt;br /&gt;  &lt;table width="200" align="left" border="0" cellpadding="0" cellspacing="0"&gt;&lt;br /&gt;   &lt;tbody&gt;&lt;tr class="color4"&gt;&lt;br /&gt;&lt;br /&gt;    &lt;td&gt;&lt;br /&gt;     &lt;table width="100%" border="0" cellpadding="4" cellspacing="1"&gt;&lt;br /&gt;      &lt;tbody&gt;&lt;tr class="colorback"&gt;&lt;td class="body"&gt;&lt;b&gt;More on improving SQL Server performance:&lt;/b&gt;&lt;/td&gt;&lt;/tr&gt;&lt;br /&gt;      &lt;tr class="colorSuperLite"&gt;&lt;td class="body"&gt;&lt;li&gt;&lt;a href="http://searchsqlserver.techtarget.com/tip/0,289483,sid87_gci1316780,00.html" target="_blank"&gt;Memory configurations for procedure cache and buffer cache&lt;/a&gt;&lt;/li&gt;&lt;p&gt;&lt;/p&gt;&lt;li&gt;&lt;a href="http://searchsqlserver.techtarget.com/tip/0,289483,sid87_gci1299068,00.html" target="_blank"&gt;Configuring SQL Server memory settings&lt;/a&gt; &lt;p&gt;&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://searchsqlserver.techtarget.com/tip/0,289483,sid87_gci1239664,00.html" target="_blank"&gt;Clustered and non-clustered indexes in SQL Server&lt;/a&gt;&lt;/li&gt;&lt;/td&gt;&lt;/tr&gt;&lt;br /&gt;     &lt;/tbody&gt;&lt;/table&gt;&lt;br /&gt;    &lt;/td&gt;&lt;br /&gt;&lt;br /&gt;   &lt;/tr&gt;&lt;br /&gt;  &lt;/tbody&gt;&lt;/table&gt;&lt;br /&gt; &lt;/td&gt;&lt;br /&gt; &lt;td&gt;&lt;img src="http://media.techtarget.com/searchSQLServer/images/spacer.gif" width="7" height="1" /&gt;&lt;/td&gt;&lt;br /&gt;&lt;/tr&gt;&lt;br /&gt;&lt;tr&gt;&lt;br /&gt; &lt;td colspan="2"&gt;&lt;img src="http://media.techtarget.com/searchSQLServer/images/spacer.gif" width="1" height="7" /&gt;&lt;/td&gt;&lt;br /&gt;&lt;/tr&gt;&lt;br /&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;br /&gt;&lt;br /&gt; cache available. This adds to the amount of data in memory and reduces your disk I/O. Other potential solutions include removing clustered indexes for extremely large tables and using only nonclustered indexes for the table, including the Primary Key.&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;This will only make a difference when the clustered index is being used for lookups, and clustered index seeks are used. If another index is in use, it will not relieve any memory pressure, as the clustered index won't be in memory. If you're using clustered index scans, then this turns into table scans that load the table into memory instead of the index. If clustered index scans are being performed, then a new nonclustered index may help the situation without removing the index.&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;&lt;b&gt;How to monitor CPU queuing&lt;/b&gt;&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;The CPU is another piece of hardware that can cause potential performance problems. Most people only look at the speed of or number of CPUs. However, just like disks, CPUs can become bottlenecked. If there is a CPU bottleneck, you may not even see the CPU performance at 100%. CPUs have command queues in much the same way that disks have I/O queues. Commands are loaded into a CPU queue and the operation waits for the CPU to become available before performing the operation. As CPUs became faster, we could do things much faster within the CPU, but we could still only do the same number of things at one time.  Now, as dual-core, tri-core and quad-core CPUs become available, we can process more commands at one time.&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;You can monitor your CPU queue using &lt;a href="http://searchsqlserver.techtarget.com/generic/0,295582,sid87_gci1310036,00.html" target="_blank"&gt;SQL Server Performance Monitor&lt;/a&gt;. You'll find PerfMon under the System object, with the counter name "Processor Queue Length." Pretty much any queue length other than zero indicates a need to increase the number of operations that SQL Server can perform at any one time. It doesn't indicate a need for faster CPUs, but a need for more CPU cores. Today's newest servers support 32 cores per server, and some of the most advanced servers support up to 64 cores -- when chases are scaled together support for 64 cores can be built (available only from certain vendors). &lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;In parts one and two, I've pointed out a variety of places within the hardware that impact whether your SQL Server system will run at peak performance. These tips are not the be-all, end-all solutions to performance problems.  Table design and index tuning always have been and will continue to be extremely important.  Today's SQL Server is expected to do more work for more hours of the day, which makes hardware tuning more important to the success of the database platform. With these tools in your arsenal to combat performance problems, you'll be able to get every ounce of performance from the existing hardware with no or minimal hardware upgrades to the platform. But when you do need to make those purchasing decisions, use these tips to make the correct purchasing decisions to get the most upgrade for your dollars spent.&lt;/p&gt;&lt;br /&gt;&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7516218764458010868-988964036208071428?l=all-about-database.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://all-about-database.blogspot.com/feeds/988964036208071428/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7516218764458010868&amp;postID=988964036208071428' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7516218764458010868/posts/default/988964036208071428'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7516218764458010868/posts/default/988964036208071428'/><link rel='alternate' type='text/html' href='http://all-about-database.blogspot.com/2008/12/tuning-sql-server-performance-via.html' title='Tuning SQL Server performance via memory and CPU processing'/><author><name>firman.arrow</name><uri>http://www.blogger.com/profile/14656110271655711161</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7516218764458010868.post-2895560382280794778</id><published>2008-09-23T21:57:00.000-07:00</published><updated>2008-09-23T22:26:32.518-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='more'/><category scheme='http://www.blogger.com/atom/ns#' term='Excel'/><category scheme='http://www.blogger.com/atom/ns#' term='Export / Import'/><category scheme='http://www.blogger.com/atom/ns#' term='SQL Server'/><title type='text'>Export data from SQL Server to Excel</title><content type='html'>&lt;b&gt;Problem&lt;br /&gt;&lt;/b&gt;Exporting data from SQL Server to Excel seems like a reasonably simple request.  I just need to write out a few reports for users on a regular basis, nothing too fancy, the same basic report with a few different parameters.  What native SQL Server options are available to do so?  Do I need to learn another tool or can I use some T-SQL commands?  Does SQL Server 2005 offer any new options to enhance this process?&lt;br /&gt;&lt;br /&gt;&lt;span class="fullpost"&gt;&lt;a href="http://all-about-database.blogspot.com/2008/09/export-data-from-sql-server-to-excel.html"&gt;..Read More&lt;/a&gt;&lt;br /&gt;&lt;p align="left"&gt;&lt;span style=";font-family:Arial;font-size:85%;"  &gt;&lt;u&gt;&lt;b&gt;Solution&lt;/b&gt;&lt;/u&gt;&lt;br /&gt;Exporting data from SQL Server to Excel can be achieved in a variety of ways.  Some of these options include &lt;a href="http://www.mssqltips.com/category.asp?catid=13" target="_blank"&gt;Data Transformation Services&lt;/a&gt; (DTS), &lt;a href="http://www.mssqltips.com/category.asp?catid=17" target="_blank"&gt;SQL Server Integration Services&lt;/a&gt; (SSIS) and &lt;a href="http://www.mssqltips.com/tip.asp?tip=1185" target="_blank"&gt;Bulk Copy&lt;/a&gt; (BCP).  Data Transformation Services (SQL Server 2000) and SQL Server Integration Services (SQL Server 2005) offers a GUI where widgets can be dragged and dropped Each option has advantages and disadvantages, but all can do the job.  It is just a matter of your comfort level with the tools and the best solution to meet the need.  &lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style=";font-family:Arial;font-size:85%;"  &gt;Another option that is available directly via the T-SQL language is the OPENROWSET command (&lt;a href="http://msdn2.microsoft.com/en-us/library/aa276850%28SQL.80%29.aspx" target="_blank"&gt;SQL Server 2000&lt;/a&gt; and &lt;a href="http://msdn2.microsoft.com/en-us/library/ms190312.aspx" target="_blank"&gt;SQL Server 2005&lt;/a&gt;).  This command can be called directly in any stored procedure, script or SQL Server Job from T-SQL.  Below outlines the full syntax available:&lt;/span&gt;&lt;br /&gt;&lt;/p&gt;&lt;div align="center"&gt;&lt;table id="table1" border="1" cellpadding="3" cellspacing="0" width="80%"&gt;&lt;br /&gt;&lt;tbody&gt;&lt;br /&gt;&lt;tr&gt;&lt;br /&gt;&lt;td&gt;&lt;br /&gt;&lt;form id="aspnetForm" name="aspnetForm" action="http://msdn2.microsoft.com/en-us/library/ms190312.aspx" method="post" _initialaction="ms190312.aspx"&gt;OPENROWSET&lt;br /&gt;&lt;div id="ctl00_LibFrame_ResizeContainer"&gt;&lt;div class="contentPanel" id="ctl00_LibFrame_contentPanel" style="left: 467px; width: 550px;"&gt;&lt;div class="RightPanel" id="ctl00_LibFrame_MtpsContentPanel"&gt;&lt;div class="ContentArea"&gt;&lt;div id="ctl00_LibFrame_MainContent"&gt;&lt;div class="topic"&gt;&lt;div id="mainSection"&gt;&lt;div id="mainBody"&gt;&lt;div class="MTPS_CollapsibleRegion"&gt;&lt;div class="section" id="ctl00_LibFrame_ctl043ce837a" style="display: block;"&gt;&lt;div id="ctl00_LibFrame_ctl04"&gt;&lt;a name="syntaxToggle"&gt;&lt;div class="code" id="ctl00_LibFrame_ctl05_other"&gt;&lt;pre class="code" id="ctl00_LibFrame_ctl05other" space="preserve"&gt;( { 'provider_name' , { 'datasource' ; 'user_id' ; 'password'&lt;br /&gt;| 'provider_string' }&lt;br /&gt;  , {   [ catalog. ] [ schema. ] object&lt;br /&gt;   | 'query'&lt;br /&gt;}&lt;br /&gt;| BULK 'data_file' ,&lt;br /&gt;&lt;br /&gt;   { FORMATFILE = 'format_file_path' [ &amp;lt;bulk_options&amp;gt; ]&lt;br /&gt;   | SINGLE_BLOB | SINGLE_CLOB | SINGLE_NCLOB }&lt;br /&gt;} )&lt;br /&gt;&amp;lt;bulk_options&amp;gt; ::=&lt;br /&gt;[ , CODEPAGE = { 'ACP' | 'OEM' | 'RAW' | 'code_page' } ]&lt;br /&gt;[ , ERRORFILE = 'file_name' ]&lt;br /&gt;[ , FIRSTROW = first_row ]&lt;br /&gt;[ , LASTROW = last_row ]&lt;br /&gt;[ , MAXERRORS = maximum_errors ]&lt;br /&gt;[ , ROWS_PER_BATCH = rows_per_batch ]&lt;br /&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/form&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/div&gt;&lt;br /&gt;&lt;p align="center"&gt;&lt;span style=";font-family:Arial;font-size:85%;"  &gt;Source - &lt;a href="http://msdn2.microsoft.com/en-us/library/ms190312.aspx" target="_blank"&gt;SQL Server 2005 Books Online&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style=";font-family:Arial;font-size:85%;"  &gt; Below is a simple example of writing out the Job name and date to Sheet1 of an Excel spreadsheet in either SQL Server 2005 or 2000:&lt;/span&gt;&lt;/p&gt;&lt;div align="center"&gt;&lt;br /&gt;&lt;table id="table2" align="center" border="1" cellpadding="3" cellspacing="0" width="60%"&gt;&lt;br /&gt;&lt;tbody&gt;&lt;br /&gt;&lt;tr&gt;&lt;br /&gt;&lt;td&gt;&lt;span style="color: rgb(0, 0, 255);font-size:85%;" &gt;&lt;span style="font-family:Arial;"&gt;INSERT&lt;/span&gt;&lt;/span&gt;&lt;span style="font-family:Arial;"&gt;&lt;span style="font-size:85%;"&gt; &lt;/span&gt;&lt;span style="color: rgb(0, 0, 255);font-size:85%;" &gt;INTO&lt;/span&gt;&lt;span style="font-size:85%;"&gt; &lt;/span&gt;&lt;span style="color: rgb(0, 0, 255);font-size:85%;" &gt;OPENROWSET&lt;/span&gt;&lt;span style="color: rgb(128, 128, 128);font-size:85%;" &gt;(&lt;/span&gt;&lt;span style="color: rgb(255, 0, 0);font-size:85%;" &gt;'Microsoft.Jet.OLEDB.4.0'&lt;/span&gt;&lt;span style="color: rgb(128, 128, 128);font-size:85%;" &gt;,&lt;/span&gt;&lt;span style="font-size:85%;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="color: rgb(255, 0, 0);font-size:85%;" &gt;'Excel 8.0;Database=C:\testing.xls;'&lt;/span&gt;&lt;span style="color: rgb(128, 128, 128);font-size:85%;" &gt;,&lt;/span&gt;&lt;span style="font-size:85%;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="color: rgb(255, 0, 0);font-size:85%;" &gt;'SELECT Name, Date FROM [Sheet1$]'&lt;/span&gt;&lt;span style="color: rgb(128, 128, 128);font-size:85%;" &gt;)&lt;/span&gt;&lt;span style="font-size:85%;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="color: rgb(0, 0, 255);font-size:85%;" &gt;SELECT&lt;/span&gt;&lt;span style="font-size:85%;"&gt; [Name]&lt;/span&gt;&lt;span style="color: rgb(128, 128, 128);font-size:85%;" &gt;,&lt;/span&gt;&lt;span style="font-size:85%;"&gt; &lt;/span&gt;&lt;span style="color: rgb(255, 0, 255);font-size:85%;" &gt;GETDATE&lt;/span&gt;&lt;span style="color: rgb(128, 128, 128);font-size:85%;" &gt;()&lt;/span&gt;&lt;span style="font-size:85%;"&gt; &lt;/span&gt;&lt;span style="color: rgb(0, 0, 255);font-size:85%;" &gt;FROM&lt;/span&gt;&lt;span style="font-size:85%;"&gt; msdb&lt;/span&gt;&lt;span style="color: rgb(128, 128, 128);font-size:85%;" &gt;.&lt;/span&gt;&lt;span style="font-size:85%;"&gt;dbo&lt;/span&gt;&lt;span style="color: rgb(128, 128, 128);font-size:85%;" &gt;.&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:85%;"&gt;&lt;span style="font-family:Arial;"&gt;sysjobs&lt;br /&gt;GO&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/div&gt;&lt;br /&gt;&lt;p&gt;&lt;span style=";font-family:Arial;font-size:85%;"  &gt;Using the OPENROWSET command creates two caveats.  The first caveat is the need to have an &lt;a href="http://www.mssqltips.com/tipimages/1202_testing.xls"&gt;Excel spreadsheet&lt;/a&gt; serve as a template in the needed directory with the correct worksheets and columns.  Without this the you would receive an error message.  The second caveat is that it is necessary to enable the OPENROWSET command with the &lt;a href="http://www.mssqltips.com/tip.asp?tip=1020" target="_blank"&gt;SQL Server 2005 Surface Area Configuration&lt;/a&gt; utility.  Note - This step is not needed for SQL Server 2000. With the loop example you could copy and paste the Excel spreadsheet and load the data as needed. &lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style=";font-family:Arial;font-size:85%;"  &gt;Although the example above is very simple, you could integrate this simple example into your code.  For example, if you had the need to write out a number of reports in the same format, you could loop (WHILE loop or Cursor) over the records and write them out to different Excel spreadsheets based on the name or report type.   In addition, you could integrate this code with either SQL Server 2000 mail or Database mail (SQL Server 2005) and mail the results to the users with very little effort and build the process entirely with T-SQL.&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style=";font-family:Arial;font-size:85%;"  &gt;&lt;u&gt;&lt;b&gt;Next Steps&lt;/b&gt;&lt;/u&gt;&lt;/span&gt;&lt;br /&gt;&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;span style=";font-family:Arial;font-size:85%;"  &gt;Depending on how your users analyze data and the software infrastructure you have in place, Excel may be a simple and easy solution for your users to receive their data in a familiar interface for them to conduct the needed analysis.  I am surprised by the sophisticated Excel infrastructure at some organizations, but these sorts of applications seem to grow organically.&lt;/span&gt;&lt;br /&gt;&lt;/li&gt;&lt;li&gt;&lt;span style=";font-family:Arial;font-size:85%;"  &gt;The next time you are faced with exporting SQL Server data to Excel take a step back and understand the requirements then determine which of the following options best meets the need:&lt;/span&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;&lt;span style=";font-family:Arial;font-size:85%;"  &gt;&lt;a href="http://www.mssqltips.com/category.asp?catid=13" target="_blank"&gt;Data Transformation Services&lt;/a&gt; &lt;/span&gt;&lt;br /&gt;&lt;/li&gt;&lt;li&gt;&lt;span style=";font-family:Arial;font-size:85%;"  &gt;&lt;a href="http://www.mssqltips.com/category.asp?catid=17" target="_blank"&gt;SQL Server Integration Services&lt;/a&gt; &lt;/span&gt;&lt;br /&gt;&lt;/li&gt;&lt;li&gt;&lt;span style=";font-family:Arial;font-size:85%;"  &gt;&lt;a href="http://www.mssqltips.com/tip.asp?tip=1185" target="_blank"&gt;Bulk Copy&lt;/a&gt; (BCP)&lt;/span&gt;&lt;br /&gt;&lt;/li&gt;&lt;li&gt;&lt;span style=";font-family:Arial;font-size:85%;"  &gt;OPENROWSET &lt;/span&gt;&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;&lt;/li&gt;&lt;li&gt;&lt;span style=";font-family:Arial;font-size:85%;"  &gt;Check out the following &lt;a href="http://www.mssqltips.com/" target="_blank"&gt;MSSQLTips.com&lt;/a&gt; tips related to data import\export:&lt;/span&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;&lt;span style=";font-family:Arial;font-size:85%;"  &gt;&lt;a href="http://www.mssqltips.com/tip.asp?tip=1055" target="_blank"&gt;SQL Server 2000 to 2005 Crosswalk - The SQL Server Import and Export Wizard&lt;/a&gt;&lt;/span&gt;&lt;br /&gt;&lt;/li&gt;&lt;li&gt;&lt;span style=";font-family:Arial;font-size:85%;"  &gt;&lt;a href="http://www.mssqltips.com/tip.asp?tip=1060" target="_blank"&gt;BCP XML Format Files with SQL Server 2005&lt;/a&gt;&lt;/span&gt;&lt;br /&gt;&lt;/li&gt;&lt;li&gt;&lt;span style=";font-family:Arial;font-size:85%;"  &gt;&lt;a href="http://www.mssqltips.com/tip.asp?tip=1084" target="_blank"&gt;Dynamic Flat File Connections in SQL Server Integration Services&lt;/a&gt;&lt;br /&gt;&lt;/span&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7516218764458010868-2895560382280794778?l=all-about-database.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://all-about-database.blogspot.com/feeds/2895560382280794778/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7516218764458010868&amp;postID=2895560382280794778' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7516218764458010868/posts/default/2895560382280794778'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7516218764458010868/posts/default/2895560382280794778'/><link rel='alternate' type='text/html' href='http://all-about-database.blogspot.com/2008/09/export-data-from-sql-server-to-excel.html' title='Export data from SQL Server to Excel'/><author><name>firman.arrow</name><uri>http://www.blogger.com/profile/14656110271655711161</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7516218764458010868.post-2819092817506150587</id><published>2008-09-23T21:44:00.000-07:00</published><updated>2008-09-23T22:25:28.846-07:00</updated><title type='text'>Getting IO and time statistics for SQL Server queries</title><content type='html'>&lt;p&gt;&lt;b&gt;Problem&lt;/b&gt;&lt;br /&gt;If you're like any other DBA or developer in the world, writing sound T-SQL code and performance tuning is one of your primary duties. There are many tools, both native to SQL Server and third-party, that assist you in this endeavor.  But in thinking about writing and tuning queries, what should the focus be? For many, the length of time taken to execute the query is "good enough". One focus should be on the resources used by the server, since the length of time taken to execute the query can vary based on other server activity. In addition to using Profiler and Execution Plans, consider using &lt;i&gt;SET STATISTICS IO&lt;/i&gt; and &lt;i&gt;SET STATISTICS TIME&lt;/i&gt;. &lt;/p&gt;&lt;a href="http://all-about-database.blogspot.com/2008/09/getting-io-and-time-statistics-for-sql.html"&gt;..Read More&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class="fullpost"&gt;&lt;br /&gt;&lt;p&gt;&lt;u&gt;&lt;b&gt;Solution&lt;/b&gt;&lt;/u&gt;&lt;br /&gt;SET STATISTICS IO and SET STATISTICS TIME are two settings that can help you measure the absolute resources needed by a server during query execution. &lt;i&gt;SET STATISTICS IO&lt;/i&gt; displays statistics on the amount of disk activity generated by the query. &lt;i&gt;SET STATISTICS TIME&lt;/i&gt; displays the amount of time needed to parse, compile, and execute each statement in the query. By writing a query a few different ways you can compare the statistics and determine the best statement to deploy to production.&lt;br /&gt;&lt;br /&gt;Turning these settings on can be done one of two ways.  First this can be done by using the SET commands to turn on and off these options or you can turn these options on and off by using the option settings in Query Analyzer.&lt;/p&gt;&lt;br /&gt;&lt;p&gt;Before you run the commands you need to turn on these settings and then issue your SQL statements.  In addition, once these are turned on you will get these statistics for all queries for the current session you are working in until you have turned these settings off.&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;&lt;u&gt;TRANSACT-SQL STATEMENT&lt;/u&gt; (added at the beginning of the statement). &lt;/p&gt;&lt;br /&gt;&lt;p&gt;Here is an example of turning the STATISTICS IO on and off.&lt;/p&gt;&lt;br /&gt;&lt;center&gt;&lt;br /&gt;&lt;table border="1" cellpadding="4" cellspacing="0" width="400"&gt;&lt;br /&gt;&lt;tbody&gt;&lt;br /&gt;&lt;tr valign="top"&gt;&lt;br /&gt;&lt;td style="font-size: small; color: red; font-family: Arial; text-align: left;"&gt;&lt;span style="font-size:85%;"&gt;&lt;b&gt;-- turn on statistics IO&lt;/b&gt;&lt;/span&gt;&lt;b&gt;&lt;span style="font-size:85%;"&gt;&lt;br /&gt;SET STATISTICS IO ON&lt;br /&gt;GO&lt;/span&gt;&lt;/b&gt;&lt;br /&gt;&lt;p&gt;&lt;span style="font-size:85%;"&gt;-- your query goes here&lt;br /&gt;&lt;b&gt;SELECT * FROM Employee&lt;/b&gt;&lt;br /&gt;&lt;b&gt;GO&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;&lt;span style="font-size:85%;"&gt;&lt;b&gt;-- turn off statistics IO&lt;/b&gt;&lt;/span&gt;&lt;span style="font-size:85%;color:red;"&gt;&lt;b&gt;&lt;br /&gt;SET STATISTICS IO OFF&lt;br /&gt;GO&lt;/b&gt; &lt;/span&gt;&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/center&gt;&lt;br /&gt;&lt;p&gt;Here is an example of the output with SET STATISTICS IO turned on (the query statement evaluated is a cursor).  From this we can see that there are several iterations for each time the cursor loops through the data.  Each of these shows how many pages are accessed for each process that SQL Server executes.&lt;/p&gt;&lt;br /&gt;&lt;center&gt;&lt;img src="http://www.mssqltips.com/tipimages/1255_SET_STATISTICS_IO.jpg" height="634" width="599" /&gt; &lt;!--Place the SET STATISTICS IO output image here: "SET_STATISTICS_IO.bmp" --&gt;&lt;/center&gt;&lt;br /&gt;&lt;p&gt;Here is an example of the output with SET STATISTICS TIME turned on.  This output shows the time in milliseconds for each operation to complete.  These times could vary depending on when this is run, the load on the system as well as the size of the data set.&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;center&gt;&lt;img src="http://www.mssqltips.com/tipimages/1255_SET_STATISTICS_TIME.jpg" height="599" width="400" /&gt; &lt;!--Place the SET STATISTICS TIME output image here: "SET_STATISTICS_TIME.bmp" --&gt;&lt;/center&gt;&lt;br /&gt;&lt;p&gt;&lt;u&gt;WITHIN QUERY ANALYZER&lt;/u&gt;&lt;br /&gt;To set the STATISTICS IO and STATISTICS TIME options inside Query Analyzer, click Tools -&amp;gt; Options -&amp;gt; Connection Properties and then check either or both "Set statistics time" or "Set statistics IO".&lt;/p&gt;&lt;br /&gt;&lt;center&gt;&lt;img src="http://www.mssqltips.com/tipimages/1255_QueryAnalyzer-SET_Options.jpg" height="416" width="506" /&gt; &lt;!--Place the Query Analyzer output image here: "QueryAnalyzer-SET_Options.bmp" --&gt;&lt;/center&gt;&lt;br /&gt;&lt;p align="left"&gt;There you have it.  Two more additional pieces of information that can be used to determine how your queries are executing.  The data you get from the execution plan is helpful, but these additional pieces of information provide additional insight as to how your queries are performing.&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;&lt;u&gt;&lt;b&gt;Next Steps&lt;/b&gt;&lt;/u&gt; &lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;li&gt;When testing or tuning queries, use the SET STATISTICS IO and TIME options to shed more light on what server resources are being used&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Read more information about setting &lt;a href="http://msdn2.microsoft.com/en-us/library/aa259191%28SQL.80%29.aspx" target="_blank"&gt;STATISTICS IO options in SQL Server 2000&lt;/a&gt; and &lt;a href="http://msdn2.microsoft.com/en-us/library/ms184361.aspx" target="_blank"&gt;STATISTICS IO options in SQL Server 2005&lt;/a&gt;&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Read more information about setting &lt;a href="http://msdn2.microsoft.com/en-us/library/aa259194%28SQL.80%29.aspx" target="_blank"&gt;STATISTICS TIME options in SQL Server 2000&lt;/a&gt; and &lt;a href="http://msdn2.microsoft.com/en-us/library/ms190287.aspx" target="_blank"&gt;STATISTICS TIME options in SQL Server 2005&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Read information on &lt;a href="http://msdn2.microsoft.com/en-us/library/ms191227.aspx" target="_blank"&gt;Analyzing a Query in SQL Server 2005&lt;/a&gt;&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Find more great tips on &lt;a href="http://www.mssqltips.com/category.asp?catid=37"&gt;Query Optimization&lt;/a&gt; and &lt;a href="http://www.mssqltips.com/category.asp?catid=9"&gt;Performance Tuning&lt;/a&gt; on &lt;a href="http://www.mssqltips.com/"&gt;MSSQLTIPS.com&lt;/a&gt;!&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Thanks go out to Tim Cullen for providing this tip&lt;/li&gt;&lt;br /&gt;&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7516218764458010868-2819092817506150587?l=all-about-database.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://all-about-database.blogspot.com/feeds/2819092817506150587/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7516218764458010868&amp;postID=2819092817506150587' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7516218764458010868/posts/default/2819092817506150587'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7516218764458010868/posts/default/2819092817506150587'/><link rel='alternate' type='text/html' href='http://all-about-database.blogspot.com/2008/09/getting-io-and-time-statistics-for-sql.html' title='Getting IO and time statistics for SQL Server queries'/><author><name>firman.arrow</name><uri>http://www.blogger.com/profile/14656110271655711161</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7516218764458010868.post-2825739798003264728</id><published>2008-09-23T21:41:00.000-07:00</published><updated>2008-09-23T21:44:25.775-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='more'/><category scheme='http://www.blogger.com/atom/ns#' term='SQL Server'/><category scheme='http://www.blogger.com/atom/ns#' term='Server Monitoring'/><title type='text'>Gathering I/O statistics down to the SQL Server database file level</title><content type='html'>&lt;p&gt;&lt;b&gt;Problem&lt;/b&gt;&lt;br /&gt;When managing your SQL Server environment there are many aspects that need to be reviewed to determine where the bottlenecks are occurring to ensure you are getting the best performance possible.  SQL Server offers many great tools and functions to determine issues with locking, blocking, fragmentation, missing indexes, deadlocks, etc... In addition, to looking at all of these areas another area of concern is I/O.  Disk I/O can be tracked at the OS level by using counters in Performance Monitor, but these counters give you an overall picture of what is occurring on the server.  What other options are available to look at I/O related information down to the file level for each database?&lt;/p&gt;&lt;br /&gt;&lt;span class="fullpost"&gt;&lt;p&gt;&lt;b&gt;&lt;u&gt;Solution&lt;br /&gt;&lt;/u&gt;&lt;/b&gt;As mentioned above, SQL Server offers many great little functions and utility programs to help you gain an insight as to what is occurring on the server.  One of these tools is &lt;span codelanguage="other"&gt;&lt;a href="http://msdn2.microsoft.com/en-us/library/ms187309.aspx" target="_blank"&gt;fn_virtualfilestats&lt;/a&gt;.&lt;/span&gt;&lt;/p&gt;&lt;p&gt;This function, &lt;span codelanguage="other"&gt;fn_virtualfilestats allows you to get information for each physical file that is being used to hold your data including both the data and log files. The function returns read and write information as well as stall information, which is the time users had to wait for an I/O operation to complete.  Each time this function is called it returns the overall numbers that SQL Server has collected since the last time the database engine was started, so to use this effectively you need to gather data from two different points of time and then do a comparison.&lt;/span&gt;&lt;/p&gt;&lt;p&gt;To run this function to get data for all databases and all files this can be done as easily as this:&lt;/p&gt;&lt;p&gt;&lt;b&gt;SQL 2005&lt;/b&gt;&lt;/p&gt;&lt;table border="1" cellpadding="4" cellspacing="0" width="100%"&gt;&lt;br /&gt;&lt;br /&gt;&lt;tbody&gt;&lt;br /&gt;&lt;tr&gt;&lt;br /&gt;&lt;td&gt;&lt;span style="font-family:Courier New;font-size:85%;"&gt;SELECT * FROM fn_virtualfilestats(NULL,NULL);&lt;/span&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;br /&gt;&lt;p&gt;&lt;b&gt;SQL 2000&lt;/b&gt;&lt;/p&gt;&lt;table border="1" cellpadding="4" cellspacing="0" width="100%"&gt;&lt;br /&gt;&lt;tbody&gt;&lt;br /&gt;&lt;tr&gt;&lt;br /&gt;&lt;td&gt;&lt;span style="font-family:Courier New;font-size:85%;"&gt;SELECT * FROM :: fn_virtualfilestats(-1, -1)&lt;/span&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;br /&gt;&lt;p&gt;The output for SQL 2000 and 2005 is pretty much the same, but some additional columns have been added for SQL Server 2005.&lt;/p&gt;&lt;table xmlns="" border="1" cellpadding="2" cellspacing="0" width="100%"&gt;&lt;br /&gt;&lt;tbody&gt;&lt;br /&gt;&lt;tr&gt;&lt;br /&gt;&lt;th bg style="color:#c0c0c0;"&gt;&lt;span style="font-family:Arial;font-size:85%;"&gt;Column Name &lt;/span&gt;&lt;/th&gt;&lt;br /&gt;&lt;br /&gt;&lt;th bg style="color:#c0c0c0;"&gt;&lt;span style="font-family:Arial;font-size:85%;"&gt;Notes&lt;/span&gt;&lt;/th&gt;&lt;br /&gt;&lt;th bg style="color:#c0c0c0;"&gt;&lt;span style="font-family:Arial;font-size:85%;"&gt;Description &lt;/span&gt;&lt;/th&gt;&lt;/tr&gt;&lt;br /&gt;&lt;tr&gt;&lt;br /&gt;&lt;td&gt;&lt;span style="font-family:Arial;font-size:85%;"&gt;DbId&lt;/span&gt;&lt;/td&gt;&lt;br /&gt;&lt;td&gt; &lt;/td&gt;&lt;br /&gt;&lt;td&gt;&lt;span style="font-family:Arial;font-size:85%;"&gt;Database ID.&lt;/span&gt;&lt;/td&gt;&lt;/tr&gt;&lt;br /&gt;&lt;tr&gt;&lt;br /&gt;&lt;td&gt;&lt;span style="font-family:Arial;font-size:85%;"&gt;FileId&lt;/span&gt;&lt;/td&gt;&lt;br /&gt;&lt;td&gt; &lt;/td&gt;&lt;br /&gt;&lt;td&gt;&lt;span style="font-family:Arial;font-size:85%;"&gt;File ID.&lt;/span&gt;&lt;/td&gt;&lt;/tr&gt;&lt;br /&gt;&lt;tr&gt;&lt;br /&gt;&lt;br /&gt;&lt;td&gt;&lt;span style="font-family:Arial;font-size:85%;"&gt;TimeStamp&lt;/span&gt;&lt;/td&gt;&lt;br /&gt;&lt;td&gt; &lt;/td&gt;&lt;br /&gt;&lt;td&gt;&lt;span style="font-family:Arial;font-size:85%;"&gt;Database timestamp at which the data was taken.&lt;/span&gt;&lt;/td&gt;&lt;/tr&gt;&lt;br /&gt;&lt;tr&gt;&lt;br /&gt;&lt;td&gt;&lt;span style="font-family:Arial;font-size:85%;"&gt;NumberReads&lt;/span&gt;&lt;/td&gt;&lt;br /&gt;&lt;td&gt; &lt;/td&gt;&lt;br /&gt;&lt;td&gt;&lt;span style="font-family:Arial;font-size:85%;"&gt;Number of reads issued on the file.&lt;/span&gt;&lt;/td&gt;&lt;/tr&gt;&lt;br /&gt;&lt;tr&gt;&lt;br /&gt;&lt;td&gt;&lt;span style="font-family:Arial;font-size:85%;"&gt;BytesRead&lt;/span&gt;&lt;/td&gt;&lt;br /&gt;&lt;td&gt; &lt;/td&gt;&lt;br /&gt;&lt;td&gt;&lt;span style="font-family:Arial;font-size:85%;"&gt;Number of bytes read issued on the file.&lt;/span&gt;&lt;/td&gt;&lt;/tr&gt;&lt;br /&gt;&lt;br /&gt;&lt;tr&gt;&lt;br /&gt;&lt;td&gt;&lt;span style="font-family:Arial;font-size:85%;"&gt;IoStallReadMS&lt;/span&gt;&lt;/td&gt;&lt;br /&gt;&lt;td&gt;&lt;span style="font-family:Arial;font-size:85%;"&gt;SQL2005  only&lt;/span&gt;&lt;/td&gt;&lt;br /&gt;&lt;td&gt;&lt;span style="font-family:Arial;font-size:85%;"&gt;Total amount of time, in milliseconds, that users waited for the read I/Os to complete on the file.&lt;/span&gt;&lt;/td&gt;&lt;/tr&gt;&lt;br /&gt;&lt;tr&gt;&lt;br /&gt;&lt;td&gt;&lt;span style="font-family:Arial;font-size:85%;"&gt;NumberWrites&lt;/span&gt;&lt;/td&gt;&lt;br /&gt;&lt;td&gt; &lt;/td&gt;&lt;br /&gt;&lt;td&gt;&lt;span style="font-family:Arial;font-size:85%;"&gt;Number of writes made on the file.&lt;/span&gt;&lt;/td&gt;&lt;/tr&gt;&lt;br /&gt;&lt;tr&gt;&lt;br /&gt;&lt;td&gt;&lt;span style="font-family:Arial;font-size:85%;"&gt;BytesWritten&lt;/span&gt;&lt;/td&gt;&lt;br /&gt;&lt;br /&gt;&lt;td&gt; &lt;/td&gt;&lt;br /&gt;&lt;td&gt;&lt;span style="font-family:Arial;font-size:85%;"&gt;Number of bytes written made on the file.&lt;/span&gt;&lt;/td&gt;&lt;/tr&gt;&lt;br /&gt;&lt;tr&gt;&lt;br /&gt;&lt;td&gt;&lt;span style="font-family:Arial;font-size:85%;"&gt;IoStallWriteMS&lt;/span&gt;&lt;/td&gt;&lt;br /&gt;&lt;td&gt;&lt;span style="font-family:Arial;font-size:85%;"&gt;SQL2005  only&lt;/span&gt;&lt;/td&gt;&lt;br /&gt;&lt;td&gt;&lt;span style="font-family:Arial;font-size:85%;"&gt;Total amount of time, in milliseconds, that users waited for the write I/Os to complete on the file.&lt;/span&gt;&lt;/td&gt;&lt;/tr&gt;&lt;br /&gt;&lt;tr&gt;&lt;br /&gt;&lt;td&gt;&lt;span style="font-family:Arial;font-size:85%;"&gt;IoStallMS&lt;/span&gt;&lt;/td&gt;&lt;br /&gt;&lt;td&gt; &lt;/td&gt;&lt;br /&gt;&lt;td&gt;&lt;span style="font-family:Arial;font-size:85%;"&gt;Sum of &lt;b&gt;IoStallReadMS&lt;/b&gt; and &lt;b&gt;IoStallWriteMS&lt;/b&gt;.&lt;/span&gt;&lt;/td&gt;&lt;/tr&gt;&lt;br /&gt;&lt;br /&gt;&lt;tr&gt;&lt;br /&gt;&lt;td&gt;&lt;span style="font-family:Arial;font-size:85%;"&gt;FileHandle&lt;/span&gt;&lt;/td&gt;&lt;br /&gt;&lt;td&gt; &lt;/td&gt;&lt;br /&gt;&lt;td&gt;&lt;span style="font-family:Arial;font-size:85%;"&gt;Value of the file handle.&lt;/span&gt;&lt;/td&gt;&lt;/tr&gt;&lt;br /&gt;&lt;tr&gt;&lt;br /&gt;&lt;td&gt;&lt;span style="font-family:Arial;font-size:85%;"&gt;BytesOnDisk&lt;/span&gt;&lt;/td&gt;&lt;br /&gt;&lt;td&gt;&lt;span style="font-family:Arial;font-size:85%;"&gt;SQL2005  only&lt;/span&gt;&lt;/td&gt;&lt;br /&gt;&lt;td&gt;&lt;span style="font-family:Arial;font-size:85%;"&gt;Physical file size (count of bytes) on disk.&lt;/span&gt;&lt;br /&gt;&lt;p&gt;&lt;span style="font-family:Arial;font-size:85%;"&gt;For database files, this is the same value as &lt;b&gt;size&lt;/b&gt; in &lt;b&gt;sys.database_files&lt;/b&gt;, but is expressed in bytes rather than pages.&lt;/span&gt;&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;&lt;span style="font-family:Arial;font-size:85%;"&gt;For database snapshot spare files, this is the space the operating system is using for the file.&lt;/span&gt;&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;br /&gt;&lt;p&gt;&lt;span style="font-size:85%;"&gt;(Source SQL Server 2005 Books Online)&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;b&gt;Sample Output&lt;/b&gt;&lt;/p&gt;&lt;p&gt;As you can see from the sample output, the Dbid and FileId columns are pretty cryptic.  The Dbid can be be translated to the database  name pretty easily by using the DB_NAME() function, but the fileId needs to be looked up from one of the system tables.&lt;/p&gt;&lt;p&gt;&lt;img src="http://www.mssqltips.com/tipimages/1416_v1.gif" border="1" /&gt;&lt;/p&gt;&lt;p&gt;To lookup the filename from the system tables you can use these queries.&lt;/p&gt;&lt;p&gt;&lt;b&gt;SQL 2005&lt;/b&gt;&lt;/p&gt;&lt;table border="1" cellpadding="4" cellspacing="0" width="100%"&gt;&lt;br /&gt;&lt;tbody&gt;&lt;br /&gt;&lt;tr&gt;&lt;br /&gt;&lt;td&gt;&lt;span style="font-family:Courier New;font-size:85%;"&gt;SELECT dbid, fileid, filename&lt;br /&gt;FROM sys.sysaltfiles&lt;br /&gt;WHERE dbid = 5 and fileid in (1,2)&lt;/span&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;br /&gt;&lt;p&gt;&lt;b&gt;SQL 2000&lt;/b&gt;&lt;/p&gt;&lt;table border="1" cellpadding="4" cellspacing="0" width="100%"&gt;&lt;br /&gt;&lt;tbody&gt;&lt;br /&gt;&lt;tr&gt;&lt;br /&gt;&lt;td&gt;&lt;span style="font-family:Courier New;font-size:85%;"&gt;SELECT dbid, fileid, filename&lt;br /&gt;FROM dbo.sysaltfiles&lt;br /&gt;WHERE dbid = 5 and fileid in (1,2)&lt;/span&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;br /&gt;&lt;p&gt;Here is sample output.&lt;/p&gt;&lt;p&gt;&lt;img src="http://www.mssqltips.com/tipimages/1416_v2.gif" border="1" /&gt;&lt;/p&gt;&lt;p&gt;From just using this function directly you can gather data from two different points in time and then do a comparison to determine the change that has occurred between these two periods of time.  Here is a sample query that gathers data, waits for a period of time and then gathers data again to show you a comparison.&lt;/p&gt;&lt;p&gt;This example is written for SQL Server 2005, but can easily be changed for SQL 2000.&lt;/p&gt;&lt;table border="1" cellpadding="4" cellspacing="0" width="100%"&gt;&lt;br /&gt;&lt;tbody&gt;&lt;br /&gt;&lt;tr&gt;&lt;br /&gt;&lt;td&gt;&lt;code  style="font-size:12px;"&gt;&lt;span style="color:blue;"&gt;USE &lt;/span&gt;&lt;span style="color:black;"&gt;master&lt;br /&gt;GO&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="color:green;"&gt;-- create table&lt;br /&gt;&lt;/span&gt;&lt;span style="color:blue;"&gt;IF &lt;/span&gt;&lt;span style="color:gray;"&gt;NOT EXISTS (&lt;/span&gt;&lt;span style="color:blue;"&gt;SELECT &lt;/span&gt;&lt;span style="color:gray;"&gt;* &lt;br /&gt;       &lt;/span&gt;&lt;span style="color:blue;"&gt;FROM &lt;/span&gt;&lt;span style="color:black;"&gt;sys.objects &lt;br /&gt;       &lt;/span&gt;&lt;span style="color:blue;"&gt;WHERE &lt;/span&gt;&lt;span style="color:magenta;"&gt;OBJECT_ID &lt;/span&gt;&lt;span style="color:blue;"&gt;= &lt;/span&gt;&lt;span style="color:magenta;"&gt;OBJECT_ID&lt;/span&gt;&lt;span style="color:gray;"&gt;(&lt;/span&gt;&lt;span style="color:red;"&gt;N'[dbo].[filestats]'&lt;/span&gt;&lt;span style="color:gray;"&gt;) &lt;br /&gt;       AND &lt;/span&gt;&lt;span style="color:black;"&gt;type &lt;/span&gt;&lt;span style="color:blue;"&gt;IN &lt;/span&gt;&lt;span style="color:gray;"&gt;(&lt;/span&gt;&lt;span style="color:red;"&gt;N'U'&lt;/span&gt;&lt;span style="color:gray;"&gt;))&lt;br /&gt;&lt;/span&gt;&lt;span style="color:blue;"&gt;BEGIN&lt;br /&gt;   CREATE TABLE &lt;/span&gt;&lt;span style="color:black;"&gt;filestats&lt;br /&gt;    &lt;/span&gt;&lt;span style="color:gray;"&gt;(&lt;/span&gt;&lt;span style="color:black;"&gt;dbname  &lt;/span&gt;&lt;span style="color:blue;"&gt;VARCHAR&lt;/span&gt;&lt;span style="color:gray;"&gt;(&lt;/span&gt;&lt;span style="color:black;"&gt;128&lt;/span&gt;&lt;span style="color:gray;"&gt;),&lt;br /&gt;    &lt;/span&gt;&lt;span style="color:black;"&gt;fName  &lt;/span&gt;&lt;span style="color:blue;"&gt;VARCHAR&lt;/span&gt;&lt;span style="color:gray;"&gt;(&lt;/span&gt;&lt;span style="color:black;"&gt;2048&lt;/span&gt;&lt;span style="color:gray;"&gt;), &lt;br /&gt;    &lt;/span&gt;&lt;span style="color:black;"&gt;timeStart  datetime&lt;/span&gt;&lt;span style="color:gray;"&gt;,&lt;br /&gt;    &lt;/span&gt;&lt;span style="color:black;"&gt;timeEnd datetime&lt;/span&gt;&lt;span style="color:gray;"&gt;,&lt;br /&gt;    &lt;/span&gt;&lt;span style="color:black;"&gt;timeDiff bigint&lt;/span&gt;&lt;span style="color:gray;"&gt;,&lt;br /&gt;    &lt;/span&gt;&lt;span style="color:black;"&gt;readsNum1 bigint&lt;/span&gt;&lt;span style="color:gray;"&gt;,&lt;br /&gt;    &lt;/span&gt;&lt;span style="color:black;"&gt;readsNum2 bigint&lt;/span&gt;&lt;span style="color:gray;"&gt;,&lt;br /&gt;    &lt;/span&gt;&lt;span style="color:black;"&gt;readsBytes1 bigint&lt;/span&gt;&lt;span style="color:gray;"&gt;,&lt;br /&gt;    &lt;/span&gt;&lt;span style="color:black;"&gt;readsBytes2 bigint&lt;/span&gt;&lt;span style="color:gray;"&gt;,&lt;br /&gt;    &lt;/span&gt;&lt;span style="color:black;"&gt;readsIoStall1 bigint&lt;/span&gt;&lt;span style="color:gray;"&gt;,&lt;br /&gt;    &lt;/span&gt;&lt;span style="color:black;"&gt;readsIoStall2 bigint&lt;/span&gt;&lt;span style="color:gray;"&gt;,&lt;br /&gt;    &lt;/span&gt;&lt;span style="color:black;"&gt;writesNum1 bigint&lt;/span&gt;&lt;span style="color:gray;"&gt;,&lt;br /&gt;    &lt;/span&gt;&lt;span style="color:black;"&gt;writesNum2 bigint&lt;/span&gt;&lt;span style="color:gray;"&gt;,&lt;br /&gt;    &lt;/span&gt;&lt;span style="color:black;"&gt;writesBytes1 bigint&lt;/span&gt;&lt;span style="color:gray;"&gt;,&lt;br /&gt;    &lt;/span&gt;&lt;span style="color:black;"&gt;writesBytes2 bigint&lt;/span&gt;&lt;span style="color:gray;"&gt;,&lt;br /&gt;    &lt;/span&gt;&lt;span style="color:black;"&gt;writesIoStall1 bigint&lt;/span&gt;&lt;span style="color:gray;"&gt;,&lt;br /&gt;    &lt;/span&gt;&lt;span style="color:black;"&gt;writesIoStall2 bigint&lt;/span&gt;&lt;span style="color:gray;"&gt;,&lt;br /&gt;    &lt;/span&gt;&lt;span style="color:black;"&gt;ioStall1 bigint&lt;/span&gt;&lt;span style="color:gray;"&gt;,&lt;br /&gt;    &lt;/span&gt;&lt;span style="color:black;"&gt;ioStall2 bigint&lt;br /&gt;    &lt;/span&gt;&lt;span style="color:gray;"&gt;)&lt;br /&gt;&lt;/span&gt;&lt;span style="color:blue;"&gt;END&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="color:green;"&gt;-- clear data&lt;br /&gt;&lt;/span&gt;&lt;span style="color:blue;"&gt;TRUNCATE TABLE &lt;/span&gt;&lt;span style="color:black;"&gt;dbo.filestats&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="color:green;"&gt;-- insert first segment counters&lt;br /&gt;&lt;/span&gt;&lt;span style="color:blue;"&gt;INSERT INTO &lt;/span&gt;&lt;span style="color:black;"&gt;dbo.filestats&lt;br /&gt;   &lt;/span&gt;&lt;span style="color:gray;"&gt;(&lt;/span&gt;&lt;span style="color:black;"&gt;dbname&lt;/span&gt;&lt;span style="color:gray;"&gt;,&lt;br /&gt;   &lt;/span&gt;&lt;span style="color:black;"&gt;fName&lt;/span&gt;&lt;span style="color:gray;"&gt;, &lt;br /&gt;   &lt;/span&gt;&lt;span style="color:black;"&gt;TimeStart&lt;/span&gt;&lt;span style="color:gray;"&gt;,&lt;br /&gt;   &lt;/span&gt;&lt;span style="color:black;"&gt;readsNum1&lt;/span&gt;&lt;span style="color:gray;"&gt;,&lt;br /&gt;   &lt;/span&gt;&lt;span style="color:black;"&gt;readsBytes1&lt;/span&gt;&lt;span style="color:gray;"&gt;,&lt;br /&gt;   &lt;/span&gt;&lt;span style="color:black;"&gt;readsIoStall1&lt;/span&gt;&lt;span style="color:gray;"&gt;, &lt;br /&gt;   &lt;/span&gt;&lt;span style="color:black;"&gt;writesNum1&lt;/span&gt;&lt;span style="color:gray;"&gt;,&lt;br /&gt;   &lt;/span&gt;&lt;span style="color:black;"&gt;writesBytes1&lt;/span&gt;&lt;span style="color:gray;"&gt;,&lt;br /&gt;   &lt;/span&gt;&lt;span style="color:black;"&gt;writesIoStall1&lt;/span&gt;&lt;span style="color:gray;"&gt;, &lt;br /&gt;   &lt;/span&gt;&lt;span style="color:black;"&gt;IoStall1&lt;br /&gt;   &lt;/span&gt;&lt;span style="color:gray;"&gt;)&lt;br /&gt;&lt;/span&gt;&lt;span style="color:blue;"&gt;SELECT &lt;br /&gt;   &lt;/span&gt;&lt;span style="color:magenta;"&gt;DB_NAME&lt;/span&gt;&lt;span style="color:gray;"&gt;(&lt;/span&gt;&lt;span style="color:black;"&gt;a.dbid&lt;/span&gt;&lt;span style="color:gray;"&gt;) &lt;/span&gt;&lt;span style="color:blue;"&gt;AS &lt;/span&gt;&lt;span style="color:black;"&gt;Database_name&lt;/span&gt;&lt;span style="color:gray;"&gt;,&lt;br /&gt;   &lt;/span&gt;&lt;span style="color:black;"&gt;b.filename&lt;/span&gt;&lt;span style="color:gray;"&gt;,&lt;br /&gt;   &lt;/span&gt;&lt;span style="color:magenta;"&gt;GETDATE&lt;/span&gt;&lt;span style="color:gray;"&gt;(),&lt;br /&gt;   &lt;/span&gt;&lt;span style="color:black;"&gt;numberReads&lt;/span&gt;&lt;span style="color:gray;"&gt;,&lt;br /&gt;   &lt;/span&gt;&lt;span style="color:black;"&gt;BytesRead&lt;/span&gt;&lt;span style="color:gray;"&gt;,&lt;br /&gt;   &lt;/span&gt;&lt;span style="color:black;"&gt;IoStallReadMS&lt;/span&gt;&lt;span style="color:gray;"&gt;,&lt;br /&gt;   &lt;/span&gt;&lt;span style="color:black;"&gt;NumberWrites&lt;/span&gt;&lt;span style="color:gray;"&gt;,&lt;br /&gt;   &lt;/span&gt;&lt;span style="color:black;"&gt;BytesWritten&lt;/span&gt;&lt;span style="color:gray;"&gt;,&lt;br /&gt;   &lt;/span&gt;&lt;span style="color:black;"&gt;IoStallWriteMS&lt;/span&gt;&lt;span style="color:gray;"&gt;,&lt;br /&gt;   &lt;/span&gt;&lt;span style="color:black;"&gt;IoStallMS&lt;br /&gt;&lt;/span&gt;&lt;span style="color:blue;"&gt;FROM &lt;br /&gt;   &lt;/span&gt;&lt;span style="color:darkred;"&gt;fn_virtualfilestats&lt;/span&gt;&lt;span style="color:gray;"&gt;(NULL,NULL) &lt;/span&gt;&lt;span style="color:black;"&gt;a &lt;/span&gt;&lt;span style="color:blue;"&gt;INNER JOIN&lt;br /&gt;   &lt;/span&gt;&lt;span style="color:black;"&gt;sysaltfiles b &lt;/span&gt;&lt;span style="color:blue;"&gt;ON &lt;/span&gt;&lt;span style="color:black;"&gt;a.dbid &lt;/span&gt;&lt;span style="color:blue;"&gt;= &lt;/span&gt;&lt;span style="color:black;"&gt;b.dbid &lt;/span&gt;&lt;span style="color:gray;"&gt;AND &lt;/span&gt;&lt;span style="color:black;"&gt;a.fileid &lt;/span&gt;&lt;span style="color:blue;"&gt;= &lt;/span&gt;&lt;span style="color:black;"&gt;b.fileid&lt;br /&gt;&lt;/span&gt;&lt;span style="color:blue;"&gt;ORDER BY &lt;br /&gt;   &lt;/span&gt;&lt;span style="color:black;"&gt;Database_Name&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="color:green;"&gt;/*Delay second read */&lt;br /&gt;&lt;/span&gt;&lt;span style="color:blue;"&gt;WAITFOR &lt;/span&gt;&lt;span style="color:black;"&gt;DELAY &lt;/span&gt;&lt;span style="color:red;"&gt;'000:01:00'&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="color:green;"&gt;-- add second segment counters&lt;br /&gt;&lt;/span&gt;&lt;span style="color:blue;"&gt;UPDATE &lt;/span&gt;&lt;span style="color:black;"&gt;dbo.filestats &lt;br /&gt;&lt;/span&gt;&lt;span style="color:blue;"&gt;SET &lt;br /&gt;   &lt;/span&gt;&lt;span style="color:black;"&gt;timeEnd &lt;/span&gt;&lt;span style="color:blue;"&gt;= &lt;/span&gt;&lt;span style="color:magenta;"&gt;GETDATE&lt;/span&gt;&lt;span style="color:gray;"&gt;(),&lt;br /&gt;   &lt;/span&gt;&lt;span style="color:black;"&gt;readsNum2 &lt;/span&gt;&lt;span style="color:blue;"&gt;= &lt;/span&gt;&lt;span style="color:black;"&gt;a.numberReads&lt;/span&gt;&lt;span style="color:gray;"&gt;,&lt;br /&gt;   &lt;/span&gt;&lt;span style="color:black;"&gt;readsBytes2 &lt;/span&gt;&lt;span style="color:blue;"&gt;= &lt;/span&gt;&lt;span style="color:black;"&gt;a.BytesRead&lt;/span&gt;&lt;span style="color:gray;"&gt;,&lt;br /&gt;   &lt;/span&gt;&lt;span style="color:black;"&gt;readsIoStall2 &lt;/span&gt;&lt;span style="color:blue;"&gt;= &lt;/span&gt;&lt;span style="color:black;"&gt;a.IoStallReadMS &lt;/span&gt;&lt;span style="color:gray;"&gt;,&lt;br /&gt;   &lt;/span&gt;&lt;span style="color:black;"&gt;writesNum2 &lt;/span&gt;&lt;span style="color:blue;"&gt;= &lt;/span&gt;&lt;span style="color:black;"&gt;a.NumberWrites&lt;/span&gt;&lt;span style="color:gray;"&gt;,&lt;br /&gt;   &lt;/span&gt;&lt;span style="color:black;"&gt;writesBytes2 &lt;/span&gt;&lt;span style="color:blue;"&gt;= &lt;/span&gt;&lt;span style="color:black;"&gt;a.BytesWritten&lt;/span&gt;&lt;span style="color:gray;"&gt;,&lt;br /&gt;   &lt;/span&gt;&lt;span style="color:black;"&gt;writesIoStall2 &lt;/span&gt;&lt;span style="color:blue;"&gt;= &lt;/span&gt;&lt;span style="color:black;"&gt;a.IoStallWriteMS&lt;/span&gt;&lt;span style="color:gray;"&gt;,&lt;br /&gt;   &lt;/span&gt;&lt;span style="color:black;"&gt;IoStall2 &lt;/span&gt;&lt;span style="color:blue;"&gt;= &lt;/span&gt;&lt;span style="color:black;"&gt;a.IoStallMS&lt;/span&gt;&lt;span style="color:gray;"&gt;,&lt;br /&gt;   &lt;/span&gt;&lt;span style="color:black;"&gt;timeDiff &lt;/span&gt;&lt;span style="color:blue;"&gt;= &lt;/span&gt;&lt;span style="color:magenta;"&gt;DATEDIFF&lt;/span&gt;&lt;span style="color:gray;"&gt;(&lt;/span&gt;&lt;span style="color:black;"&gt;s&lt;/span&gt;&lt;span style="color:gray;"&gt;,&lt;/span&gt;&lt;span style="color:black;"&gt;timeStart&lt;/span&gt;&lt;span style="color:gray;"&gt;,&lt;/span&gt;&lt;span style="color:magenta;"&gt;GETDATE&lt;/span&gt;&lt;span style="color:gray;"&gt;())&lt;br /&gt;&lt;/span&gt;&lt;span style="color:blue;"&gt;FROM &lt;br /&gt;   &lt;/span&gt;&lt;span style="color:darkred;"&gt;fn_virtualfilestats&lt;/span&gt;&lt;span style="color:gray;"&gt;(NULL,NULL) &lt;/span&gt;&lt;span style="color:black;"&gt;a &lt;/span&gt;&lt;span style="color:blue;"&gt;INNER JOIN&lt;br /&gt;   &lt;/span&gt;&lt;span style="color:black;"&gt;sysaltfiles b &lt;/span&gt;&lt;span style="color:blue;"&gt;ON &lt;/span&gt;&lt;span style="color:black;"&gt;a.dbid &lt;/span&gt;&lt;span style="color:blue;"&gt;= &lt;/span&gt;&lt;span style="color:black;"&gt;b.dbid &lt;/span&gt;&lt;span style="color:gray;"&gt;AND &lt;/span&gt;&lt;span style="color:black;"&gt;a.fileid &lt;/span&gt;&lt;span style="color:blue;"&gt;= &lt;/span&gt;&lt;span style="color:black;"&gt;b.fileid&lt;br /&gt;&lt;/span&gt;&lt;span style="color:blue;"&gt;WHERE   &lt;br /&gt;   &lt;/span&gt;&lt;span style="color:black;"&gt;fName&lt;/span&gt;&lt;span style="color:blue;"&gt;= &lt;/span&gt;&lt;span style="color:black;"&gt;b.filename &lt;/span&gt;&lt;span style="color:gray;"&gt;AND &lt;/span&gt;&lt;span style="color:black;"&gt;dbname&lt;/span&gt;&lt;span style="color:blue;"&gt;=&lt;/span&gt;&lt;span style="color:magenta;"&gt;DB_NAME&lt;/span&gt;&lt;span style="color:gray;"&gt;(&lt;/span&gt;&lt;span style="color:black;"&gt;a.dbid&lt;/span&gt;&lt;span style="color:gray;"&gt;)&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="color:green;"&gt;-- select data&lt;br /&gt;&lt;/span&gt;&lt;span style="color:blue;"&gt;SELECT &lt;br /&gt;   &lt;/span&gt;&lt;span style="color:black;"&gt;dbname&lt;/span&gt;&lt;span style="color:gray;"&gt;,&lt;br /&gt;   &lt;/span&gt;&lt;span style="color:black;"&gt;fName&lt;/span&gt;&lt;span style="color:gray;"&gt;,&lt;br /&gt;   &lt;/span&gt;&lt;span style="color:black;"&gt;timeDiff&lt;/span&gt;&lt;span style="color:gray;"&gt;,&lt;br /&gt;   &lt;/span&gt;&lt;span style="color:black;"&gt;readsNum2 &lt;/span&gt;&lt;span style="color:gray;"&gt;- &lt;/span&gt;&lt;span style="color:black;"&gt;readsNum1 &lt;/span&gt;&lt;span style="color:blue;"&gt;AS &lt;/span&gt;&lt;span style="color:black;"&gt;readsNumDiff&lt;/span&gt;&lt;span style="color:gray;"&gt;,&lt;br /&gt;   &lt;/span&gt;&lt;span style="color:black;"&gt;readsBytes2 &lt;/span&gt;&lt;span style="color:gray;"&gt;- &lt;/span&gt;&lt;span style="color:black;"&gt;readsBytes2 &lt;/span&gt;&lt;span style="color:blue;"&gt;AS &lt;/span&gt;&lt;span style="color:black;"&gt;readsBytesDiff&lt;/span&gt;&lt;span style="color:gray;"&gt;,&lt;br /&gt;   &lt;/span&gt;&lt;span style="color:black;"&gt;readsIoStall2 &lt;/span&gt;&lt;span style="color:gray;"&gt;- &lt;/span&gt;&lt;span style="color:black;"&gt;readsIOStall1 &lt;/span&gt;&lt;span style="color:blue;"&gt;AS &lt;/span&gt;&lt;span style="color:black;"&gt;readsIOStallDiff&lt;/span&gt;&lt;span style="color:gray;"&gt;,&lt;br /&gt;   &lt;/span&gt;&lt;span style="color:black;"&gt;writesNum2 &lt;/span&gt;&lt;span style="color:gray;"&gt;- &lt;/span&gt;&lt;span style="color:black;"&gt;writesNum1 &lt;/span&gt;&lt;span style="color:blue;"&gt;AS &lt;/span&gt;&lt;span style="color:black;"&gt;writesNumDiff&lt;/span&gt;&lt;span style="color:gray;"&gt;,&lt;br /&gt;   &lt;/span&gt;&lt;span style="color:black;"&gt;writesBytes2 &lt;/span&gt;&lt;span style="color:gray;"&gt;- &lt;/span&gt;&lt;span style="color:black;"&gt;writesBytes2 &lt;/span&gt;&lt;span style="color:blue;"&gt;AS &lt;/span&gt;&lt;span style="color:black;"&gt;writesBytesDiff&lt;/span&gt;&lt;span style="color:gray;"&gt;,&lt;br /&gt;   &lt;/span&gt;&lt;span style="color:black;"&gt;writesIoStall2 &lt;/span&gt;&lt;span style="color:gray;"&gt;- &lt;/span&gt;&lt;span style="color:black;"&gt;writesIOStall1 &lt;/span&gt;&lt;span style="color:blue;"&gt;AS &lt;/span&gt;&lt;span style="color:black;"&gt;writesIOStallDiff&lt;/span&gt;&lt;span style="color:gray;"&gt;,   &lt;br /&gt;   &lt;/span&gt;&lt;span style="color:black;"&gt;ioStall2 &lt;/span&gt;&lt;span style="color:gray;"&gt;- &lt;/span&gt;&lt;span style="color:black;"&gt;ioStall1 &lt;/span&gt;&lt;span style="color:blue;"&gt;AS &lt;/span&gt;&lt;span style="color:black;"&gt;ioStallDiff&lt;br /&gt;&lt;/span&gt;&lt;span style="color:blue;"&gt;FROM &lt;/span&gt;&lt;span style="color:black;"&gt;dbo.filestats &lt;br /&gt; &lt;/span&gt;&lt;/code&gt; &lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;br /&gt;&lt;p&gt;&lt;b&gt;Summary&lt;br /&gt;&lt;/b&gt;One problem that you may be faced with though is that not all files are stored on their own physical disks, so you may have a case where you want to look at things from a drive perspective vs. at an individual file level.  Here is a previous article written by Andy Novick that has the entire process broken down into functions, so you can aggregate things to a drive perspective.   The article can be found here, &lt;a href="http://www.novicksoftware.com/Articles/sql-server-io-statistics.htm" target="_blank"&gt;Examining SQL Server's I/O Statistics&lt;/a&gt;&lt;/p&gt;&lt;p&gt;&lt;u&gt;&lt;b&gt;Next Steps&lt;/b&gt;&lt;/u&gt;&lt;/p&gt;&lt;ul&gt;&lt;li&gt;When researching performance problems, don't forget to look at I/O stats as well.  This handy little function could give you big insight into some of your performance issues.&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Stay tuned for more performance related tips, but for now check out these &lt;a href="http://www.mssqltips.com/category.asp?catid=9"&gt;other tips&lt;/a&gt;.&lt;/li&gt;&lt;br /&gt;&lt;/ul&gt;&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7516218764458010868-2825739798003264728?l=all-about-database.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://all-about-database.blogspot.com/feeds/2825739798003264728/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7516218764458010868&amp;postID=2825739798003264728' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7516218764458010868/posts/default/2825739798003264728'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7516218764458010868/posts/default/2825739798003264728'/><link rel='alternate' type='text/html' href='http://all-about-database.blogspot.com/2008/09/gathering-io-statistics-down-to-sql.html' title='Gathering I/O statistics down to the SQL Server database file level'/><author><name>firman.arrow</name><uri>http://www.blogger.com/profile/14656110271655711161</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7516218764458010868.post-1186426228281129261</id><published>2008-09-23T18:20:00.000-07:00</published><updated>2008-09-23T21:35:13.574-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='more'/><category scheme='http://www.blogger.com/atom/ns#' term='SQL Server'/><category scheme='http://www.blogger.com/atom/ns#' term='Server Monitoring'/><title type='text'>How To Collect Performance Data With TYPEPERF.EXE</title><content type='html'>&lt;p&gt;&lt;u&gt;&lt;b&gt;Problem&lt;/b&gt;&lt;/u&gt;&lt;br /&gt;As a DBA I like to take advantage of command line tools when I'm working on performance tuning and optimizing my SQL Server databases.  One of the things I typically need to do is to collect performance data on the server which includes CPU, memory and disk utilization as well as SQL Server-specific data.  What command line tools are available to do this?&lt;/p&gt;&lt;u&gt;&lt;b&gt;Solution&lt;/b&gt;&lt;/u&gt;&lt;br /&gt;&lt;p&gt;TYPEPERF.EXE is a command line tool included with the Windows operating system that writes performance data to the command window or to a file.  It is necessary to capture performance data whenever you are trying to diagnose performance issues on a server.  Performance data provides information on the server's utilization of the processor, memory, and disk, as well as SQL Server-specific performance data.  &lt;/p&gt;&lt;br /&gt;&lt;span class="fullpost"&gt;&lt;p&gt;The Windows operating system supplies a plethora of performance data in the form of objects which have associated counters.  As an example SQL Server provides the SQLServer:General Statistics object which reports the details on logins, logouts, database connections, etc.  Some objects break out the counters into specific instances.  As an example the SQLServer:Databases object provides details on data file and transaction log file sizes, the percentage of the transaction log in use, active transactions, etc. for each database. You can specify a single database or all databases combined together as the instance.  Unfortunately the term "instance" has a different connotation in SQL Server; i.e. a named instance.  &lt;/p&gt;&lt;br /&gt;&lt;p&gt;As is typical with command line tools, there are many options available which allow you to fine-tune how you would like to use the tool.  Open a command prompt and enter TYPEPERF -? and you will see the following output: &lt;/p&gt;&lt;div align="center"&gt;&lt;table id="table2" border="1" cellpadding="4" cellspacing="0" width="600"&gt;&lt;br /&gt;&lt;tbody&gt;&lt;br /&gt;&lt;tr&gt;&lt;br /&gt;&lt;td&gt;&lt;pre&gt;Usage:&lt;br /&gt;typeperf { &amp;lt;counter [counter ...]&amp;gt;&lt;br /&gt;                            | -cf &amp;lt;filename&amp;gt;&lt;br /&gt;                            | -q [object]&lt;br /&gt;                            | -qx [object]&lt;br /&gt;                            } [options]&lt;br /&gt;Parameters:&lt;br /&gt;&amp;lt;counter [counter ...]&amp;gt;       Performance counters to monitor.&lt;br /&gt;Options:&lt;br /&gt;-?                            Displays context sensitive help.&lt;br /&gt;-f &amp;lt;CSV|TSV|BIN|SQL&amp;gt;          Output file format. Default is CSV.&lt;br /&gt;-cf &amp;lt;filename&amp;gt;                File containing performance counters to&lt;br /&gt;                            monitor, one per line.&lt;br /&gt;-si &amp;lt;[[hh:]mm:]ss&amp;gt;            Time between samples. Default is 1 second.&lt;br /&gt;-o &amp;lt;filename&amp;gt;                 Path of output file or SQL database. Default&lt;br /&gt;                            is STDOUT.&lt;br /&gt;-q [object]                   List installed counters (no instances). To&lt;br /&gt;                            list counters for one object, include the&lt;br /&gt;                            object name, such as Processor.&lt;br /&gt;-qx [object]                  List installed counters with instances. To&lt;br /&gt;                            list counters for one object, include the&lt;br /&gt;                            object name, such as Processor.&lt;br /&gt;-sc &amp;lt;samples&amp;gt;                 Number of samples to collect. Default is to&lt;br /&gt;                            sample until CTRL+C.&lt;br /&gt;-config &amp;lt;filename&amp;gt;            Settings file containing command options.&lt;br /&gt;-s &amp;lt;computer_name&amp;gt;            Server to monitor if no server is specified&lt;br /&gt;                            in the counter path.&lt;br /&gt;-y                            Answer yes to all questions without prompting.&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;The ultimate goal of using TYPEPERF is to capture performance data in a repeatable way; e.g. specify your options in a batch file that you can execute as required.  The default is to display the performance data in the command window; alternatively you can use the -f option to specify a CSV file (comma separated values), TSV file (tab separated values), etc.&lt;/p&gt;&lt;br /&gt;&lt;p&gt;To get started let's figure out what performance objects are available then setup TYPEPERF to capture some performance data.  There are two options that you can use to get the list of performance objects on a particular machine:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;-q [object] lists the installed counters without the instances&lt;br /&gt;&lt;/li&gt;&lt;li&gt;-qx [object] list the counters including the instances&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;In both cases [object] is an optional parameter which filters the list to just that object.  The default is to query the performance objects on your current machine; you can include -s &amp;lt;computer name&amp;gt; to specify another machine.  To get the list of counters for the SQL Server Buffer Manager object enter the following command: &lt;/p&gt;&lt;p&gt;TYPEPERF -q "SQLServer:Buffer Manager"&lt;/p&gt;&lt;p&gt;You will see output similar to the following:&lt;/p&gt;&lt;div align="center"&gt;&lt;br /&gt;&lt;table id="table2" border="1" cellpadding="4" cellspacing="0" width="600"&gt;&lt;br /&gt;&lt;tbody&gt;&lt;br /&gt;&lt;tr&gt;&lt;br /&gt;&lt;td&gt;&lt;pre&gt;\SQLServer:Buffer Manager\Buffer cache hit ratio&lt;br /&gt;\SQLServer:Buffer Manager\Page lookups/sec&lt;br /&gt;\SQLServer:Buffer Manager\Free list stalls/sec&lt;br /&gt;\SQLServer:Buffer Manager\Free pages&lt;br /&gt;\SQLServer:Buffer Manager\Total pages&lt;br /&gt;\SQLServer:Buffer Manager\Target pages&lt;br /&gt;\SQLServer:Buffer Manager\Database pages&lt;br /&gt;\SQLServer:Buffer Manager\Reserved pages&lt;br /&gt;\SQLServer:Buffer Manager\Stolen pages&lt;br /&gt;\SQLServer:Buffer Manager\Lazy writes/sec&lt;br /&gt;\SQLServer:Buffer Manager\Readahead pages/sec&lt;br /&gt;\SQLServer:Buffer Manager\Page reads/sec&lt;br /&gt;\SQLServer:Buffer Manager\Page writes/sec&lt;br /&gt;\SQLServer:Buffer Manager\Checkpoint pages/sec&lt;br /&gt;\SQLServer:Buffer Manager\AWE lookup maps/sec&lt;br /&gt;\SQLServer:Buffer Manager\AWE stolen maps/sec&lt;br /&gt;\SQLServer:Buffer Manager\AWE write maps/sec&lt;br /&gt;\SQLServer:Buffer Manager\AWE unmap calls/sec&lt;br /&gt;\SQLServer:Buffer Manager\AWE unmap pages/sec&lt;br /&gt;\SQLServer:Buffer Manager\Page life expectancy&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/div&gt;&lt;br /&gt;&lt;p&gt;To get a list of counters with instances enter the following command:&lt;/p&gt;&lt;p&gt;TYPEPERF -qx "SQLServer:Databases" | FIND "tempdb"&lt;/p&gt;&lt;p&gt;You will see output similar to the following:&lt;/p&gt;&lt;br /&gt;&lt;div align="center"&gt;&lt;table id="table2" border="1" cellpadding="4" cellspacing="0" width="600"&gt;&lt;br /&gt;&lt;tbody&gt;&lt;br /&gt;&lt;tr&gt;&lt;br /&gt;&lt;td&gt;&lt;pre&gt;\SQLServer:Databases(tempdb)\Data File(s) Size (KB)&lt;br /&gt;\SQLServer:Databases(tempdb)\Log File(s) Size (KB)&lt;br /&gt;\SQLServer:Databases(tempdb)\Log File(s) Used Size (KB)&lt;br /&gt;\SQLServer:Databases(tempdb)\Percent Log Used&lt;br /&gt;\SQLServer:Databases(tempdb)\Active Transactions&lt;br /&gt;\SQLServer:Databases(tempdb)\Transactions/sec&lt;br /&gt;\SQLServer:Databases(tempdb)\Repl. Pending Xacts&lt;br /&gt;\SQLServer:Databases(tempdb)\Repl. Trans. Rate&lt;br /&gt;\SQLServer:Databases(tempdb)\Log Cache Reads/sec&lt;br /&gt;\SQLServer:Databases(tempdb)\Log Cache Hit Ratio&lt;br /&gt;\SQLServer:Databases(tempdb)\Bulk Copy Rows/sec&lt;br /&gt;\SQLServer:Databases(tempdb)\Bulk Copy Throughput/sec&lt;br /&gt;\SQLServer:Databases(tempdb)\Backup/Restore Throughput/sec&lt;br /&gt;\SQLServer:Databases(tempdb)\DBCC Logical Scan Bytes/sec&lt;br /&gt;\SQLServer:Databases(tempdb)\Shrink Data Movement Bytes/sec&lt;br /&gt;\SQLServer:Databases(tempdb)\Log Flushes/sec&lt;br /&gt;\SQLServer:Databases(tempdb)\Log Bytes Flushed/sec&lt;br /&gt;\SQLServer:Databases(tempdb)\Log Flush Waits/sec&lt;br /&gt;\SQLServer:Databases(tempdb)\Log Flush Wait Time&lt;br /&gt;\SQLServer:Databases(tempdb)\Log Truncations&lt;br /&gt;\SQLServer:Databases(tempdb)\Log Growths&lt;br /&gt;\SQLServer:Databases(tempdb)\Log Shrinks&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/div&gt;&lt;p&gt;Instances in this case (-x option) report the performance counters for the SQLServer:Databases object for each SQL Server database (there is also a _Total instance which combines all databases).  The above output was filtered to include just the tempdb database by piping to the FIND command.    When you are working with a named instance of SQL Server, the performance objects will reflect the SQL Server instance name.  For example I am running an instance of SQL Server 2000 Enterprise Edition which is named SQL2000EE; the performance objects are named MSSQL$SQL2000EE instead of SQLServer as shown above.&lt;/p&gt;&lt;p&gt;Use the -q or -qx options to get the list of performance counters, redirect the list to a text file, then edit the file as necessary to get just the performance counters that you want to capture.  Include the -cf &amp;lt;filename&amp;gt; option on your TYPEPERF command line to get the list of counters to report on from a text file.&lt;/p&gt;&lt;p&gt;Now we are ready to use TYPEPERF to report some performance data.  Here is a sample command:&lt;/p&gt;&lt;p&gt;TYPEPERF -cf MyCounters.txt&lt;/p&gt;&lt;p&gt;The above command will display the counters in the text file MyCounters.txt in the command window every second.  Hit Ctrl-C to cancel.&lt;/p&gt;&lt;p&gt;Here is another example:&lt;/p&gt;&lt;p&gt;TYPEPERF -f CSV -o MyCounters.csv -si 15 -cf MyCounters.txt -sc 60&lt;/p&gt;&lt;p&gt;The above example writes the counter values to MyCounters.csv every 15 seconds.  It stops after writing out the counters 60 times (i.e. 15 minutes).&lt;/p&gt;&lt;p&gt;An example of the output is shown below in Excel 2007:  &lt;/p&gt;&lt;br /&gt;&lt;div align="center"&gt;&lt;img src="http://www.mssqltips.com/tipImages/1575_output.JPG" height="677" width="568" /&gt; &lt;/div&gt;&lt;br /&gt;&lt;p&gt;The first row has the counter names; the columns do not show the full counter names just to conserve space.  The list of counters in MyCounters.txt is:&lt;br /&gt;&lt;/p&gt;&lt;div align="center"&gt;&lt;table id="table2" border="1" cellpadding="4" cellspacing="0" width="600"&gt;&lt;br /&gt;&lt;tbody&gt;&lt;br /&gt;&lt;tr&gt;&lt;br /&gt;&lt;td&gt;&lt;pre&gt;\SQLServer:Databases(_Total)\DBCC Logical Scan Bytes/sec&lt;br /&gt;\SQLServer:Databases(tempdb)\Percent Log Used&lt;br /&gt;\SQLServer:Buffer Manager\Buffer cache hit ratio&lt;br /&gt;\SQLServer:General Statistics\User Connections&lt;br /&gt;\SQLServer:Locks(_Total)\Lock Requests/sec&lt;br /&gt;\SQLServer:SQL Statistics\Batch Requests/sec&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/div&gt;&lt;p&gt; In the above screen shot the custom format used for the Time column is m/d/yyyy h:mm:ss.&lt;/p&gt;&lt;p class="style5"&gt;&lt;u&gt;&lt;b&gt;Next Steps&lt;/b&gt;&lt;/u&gt;&lt;br /&gt;&lt;/p&gt;&lt;ul&gt;&lt;li class="style5"&gt;Take a look at our earlier tip &lt;a href="http://www.mssqltips.com/tip.asp?tip=1515"&gt;Creating SQL Server performance based reports using Excel&lt;/a&gt; for some helpful hints on formatting the performance data in Excel.  I used these to format the data in Excel shown above.&lt;br /&gt;&lt;/li&gt;&lt;li class="style5"&gt;Add TYPEPERF.EXE to your tool box.  It provides a simple, repeatable way to quickly start capturing performance data.&lt;/li&gt;&lt;/ul&gt;          &lt;br /&gt;&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7516218764458010868-1186426228281129261?l=all-about-database.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://all-about-database.blogspot.com/feeds/1186426228281129261/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7516218764458010868&amp;postID=1186426228281129261' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7516218764458010868/posts/default/1186426228281129261'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7516218764458010868/posts/default/1186426228281129261'/><link rel='alternate' type='text/html' href='http://all-about-database.blogspot.com/2008/09/how-to-collect-performance-data-with.html' title='How To Collect Performance Data With TYPEPERF.EXE'/><author><name>firman.arrow</name><uri>http://www.blogger.com/profile/14656110271655711161</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7516218764458010868.post-5488727345815636469</id><published>2008-09-16T08:21:00.000-07:00</published><updated>2008-09-16T08:31:01.967-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Maintenance'/><category scheme='http://www.blogger.com/atom/ns#' term='more'/><title type='text'>Top Tips for Effective Database Maintenance</title><content type='html'>&lt;a href="mailto:paul@sqlskills.com"&gt;Paul S. Randal&lt;/a&gt;&lt;br /&gt;                                              &lt;br /&gt;                                                &lt;div class="FeatureAtAGlance"&gt;At a Glance:&lt;/div&gt;&lt;ul&gt;&lt;li&gt;Managing data and transaction log files&lt;/li&gt;&lt;li class="ListingBullet"&gt;Eliminating index fragmentation&lt;/li&gt;&lt;li class="ListingBullet"&gt;Ensuring accurate, up-to-date statistics&lt;/li&gt;&lt;li class="ListingBullet"&gt;Detecting corrupted database pages&lt;/li&gt;&lt;li class="ListingBullet"&gt;Establishing an effective backup strategy&lt;/li&gt;&lt;/ul&gt;&lt;img id="ctl00_ContentPlaceHolder1_cpe236642_i" src="http://i.technet.microsoft.com/Platform/Controls/CollapsibleArea/resources/plus.gif" style="border-width: 0px; vertical-align: middle;" /&gt; Contents&lt;br /&gt;  &lt;br /&gt;                                                &lt;div class="clsIntro"&gt;Several times a week I'm asked for advice on how to effectively maintain a production database. Sometimes the questions come from DBAs who are implementing new solutions and want help &lt;/div&gt;&lt;br /&gt;                                                &lt;div id="id0230001" class="ArticleNormalPara"&gt;fine-tuning maintenance practices to fit their new databases' characteristics. More frequently, however, the questions come from people who are not professional DBAs but for one reason or another have been given ownership of and responsibility for a database. I like to call this role the "involuntary DBA." The focus of this article is to provide a primer of database maintenance best-practices for all the involuntary DBAs out there.&lt;/div&gt;&lt;br /&gt;                                                          &lt;span class="fullpost"&gt;&lt;br /&gt;&lt;br /&gt;                                                &lt;div id="id0230002" class="ArticleNormalPara"&gt;As with the majority of tasks and procedures in the IT world, there isn't an easy one-size-fits-all solution for effective database maintenance, but there are some key areas that nearly always need to be addressed. My top five areas of concern are (in no particular order of importance):&lt;/div&gt;                                                 &lt;div id="id0230004" class="ArticleNormalPara"&gt;&lt;ul&gt;&lt;br /&gt;                                                                    &lt;li&gt;Data and log file management&lt;/li&gt;                                                                     &lt;li&gt;Index fragmentation&lt;/li&gt;                                                                     &lt;li&gt;Statistics&lt;/li&gt;                                                                     &lt;li&gt;Corruption detection&lt;/li&gt;                                                                     &lt;li&gt;Backups&lt;/li&gt;&lt;/ul&gt;&lt;/div&gt;&lt;br /&gt;                                                &lt;div id="id0230010" class="ArticleNormalPara"&gt;An unmaintained (or poorly maintained) database can develop problems in one or more of these areas, which can eventually lead to poor application performance or even downtime and data loss.&lt;/div&gt;&lt;br /&gt;                                                &lt;div id="id0230011" class="ArticleNormalPara"&gt;In this article, I'll explain why these issues matter and show you some simple ways to mitigate the problems. I will base my explanations on SQL Server­&lt;span class="sup"&gt;®&lt;/span&gt; 2005, but I'll also highlight the major differences that you'll find in SQL Server 2000 and the upcoming SQL Server 2008.&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;                                                &lt;div id="id0230012" class="ArticleTypeTitle"&gt;&lt;span style="font-size:130%;"&gt;Data and Log File Management&lt;/span&gt;&lt;/div&gt;&lt;br /&gt;                                                &lt;div id="id0230013" class="ArticleNormalPara"&gt;The first area I always recommend checking when taking over a database concerns the settings related to data and (transaction) log file management. Specifically, you should make sure that:&lt;/div&gt;&lt;br /&gt;                                                &lt;/span&gt;&lt;span class="fullpost"&gt;&lt;div id="id0230015" class="ArticleNormalPara"&gt;&lt;ul&gt;&lt;li&gt;The data and log files are separated from each other and isolated from everything else as well&lt;/li&gt;                                                                     &lt;li&gt;Auto-growth is configured correctly&lt;/li&gt;                                                                     &lt;li&gt;Instant file initialization is configured&lt;/li&gt;                                                                     &lt;li&gt;Auto-shrink is not enabled and shrink is not part of any maintenance plan&lt;/li&gt;&lt;/ul&gt;&lt;/div&gt;&lt;br /&gt;                                                &lt;div id="id0230020" class="ArticleNormalPara"&gt;When data and log files (which ideally should be on separate volumes altogether) share a volume with any other application that creates or expands files, there is the potential for file fragmentation. In data files, excessive file fragmentation can be a small contributing factor in poorly performing queries (specifically those that scan very large amounts of data). In log files, it can have a much more significant impact on performance, especially if auto-growth is set to increase each file size only by a very small amount each time it is needed.&lt;/div&gt;&lt;br /&gt;                                                &lt;div id="id0230021" class="ArticleNormalPara"&gt;Log files are internally divided into sections called Virtual Log Files (VLFs) and the more fragmentation there is in the log file (I use the singular here because there is no gain from having multiple log files—there should only be one per database), the more VLFs there are. Once a log file has more than, say, 200 VLFs, performance can be negatively impacted for log-related operations such as log reads (for transactional replication/rollback, for example), log backups, and even triggers in SQL Server 2000 (the implementation of triggers changed in SQL Server 2005 to the row versioning framework instead of the transaction log).&lt;/div&gt;&lt;br /&gt;                                                &lt;div id="id0230022" class="ArticleNormalPara"&gt;The best practice regarding the sizing of data and log files is to create them with an appropriate initial size. For data files, the initial size should take into account the potential for additional data being added to the database in the short-term. For instance, if the initial size of the data is 50GB, but you know that over the next six months an additional 50GB of data will be added, it makes sense to create the data file to be 100GB right away, rather than having to grow it several times to reach that size. &lt;/div&gt;&lt;br /&gt;                                                &lt;div id="id0230023" class="ArticleNormalPara"&gt;It's a little more complicated for log files, unfortunately, and you need to consider factors like transaction size (long-running transactions cannot be cleared from the log until they complete) and log backup frequency (since this is what removes the inactive portion of the log). For more information, see "&lt;a href="http://sqlskills.com/blogs/kimberly/2005/06/25/8StepsToBetterTransactionLogThroughput.aspx"&gt;8 Steps to Better Transaction Log Throughput&lt;/a&gt;", a popular blog post on SQLskills.com written by my wife, Kimberly Tripp. &lt;/div&gt;&lt;br /&gt;&lt;br /&gt;                                                &lt;div id="id0230024" class="ArticleNormalPara"&gt;Once set up, the file sizes should be monitored at various times and proactively grown manually at an appropriate time of day. Auto-grow should be left on as a just-in-case protection so the files can still grow if they need to if some abnormal event occurs. The logic against leaving file management entirely to auto-grow is that auto-grow of small amounts leads to file fragmentation, and that auto-grow can be a time-consuming process that stalls the application workload at unpredictable times. &lt;/div&gt;&lt;br /&gt;                                                &lt;div id="id0230025" class="ArticleNormalPara"&gt;The auto-grow size should be set to a specific value, rather than a percentage, to bound the time and space needed to perform the auto-grow, if it occurs. For instance, you may want to set a 100GB data file to have a fixed 5GB auto-grow size, rather than, say 10 percent. This means it will always grow by 5GB, no matter how large the file ends up being, rather than an ever-increasing amount (10GB, 11GB, 12GB, and so on) each time the file gets bigger.&lt;/div&gt;&lt;br /&gt;                                                &lt;div id="id0230026" class="ArticleNormalPara"&gt;When a transaction log is grown (either manually or through auto-grow), it is always zero-initialized. Data files have the same default behavior in SQL Server 2000, but starting with SQL Server 2005, you can enable instant file initialization, which skips zero-initializing the files and hence makes growth and auto-growth virtually instantaneous. Contrary to popular belief, this feature is available in all editions of SQL Server. For more information, enter "instant file initialization" in the index of Books Online for SQL Server 2005 or SQL Server 2008. &lt;/div&gt;&lt;br /&gt;                                                &lt;div id="id0230027" class="ArticleNormalPara"&gt;Finally, care should be taken that shrink is not enabled in any way. Shrink can be used to reduce the size of a data or log file, but it is a very intrusive, resource-heavy process that causes massive amounts of logical scan fragmentation in data files (see below for details) and leads to poor performance. I changed the SQL Server 2005 Books Online entry for shrink to include a warning to this effect. Manual shrinking of individual data and log files, however, can be acceptable under special circumstances.&lt;/div&gt;&lt;br /&gt;&lt;div id="id0230028" class="ArticleNormalPara"&gt;Auto-shrink is the worst offender as it starts every 30 minutes in the background and tries to shrink databases where the auto-shrink database option is set to true. It is a somewhat unpredictable process in that it only shrinks databases with more than 25 percent free space. Auto-shrink uses lots of resources and causes performance-dropping fragmentation and so is not a good plan under any circumstances. You should always switch off auto-shrink with:&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;                                                &lt;div class="" id="ctl00_ContentPlaceHolder1_ctl04_"&gt;&lt;table&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td style="border: 0px none ; margin: 0px; padding: 0px;"&gt;&lt;div class="CodeSnippetTitleBar"&gt;&lt;div class="CopyCodeButton"&gt;&lt;a class="copyCode" href="javascript:CopyCode('ctl00_ContentPlaceHolder1_ctl04');"&gt;&lt;img src="http://i.technet.microsoft.com/Platform/Controls/CodeSnippet/resources/copy_off.gif" align="middle" border="0" height="9" /&gt; Copy Code&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td style="border: 0px none ; margin: 0px; padding: 0px;"&gt;&lt;pre class="libCScode" id="ctl00_ContentPlaceHolder1_ctl04" space="preserve"&gt;ALTER DATABASE MyDatabase SET AUTO_SHRINK OFF;&lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/div&gt;&lt;br /&gt;                                                &lt;div id="id0230030" class="ArticleNormalPara"&gt;A regular maintenance plan that includes a manual database shrink command is almost as bad. If you find that your database continually grows after the maintenance plan shrinks it, that's because the database needs that space in which to run. &lt;/div&gt;&lt;br /&gt;                                                &lt;div id="id0230031" class="ArticleNormalPara"&gt;The best thing to do is allow the database to grow to a steady-state size and avoid running shrink altogether. You can find more information on the downsides of using shrink, plus some commentary on the new algorithms in SQL Server 2005 on my old MSDN&lt;span class="sup"&gt;®&lt;/span&gt; blog at &lt;a href="http://blogs.msdn.com/sqlserverstorageengine/archive/tags/Shrink/default.aspx"&gt;blogs.msdn.com/sqlserverstorageengine/archive/tags/Shrink/default.aspx&lt;/a&gt;.&lt;/div&gt;&lt;br /&gt;                                              &lt;br /&gt;                                                &lt;div id="id0230032" class="ArticleTypeTitle"&gt;&lt;span style="font-size:130%;"&gt;Index Fragmentation&lt;/span&gt;&lt;/div&gt;&lt;br /&gt;                                                &lt;div id="id0230033" class="ArticleNormalPara"&gt;Apart from fragmentation at the file-system level and within the log file, it's also possible to have fragmentation within the data files, in the structures that store the table and index data. There are two basic types of fragmentation that can occur within a data file:&lt;/div&gt;&lt;br /&gt;                                                &lt;/span&gt;&lt;span class="fullpost"&gt;&lt;div id="id0230034" class="ArticleNormalPara"&gt;&lt;ul&gt;&lt;li&gt;Fragmentation within individual data and index pages (sometimes called internal fragmentation)&lt;/li&gt;                                                                     &lt;li&gt;Fragmentation within index or table structures consisting of pages (called logical scan fragmentation and extent scan fragmentation)&lt;br /&gt;                                                          &lt;/li&gt;&lt;/ul&gt;&lt;/div&gt;&lt;br /&gt;                                                &lt;div id="id0230037" class="ArticleNormalPara"&gt;Internal fragmentation is where there is a lot of empty space in a page. As &lt;strong&gt;Figure 1&lt;/strong&gt; shows, each page in a database is 8KB in size and has a 96-byte page header; as a result, a page can store roughly 8096 bytes of table or index data (specific table and index internals for data and row structures can be found on my blog at &lt;a href="http://sqlskills.com/blogs/paul"&gt;sqlskills.com/blogs/paul&lt;/a&gt; in the Inside The Storage Engine category). Empty space can occur if each table or index record is more than half the size of a page, as then only a single record can be stored per-page. This can be very hard or impossible to correct, as it would require a table or index schema change, for instance by changing an index key to be something that doesn't cause random insertion points like a GUID does.&lt;/div&gt;&lt;br /&gt;                                                &lt;div class="ArticleImageSpacer"&gt;&lt;br /&gt;                                                          &lt;img style="cursor: pointer;" alt="" src="http://i.technet.microsoft.com/cc671165.fig01.gif" onmouseover="this.style.cursor='pointer';" onclick="var large='http://i.technet.microsoft.com/cc671165.fig01_L.gif'; var small='http://i.technet.microsoft.com/cc671165.fig01.gif'; var current= this.src; if (current == small) this.src = large; else this.src = small;" /&gt;&lt;br /&gt;                                                          &lt;div class="ArticleImageCaptionText"&gt;Figure 1 &lt;strong&gt;The structure of a database page &lt;/strong&gt;(Click the image for a larger view)&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;                                                &lt;/div&gt;&lt;br /&gt;                                                &lt;div id="id0230038" class="ArticleNormalPara"&gt;More commonly, internal fragmentation results from data modifications, such as inserts, updates, and deletes, which can leave empty space on a page. Mismanaged fill-factor can also contribute to fragmentation; see Books Online for more details. Depending on the table/index schema and the application's characteristics, this empty space may never be reused once it is created and can lead to ever-increasing amounts of unusable space in the database.&lt;/div&gt;&lt;br /&gt;                                                &lt;div id="id0230039" class="ArticleNormalPara"&gt;Consider, for instance, a 100-million-row table with an average record size of 400 bytes. Over time, the application's data modification pattern leaves each page with an average of 2800 bytes of free space. The total space required by the table is about 59GB, calculated as 8096-2800 / 400 = 13 records per 8KB page, then dividing 100 million by 13 to get the number of pages. If the space wasn't being wasted, then 20 records would fit per page, bringing the total space required down to 38GB. That's a huge savings!&lt;/div&gt;&lt;br /&gt;                                                &lt;div id="id0230040" class="ArticleNormalPara"&gt;Wasted space on data/index pages can therefore lead to needing more pages to hold the same amount of data. Not only does this take up more disk space, it also means that a query needs to issue more I/Os to read the same amount of data. And all these extra pages occupy more space in the data cache, thus taking up more server memory.&lt;/div&gt;&lt;br /&gt;                                                &lt;div id="id0230041" class="ArticleNormalPara"&gt;Logical scan fragmentation is caused by an operation called a page split. This occurs when a record has to be inserted on a specific index page (according to the index key definition) but there is not enough space on the page to fit the data being inserted. The page is split in half and roughly 50 percent of the records moved to a newly allocated page. This new page is usually not physically contiguous with the old page and therefore is called fragmented. Extent scan fragmentation is similar in concept. Fragmentation within the table/index structures affects the ability of SQL Server to do efficient scans, whether over an entire table/index or bounded by a query WHERE clause (such as SELECT * FROM MyTable WHERE Column1 &amp;gt; 100 AND Column1 &amp;lt; 4000).&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;                                                &lt;div id="id0230042" class="ArticleNormalPara"&gt;&lt;br /&gt;                                                          &lt;strong&gt;Figure 2&lt;/strong&gt; shows newly created index pages with 100 percent fill-factor and no fragmentation—the pages are full and the physical order of the pages matches the logical order. &lt;strong&gt;Figure 3&lt;/strong&gt; shows the fragmentation that can occur after random inserts/updates/deletes.&lt;/div&gt;&lt;br /&gt;                                                &lt;div class="ArticleImageSpacer"&gt;&lt;br /&gt;                                                          &lt;img style="cursor: pointer;" alt="" src="http://i.technet.microsoft.com/cc671165.fig02.gif" onmouseover="this.style.cursor='pointer';" onclick="var large='http://i.technet.microsoft.com/cc671165.fig02_L.gif'; var small='http://i.technet.microsoft.com/cc671165.fig02.gif'; var current= this.src; if (current == small) this.src = large; else this.src = small;" /&gt;&lt;br /&gt;                                                          &lt;div class="ArticleImageCaptionText"&gt;Figure 2 &lt;strong&gt;Newly created index pages with no fragmentation; pages 100% full &lt;/strong&gt;(Click the image for a larger view)&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;                                                &lt;/div&gt;&lt;br /&gt;                                                &lt;div class="ArticleImageSpacer"&gt;&lt;br /&gt;                                                          &lt;img style="cursor: pointer;" alt="" src="http://i.technet.microsoft.com/cc671165.fig03.gif" onmouseover="this.style.cursor='pointer';" onclick="var large='http://i.technet.microsoft.com/cc671165.fig03_L.gif'; var small='http://i.technet.microsoft.com/cc671165.fig03.gif'; var current= this.src; if (current == small) this.src = large; else this.src = small;" /&gt;&lt;br /&gt;                                                          &lt;div class="ArticleImageCaptionText"&gt;Figure 3 &lt;strong&gt;Index pages showing internal and logical scan fragmentation after random inserts, updates, and deletes &lt;/strong&gt;(Click the image for a larger view)&lt;/div&gt;&lt;br /&gt;                                                &lt;/div&gt;&lt;br /&gt;                                                &lt;div id="id0230043" class="ArticleNormalPara"&gt;Fragmentation can sometimes be prevented by changing the table/index schema, but as I mentioned above, this may be very difficult or impossible. If prevention is not an option, there are ways to remove fragmentation once it has occurred—in particular, by rebuilding or reorganizing an index. &lt;/div&gt;&lt;br /&gt;                                                &lt;div id="id0230044" class="ArticleNormalPara"&gt;Rebuilding an index involves creating a new copy of the index—nicely compacted and as contiguous as possible—and then dropping the old, fragmented one. As SQL Server creates a new copy of the index before removing the old one, it requires free space in the data files approximately equivalent to the size of the index. In SQL Server 2000, rebuilding an index was always an offline operation. In SQL Server 2005 Enterprise Edition, however, index rebuilding can take place online, with a few restrictions. Reorganizing, on the other hand, uses an in-place algorithm to compact and defragment the index; it requires only 8KB of additional space to run—and it always runs online. In fact, in SQL Server 2000, I specifically wrote the index reorganize code as an online, space-efficient alternative to rebuilding an index.&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;                                                &lt;div id="id0230045" class="ArticleNormalPara"&gt;In SQL Server 2005, the commands to investigate are ALTER INDEX … REBUILD to rebuild indexes, and ALTER INDEX … REORGANIZE to reorganize them. This syntax replaces the SQL Server 2000 commands DBCC DBREINDEX and DBCC INDEXDEFRAG, respectively.&lt;/div&gt;&lt;br /&gt;                                                &lt;div id="id0230046" class="ArticleNormalPara"&gt;There are many trade-offs between these methods, such as the amount of transaction logging generated, the amount of free space in the database required, and whether the process is interruptible without loss of work. You'll find a white paper that discusses these trade-offs, and more, at &lt;a href="http://microsoft.com/technet/prodtechnol/sql/2000/maintain/ss2kidbp.mspx"&gt;microsoft.com/technet/prodtechnol/sql/2000/maintain/ss2kidbp.mspx&lt;/a&gt;. The paper is based on SQL Server 2000 but the concepts translate well to later versions.&lt;/div&gt;&lt;br /&gt;                                                &lt;div id="id0230047" class="ArticleNormalPara"&gt;Some people simply choose to rebuild or reorganize all indexes every night or every week (using a maintenance plan option, for instance) rather than figuring out which indexes are fragmented and whether any benefit will come from removing the fragmentation. While that can be a good solution for an involuntary DBA who just wishes to put something in place with minimal effort, note that it can be a very poor choice for larger databases or systems where resources are at a premium.&lt;/div&gt;&lt;br /&gt;                                                &lt;div id="id0230048" class="ArticleNormalPara"&gt;A more sophisticated approach involves using the DMV sys.dm_db_index_physical_stats (or DBCC SHOWCONTIG in SQL Server 2000) to periodically determine which indexes are fragmented, and then choosing whether and how to operate on those. The whitepaper also discusses using these more targeted choices. Additionally, you can see some example code to do this filtering in Example D of the Books Online entry for the DMV sys.dm_db_index_physical_stats in SQL Server 2005 (&lt;a href="http://msdn.microsoft.com/library/ms188917"&gt;msdn.microsoft.com/­library/ms188917&lt;/a&gt;) or Example E in the Books Online entry for DBCC SHOWCONTIG in SQL Server 2000 and later (at &lt;a href="http://msdn.microsoft.com/library/aa258803"&gt;msdn.microsoft.com/library/aa258803&lt;/a&gt;).&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;&lt;div id="id0230049" class="ArticleNormalPara"&gt;Whichever method you use, it is highly advisable to investigate and fix fragmentation on a regular basis.&lt;/div&gt;&lt;br /&gt;                                               &lt;span style="font-size:130%;"&gt;&lt;br /&gt;&lt;/span&gt;                                                 &lt;div id="id0230050" class="ArticleTypeTitle"&gt;&lt;span style="font-size:130%;"&gt;Statistics&lt;/span&gt;&lt;/div&gt;&lt;br /&gt;                                                &lt;div id="id0230051" class="ArticleNormalPara"&gt;The Query Processor is the part of SQL Server that decides how a query should be executed—specifically, which tables and indexes to use, and which operations to perform on them to obtain the results; this is called a query plan. Some of the most important inputs into this decision-making process are statistics that describe the distribution of data values for columns within a table or index. Obviously, statistics need to be accurate and up-to-date to be useful to the Query Processor, otherwise poorly performing query plans may be chosen.&lt;/div&gt;&lt;br /&gt;                                                &lt;div id="id0230052" class="ArticleNormalPara"&gt;Statistics are generated by reading the table/index data and determining the data distribution for the relevant columns. Statistics can be built by scanning all the data values for a particular column (a full scan) but they can also be based on a user-specified percentage of the data (a sampled scan). If the distribution of values in a column is pretty even, then a sampled scan should be good enough, and this makes creating and updating the statistics faster than with a full scan.&lt;/div&gt;&lt;br /&gt;                                                &lt;div id="id0230053" class="ArticleNormalPara"&gt;Note that statistics can be automatically created and maintained by turning on the AUTO_CREATE_STATISTICS and AUTO_UPDATE_STATISTICS database options, as shown in &lt;strong&gt;Figure 4&lt;/strong&gt;. These are on by default but if you've just inherited a database, you may want to check to make sure. Sometimes statistics can become out-of-date, in which case manually updating them is possible using the UPDATE STATISTICS operation on specific sets of statistics. Alternatively, the sp_updatestats stored procedure can be used, which updates all statistics that are out of date (in SQL Server 2000, sp_updatestats updates all statistics, regardless of age).&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;                                                &lt;div class="ArticleImageSpacer"&gt;&lt;br /&gt;                                                          &lt;img style="cursor: pointer;" alt="" src="http://i.technet.microsoft.com/cc671165.fig04.gif" onmouseover="this.style.cursor='pointer';" onclick="var large='http://i.technet.microsoft.com/cc671165.fig04_L.gif'; var small='http://i.technet.microsoft.com/cc671165.fig04.gif'; var current= this.src; if (current == small) this.src = large; else this.src = small;" /&gt;&lt;br /&gt;                                                          &lt;div class="ArticleImageCaptionText"&gt;Figure 4 &lt;strong&gt;Changing database settings through SQL Server Management Studio &lt;/strong&gt;(Click the image for a larger view)&lt;/div&gt;&lt;br /&gt;                                                &lt;/div&gt;&lt;br /&gt;                                                &lt;div id="id0230054" class="ArticleNormalPara"&gt;If you want to update statistics as part of your regular maintenance plan, there is a catch you should be aware of. Both UPDATE STATISTICS and sp_updatestats default to using the previously specified level of sampling (if any)—and this may be less than a full scan. Index rebuilds automatically update statistics with a full scan. If you manually update statistics after an index rebuild, it's possible to end up with less accurate statistics! This can happen if a sampled scan from the manual update overwrites the full scan generated by the index rebuild. On the other hand, reorganizing an index does not update statistics at all.&lt;/div&gt;&lt;br /&gt;                                                &lt;div id="id0230055" class="ArticleNormalPara"&gt;Again, many people have a maintenance plan that updates all statistics at some point before or after rebuilding all indexes—and so unknowingly they end up with potentially less accurate statistics. If you do choose to simply rebuild all indexes every so often, that will take care of statistics too. If you choose to go a more complex route with fragmentation removal, you should also do that for statistics maintenance. Here's what I suggest:&lt;/div&gt;&lt;br /&gt;&lt;/span&gt;&lt;span class="fullpost"&gt;&lt;div id="id0230057" class="ArticleNormalPara"&gt;&lt;ul&gt;&lt;li&gt;Analyze indexes and determine which indexes to operate on and how to do the fragmentation removal.&lt;/li&gt;                                                                     &lt;li&gt;For all indexes that were not rebuilt, update the statistics.&lt;/li&gt;                                                                     &lt;li&gt;Update statistics for all of the non-indexed columns.&lt;/li&gt;&lt;/ul&gt;&lt;/div&gt;&lt;br /&gt;                                                &lt;div id="id0230061" class="ArticleNormalPara"&gt;For more information on statistics, see the white paper "Statistics Used by the Query Optimizer in Microsoft&lt;span class="sup"&gt;®&lt;/span&gt; SQL Server 2005" (&lt;a href="http://microsoft.com/technet/prodtechnol/sql/2005/qrystats.mspx"&gt;microsoft.com/technet/prodtechnol/sql/2005/qrystats.mspx&lt;/a&gt;).&lt;/div&gt;&lt;br /&gt;                                              &lt;br /&gt;&lt;div id="id0230062" class="ArticleTypeTitle"&gt;&lt;span style="font-size:130%;"&gt;Corruption Detection&lt;/span&gt;&lt;/div&gt;&lt;br /&gt;                                                &lt;div id="id0230063" class="ArticleNormalPara"&gt;I have discussed performance-related maintenance. Now I want to switch gears and discuss corruption detection and mitigation.&lt;/div&gt;&lt;br /&gt;                                                &lt;div id="id0230064" class="ArticleNormalPara"&gt;It's very unlikely that the database you're managing contains totally useless information that no one cares about—so how do you go about making sure that the data remains uncorrupted and recoverable in the event of a disaster? The ins-and-outs of putting together a full disaster-recovery and high-availability strategy are beyond the scope of this article, but there are a few simple things you can do to get started.&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;                                                &lt;div id="id0230065" class="ArticleNormalPara"&gt;The overwhelming majority of corruptions are caused by "hardware." Why do I put it in quotes? Well, hardware here is really shorthand for "something in the I/O subsystem underneath SQL Server." The I/O subsystem consists of elements such as the operating system, file-system drivers, device-drivers, RAID controllers, cables, networks, and the actual disk drives themselves. That's a lot of places where problems can (and do) occur.&lt;/div&gt;&lt;br /&gt;                                                &lt;div id="id0230066" class="ArticleNormalPara"&gt;One of the most common problems is when a power failure occurs and a disk drive is in the middle of writing out a database page. If the drive cannot complete the write before it runs out of power (or write operations are cached and there isn't enough battery backup to flush the drive's cache) the result could be an incomplete page image on the disk. This can happen because an 8KB database page is actually comprised of 16 contiguous 512-byte disk sectors. An incomplete write could have written some of the sectors from the new page but leave some of the sectors from the previous page image. This situation is called a torn page. How can you detect when this happens?&lt;/div&gt;&lt;br /&gt;                                                &lt;div id="id0230067" class="ArticleNormalPara"&gt;SQL Server has a mechanism to detect this situation. It involves storing a couple of bits from every sector of the page and writing a specific pattern in their place (this happens just before the page is written to disk). If the pattern is not the same when the page is read back in, SQL Server knows the page was "torn" and raises an error. &lt;/div&gt;&lt;br /&gt;                                                &lt;div id="id0230068" class="ArticleNormalPara"&gt;In SQL Server 2005 and later, a more comprehensive mechanism called page checksums is available that can detect any corruption on a page. This involves writing a whole-page checksum on the page just before it is written out and then testing it when the page is read back in, just as for torn-page detection. After enabling page checksums, a page needs to be read into the buffer pool, changed in some way, and then written out to disk again before it is protected by a page checksum.&lt;/div&gt;&lt;br /&gt;                                                &lt;div id="id0230069" class="ArticleNormalPara"&gt;So, it is a best practice to have page checksums enabled for SQL Server 2005 onwards, with torn-page detection enabled for SQL Server 2000. To enable page checksums, use:﻿&lt;/div&gt;&lt;br /&gt;                                                &lt;div class="" id="ctl00_ContentPlaceHolder1_ctl09_"&gt;&lt;table&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td style="border: 0px none ; margin: 0px; padding: 0px;"&gt;&lt;div class="CodeSnippetTitleBar"&gt;&lt;div class="CopyCodeButton"&gt;&lt;a class="copyCode" href="javascript:CopyCode('ctl00_ContentPlaceHolder1_ctl09');"&gt;&lt;img src="http://i.technet.microsoft.com/Platform/Controls/CodeSnippet/resources/copy_off.gif" align="middle" border="0" height="9" /&gt; Copy Code&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td style="border: 0px none ; margin: 0px; padding: 0px;"&gt;&lt;pre class="libCScode" id="ctl00_ContentPlaceHolder1_ctl09" space="preserve"&gt;ALTER DATABASE MyDatabase SET PAGE_VERIFY CHECKSUM;&lt;br /&gt;&lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/div&gt;&lt;br /&gt;                                                &lt;div id="id0230071" class="ArticleNormalPara"&gt;To enable torn-page detection for SQL Server 2000, use this:&lt;/div&gt;&lt;br /&gt;                                                &lt;div class="" id="ctl00_ContentPlaceHolder1_ctl10_"&gt;&lt;table&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td style="border: 0px none ; margin: 0px; padding: 0px;"&gt;&lt;div class="CodeSnippetTitleBar"&gt;&lt;div class="CopyCodeButton"&gt;&lt;a class="copyCode" href="javascript:CopyCode('ctl00_ContentPlaceHolder1_ctl10');"&gt;&lt;img src="http://i.technet.microsoft.com/Platform/Controls/CodeSnippet/resources/copy_off.gif" align="middle" border="0" height="9" /&gt; Copy Code&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td style="border: 0px none ; margin: 0px; padding: 0px;"&gt;&lt;pre class="libCScode" id="ctl00_ContentPlaceHolder1_ctl10" space="preserve"&gt;ALTER DATABASE MyDatabase SET TORN_PAGE_DETECTION ON;&lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/div&gt;&lt;br /&gt;                                                &lt;div id="id0230073" class="ArticleNormalPara"&gt;These mechanisms allow you to detect when a page has a corruption on it, but only when the page is read. How can you easily force all the allocated pages to be read? The best method for doing this (and finding any other kinds of corruption) is to use the DBCC CHECKDB command. Regardless of the options specified, this command will always read all pages in the database, thus causing any page checksums or torn-page detection to be verified. You should also set up alerts so you know when users encounter corruption problems when running queries. You can be notified of all the problems described above using an alert for Severity 24 errors (&lt;strong&gt;Figure 5&lt;/strong&gt;).&lt;/div&gt;&lt;br /&gt;                                                &lt;div class="ArticleImageSpacer"&gt;&lt;br /&gt;                                                          &lt;img style="cursor: pointer;" alt="" src="http://i.technet.microsoft.com/cc671165.fig05.gif" onmouseover="this.style.cursor='pointer';" onclick="var large='http://i.technet.microsoft.com/cc671165.fig05_L.gif'; var small='http://i.technet.microsoft.com/cc671165.fig05.gif'; var current= this.src; if (current == small) this.src = large; else this.src = small;" /&gt;&lt;br /&gt;&lt;br /&gt;                                                          &lt;div class="ArticleImageCaptionText"&gt;Figure 5 &lt;strong&gt;Setting up an alert for all Severity 24 errors &lt;/strong&gt;(Click the image for a larger view)&lt;/div&gt;&lt;br /&gt;                                                &lt;/div&gt;&lt;br /&gt;                                                &lt;div id="id0230074" class="ArticleNormalPara"&gt;So another best practice is to regularly run DBCC CHECKDB on databases to verify their integrity. There are many variations on this command and questions around how often to run it. Unfortunately, there is no white paper available that discusses this. However, as DBCC CHECKDB was the main piece of code I wrote for SQL Server 2005, I have blogged extensively about it. See the "CHECKDB From Every Angle" category of my blog (&lt;a href="http://sqlskills.com/blogs/paul"&gt;sqlskills.com/blogs/paul&lt;/a&gt;) for many in-depth articles on consistency checking, best practices, and how-to advice. For involuntary DBAs, the rule of thumb is to run a DBCC CHECKDB as often as you take a full database backup (more on this below). I recommend running the following command:&lt;/div&gt;&lt;br /&gt;                                                &lt;div class="" id="ctl00_ContentPlaceHolder1_ctl12_"&gt;&lt;table&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td style="border: 0px none ; margin: 0px; padding: 0px;"&gt;&lt;div class="CodeSnippetTitleBar"&gt;&lt;div class="CopyCodeButton"&gt;&lt;a class="copyCode" href="javascript:CopyCode('ctl00_ContentPlaceHolder1_ctl12');"&gt;&lt;img src="http://i.technet.microsoft.com/Platform/Controls/CodeSnippet/resources/copy_off.gif" align="middle" border="0" height="9" /&gt; Copy Code&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td style="border: 0px none ; margin: 0px; padding: 0px;"&gt;&lt;pre class="libCScode" id="ctl00_ContentPlaceHolder1_ctl12" space="preserve"&gt;DBCC CHECKDB ('MyDatabase') WITH NO_INFOMSGS,&lt;br /&gt;ALL_ERRORMSGS;&lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;                                                &lt;div id="id0230077" class="ArticleNormalPara"&gt;If there is any output from this command, DBCC has found some corruptions in the database. The question then becomes what to do if DBCC CHECKDB finds any corruptions. That's where backups come in.&lt;/div&gt;&lt;br /&gt;                                              &lt;br /&gt;&lt;br /&gt;                                                &lt;div id="id0230078" class="ArticleTypeTitle"&gt;Backups&lt;/div&gt;&lt;br /&gt;                                                &lt;div id="id0230079" class="ArticleNormalPara"&gt;When a corruption or other disaster occurs, the most effective way to recover is to restore the database from backups. Now, this assumes that you have backups in the first place, and that they're not corrupt themselves. All too often, people want to know how to get a badly corrupted database running again when they don't have a backup. The simple answer is that you can't, not without experiencing some form of data loss that could play havoc with your business logic and relational data integrity.&lt;/div&gt;&lt;br /&gt;                                                &lt;div id="id0230080" class="ArticleNormalPara"&gt;So there is a very strong case for taking regular backups. The intricacies of using backup and restore are well beyond the scope of this article, but let me give you a quick primer on how to establish a backup strategy.&lt;/div&gt;&lt;br /&gt;                                                &lt;div id="id0230081" class="ArticleNormalPara"&gt;First, you should take regular full-database backups. This gives you a single point-in-time to which you can later restore. You can take a full-database backup using the BACKUP DATABASE command. Look in Books Online for examples. For added protection, you can use the WITH CHECKSUM option, which verifies the page checksums (if present) of pages being read and calculates a checksum over the entire backup. You should choose a frequency that reflects how much data or work your business is comfortable losing. For example, taking a full database backup once per day means you may lose up to a day's worth of data in the event of a disaster. If you are only using full database backups, you should be in the SIMPLE recovery model (commonly called recovery mode) to avoid complexities relating to transaction log growth management.&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;                                                &lt;div id="id0230082" class="ArticleNormalPara"&gt;Second, always keep the backups around for a few days in case one becomes corrupt—a backup from a few days ago is better than no backup at all. You should also verify the integrity of your backups using the RESTORE WITH VERIFYONLY command (again, see Books Online). If you used the WITH CHECKSUM option when the backup was created, running the verification command will check that the backup checksum is still valid, as well as re-check all the page checksums of pages within the backup.&lt;/div&gt;&lt;br /&gt;                                                &lt;div id="id0230083" class="ArticleNormalPara"&gt;Third, if a daily full database backup does not allow you to meet the maximum data/work loss your business can sustain, you may want to investigate differential database backups. A differential database backup is based on a full database backup and contains a record of all the changes since the last full database backup (a common misconception is that differential backups are incremental—they are not). A sample strategy might be to take a daily full database backup, with a differential database backup every four hours. A differential backup provides a single extra point-in-time recovery option. If you are only using full database and differential database backups, you should still be using the SIMPLE recovery model.&lt;/div&gt;&lt;br /&gt;                                                &lt;div id="id0230084" class="ArticleNormalPara"&gt;Finally, the ultimate in recoverability comes with using log backups. These are only available in the FULL (or BULK_LOGGED) recovery models and provide a backup of all the log records generated since the previous log backup. Maintaining a set of log backups with periodic full database (and maybe differential database) backups gives an unlimited number of points-in-time to recover to—including up-to-the-minute recovery. The trade-off is that the transaction log will continue to grow unless it is "freed" by taking a log backup. A sample strategy here would be a full database backup every day, a differential database backup every four hours, and a log backup every half hour.&lt;/div&gt;&lt;br /&gt;                                                &lt;div id="id0230085" class="ArticleNormalPara"&gt;Deciding on a backup strategy and setting it up can be complicated. At the very least, you should have a regular full-database backup to ensure you have at least one point-in-time to recover from.&lt;/div&gt;&lt;br /&gt;&lt;div id="id0230086" class="ArticleTypeTitle"&gt;&lt;span style="font-size:130%;"&gt;Wrap-Up&lt;/span&gt;&lt;/div&gt;&lt;br /&gt;                                                &lt;div id="id0230087" class="ArticleNormalPara"&gt;As you can see, to ensure your database stays healthy and available there are a few "'must do" tasks. Here's my final checklist for an involuntary DBA taking over a database:&lt;/div&gt;&lt;br /&gt;&lt;/span&gt;&lt;span class="fullpost"&gt;&lt;div id="id0230089" class="ArticleNormalPara"&gt;&lt;ul&gt;&lt;li&gt;Remove excessive transaction log file fragmentation.&lt;/li&gt;                                                                     &lt;li&gt;Set auto-growth correctly.&lt;/li&gt;                                                                     &lt;li&gt;Turn off any scheduled shrink operations.&lt;/li&gt;                                                                     &lt;li&gt;Turn on instant file initialization.&lt;/li&gt;                                                                     &lt;li&gt;Put a regular process in place to detect and remove index fragmentation.&lt;/li&gt;                                                                     &lt;li&gt;Turn on AUTO_CREATE_STATISTICS and AUTO_UPDATE_STATISTICS, plus have a regular process in place to update statistics.&lt;/li&gt;                                                                     &lt;li&gt;Turn on page checksums (or least torn-page detection on SQL Server 2000).&lt;/li&gt;                                                                     &lt;li&gt;Have a regular process to run DBCC CHECKDB.&lt;/li&gt;                                                                     &lt;li&gt;Have a regular process in place to take full database backups, plus differential and log backups for point-in-time recovery.&lt;/li&gt;&lt;/ul&gt;&lt;/div&gt;&lt;br /&gt;                                                &lt;div id="id0230099" class="ArticleNormalPara"&gt;I've given T-SQL commands within the article, but you can do a lot from Management Studio too. Hopefully, I have given you some useful pointers for effective database maintenance.&lt;br /&gt;&lt;/div&gt;&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7516218764458010868-5488727345815636469?l=all-about-database.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://all-about-database.blogspot.com/feeds/5488727345815636469/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7516218764458010868&amp;postID=5488727345815636469' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7516218764458010868/posts/default/5488727345815636469'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7516218764458010868/posts/default/5488727345815636469'/><link rel='alternate' type='text/html' href='http://all-about-database.blogspot.com/2008/09/top-tips-for-effective-database.html' title='Top Tips for Effective Database Maintenance'/><author><name>firman.arrow</name><uri>http://www.blogger.com/profile/14656110271655711161</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7516218764458010868.post-8825525583144193652</id><published>2008-09-16T08:14:00.000-07:00</published><updated>2008-09-23T21:38:03.495-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='more'/><category scheme='http://www.blogger.com/atom/ns#' term='Server Tuning'/><category scheme='http://www.blogger.com/atom/ns#' term='Server Monitoring'/><title type='text'>Using Performance Monitor</title><content type='html'>&lt;p&gt;As you probably already know, SQL Server is very good at tuning itself. It has the ability to monitor itself, and through a feedback loop, it knows how to internally adjust and tune itself so that it keeps running efficiently, even when external events, such as the number of user connections or the amount of available RAM, change over time.&lt;/p&gt; &lt;p&gt;But as we all know, SQL Server's ability to self-tune is not perfect and does not take into consideration every possible aspect that affects its performance. As a DBA, we need to help SQL Server along, providing it the resources it needs for it to do a good job serving up data.&lt;/p&gt; &lt;p&gt;As a good DBA, we don't want to find out from our users that SQL Server is having a performance problem. Instead, we want to be proactive and catch performance problems before they arise. That is what Window's Performance Monitor can help us do. It is a tool that allows us to monitor what is going on with our SQL Server, and to provide us the information we need to make decisions on how to best tune our SQL Servers.&lt;/p&gt; &lt;span class="fullpost"&gt;&lt;p&gt;&lt;strong&gt;Performance Monitor is an important tool, because it not only provides us with information on how SQL Server is performing, but it also lets us know how Windows Server is doing&lt;/strong&gt;, which of course directly affects SQL Server's performance. [6.5, 7.0, 2000] &lt;em&gt;Updated 7-10-2006&lt;/em&gt;&lt;/p&gt;&lt;br /&gt;&lt;span class="fullpost"&gt;&lt;br /&gt;&lt;/span&gt;&lt;p&gt;The "&lt;strong&gt;Performance Monitor&lt;/strong&gt;" under the "Microsoft SQL Server" entry under your Start Menu is the same "Performance Monitor" under the "Administrative Tools" entry under your Start Menu. They are the same programs. What is different is that when you bring up Performance Monitor from under the "Microsoft SQL Server" entry, is that it comes up already running several pre-configured SQL Server performance counters.&lt;/p&gt; &lt;p&gt;The Performance Monitor under the "Administrative Tools" entry does not come with any pre-configured counters loaded. Personally, I dislike the "Microsoft SQL Server" option and always choose the Performance Monitor option under "Administrative Tools." This way, I always get to choose the SQL Server Performance Monitor counters I prefer to use. [6.5, 7.0] &lt;em&gt;Updated 7-10-2006&lt;/em&gt;&lt;/p&gt; &lt;p class="asterisk"&gt;*****&lt;/p&gt; &lt;p&gt;If you are like me, you have one or two SQL Server production servers that are very important to monitor. &lt;strong&gt;To help me keep tabs on these "high-visibility" SQL Servers, I always run an instance of Performance Monitor in the background on my Windows NT 4.0 or Windows 2000 Workstation desktop pointing to these servers&lt;/strong&gt;. I don't log this data, but I like the ability to very quickly take a look at key performance counters (in chart mode) throughout the day.&lt;/p&gt; &lt;p&gt;Since Performance Monitor is always running, I don't have any excuse not to take a peek at my SQL Server's performance at various times throughout the day. You would be surprised at the things you find, including bottlenecks you may not know you had. In addition, after some time, you begin to better learn how your server's perform, which makes it easier to diagnose potential problems as they arise.&lt;/p&gt; &lt;p&gt;In order to minimize the affect of this constant monitoring on your SQL Servers, you will not want to monitor too many counters. Here are the key counters I like to watch on a regular basis:&lt;/p&gt; &lt;ul&gt;&lt;li&gt;&lt;em&gt;Memory — Pages/Sec&lt;/em&gt;: To see how much paging my server is doing. This should be close to zero on a dedicated SQL Server. You will see spikes during backups and restores, but this is normal.&lt;/li&gt;&lt;li&gt;&lt;em&gt;Network Interface — Bytes Total/sec:&lt;/em&gt; To see how much network activity is going on.&lt;/li&gt;&lt;li&gt;&lt;em&gt;PhysicalDisk — % Disk Time — _Total&lt;/em&gt;: To see how busy all the disk drives are.&lt;/li&gt;&lt;li&gt;&lt;em&gt;PhysicalDisk — Current Disk Queue Length — _Total&lt;/em&gt;: Also to see how busy the drives are.&lt;/li&gt;&lt;li&gt;&lt;em&gt;System — % Total Processor Time&lt;/em&gt;: To see how busy all the CPUs are as a whole.&lt;/li&gt;&lt;li&gt;&lt;em&gt;System — Processor Queue Length&lt;/em&gt;: Also see how busy the CPUs are.&lt;/li&gt;&lt;li&gt;&lt;em&gt;SQLServer: General Statistics — User Connections&lt;/em&gt;: To see how many connections (and users) are using the server. Keep in mind that one connection does not equal one user. A single user can have more than one connection, and a single connection can have more than one user.&lt;/li&gt;&lt;li&gt;&lt;em&gt;SQLServer: Access Methods — Page Splits/sec&lt;/em&gt;: Lets me know if page splits are an issue or not. If so, then that means I need either to increase the fill factor of my indexes, or to rebuild the indexes more often.&lt;/li&gt;&lt;li&gt;&lt;em&gt;SQLServer: Buffer Manager — Buffer Cache Hit Ratio&lt;/em&gt;: To find out if I have enough memory in the server. Keep in mind that this ratio is based on the average of the buffer hit cache ratio since the SQL Server service was last restarted, and is not a reflection of the current buffer cache hit ratio.&lt;/li&gt;&lt;li&gt;&lt;em&gt;SQLServer: Memory Manager — Target Server Memory (KB)&lt;/em&gt;: To see how much memory SQL Server wants. If this is the same as the SQLServer: Memory Manager — Total Server Memory (KB) counter, then I know that SQL Server has all the memory that it wants.&lt;/li&gt;&lt;li&gt;&lt;em&gt;SQLServer: Memory Manager — Total Server Memory (KB)&lt;/em&gt;: To see how much memory SQL Server actual is using. If this is the same as SQLServer: Memory Manager — Target Server Memory (KB), then I know that SQL Server has all the memory that it wants. But if this is smaller, then SQL Server needs more available memory in order to run at its optimum performance.&lt;/li&gt;&lt;/ul&gt; &lt;p&gt;Based on my experiences and preferences, these are the counters I like to watch regularly. If I see something interesting in these counters, I often add additional counters as necessary to get a more detailed look at what is going on. I run Performance Monitor from my desktop, not the server I am monitoring in order to minimize overhead on the SQL Server.&lt;/p&gt; &lt;p&gt;By default, readings will appear every second and less than two minutes at a time will appear on the graph. I don't find this time frame all that useful, so I change it to 36 seconds, which displays an hour on the screen of activity. This gives me a good feel of the health of my critical SQL Servers without putting any undue overhead on the server.&lt;/p&gt; &lt;p&gt;If you don't already check your SQL Server's key performance counters throughout the day, you need to start this important habit. The more you learn about how your servers run, the better DBA you will be. [6.5, 7.0, 2000] &lt;em&gt;Updated 7-10-2006&lt;/em&gt;&lt;/p&gt; &lt;p class="asterisk"&gt;*****&lt;/p&gt; &lt;p&gt;&lt;strong&gt;Once you have identified the Performance Monitor counters you like to use, you can save them in a file and then later reload them&lt;/strong&gt; when you want to see them again. This way, you won't have to re-add the counters to Performance Monitor each time you use it. In fact, you can create different sets of counters, with different names, so you can track different types of counters at a time. In addition, each different type of Performance Monitor's modes, such as "Chart" and "Log," allows you to store its own set of counters.&lt;/p&gt; &lt;p&gt;How you use Performance Monitor to do this depends on if you are using Windows NT 4.0 or Windows 2000.&lt;/p&gt; &lt;p&gt;If you are using Windows NT 4.0, then use Performance Monitor's "File" menu option to save and load your counter files.&lt;/p&gt; &lt;p&gt;If you are using SQL Server 2000, then you will use the "Console" menu option save and load counter files. [6.5, 7.0, 2000] &lt;em&gt;Updated 7-10-2006&lt;/em&gt;&lt;/p&gt; &lt;p class="asterisk"&gt;*****&lt;/p&gt; &lt;p&gt;When monitoring your server using NT Server 4.0's "Performance Monitor," or Windows 2000's "System Monitor" tool, &lt;strong&gt;keep in mind that the more counters you monitor the more overhead that is required to perform the monitoring&lt;/strong&gt;. This is true whether you are viewing a performance chart, logging counters, or creating alerts based on counters. Because of this, don't monitor counters you don't need to monitor. If you are using multiple counters for your monitoring, but soon realize that one or more of them are of little value to the task at hand, then remove these counters from your monitoring.&lt;/p&gt; &lt;p&gt;One difference between NT Server 4.0's Performance Monitor and Windows 2000's System Monitor is how logging is done. In Performance Monitor, you must log entire performance objects, you can't just log individual counters. This can lead to very large log files with a lot of data you don't need. In System Monitor, you are now able to log individual counters, not just entire objects. This makes these logs much smaller in size, making it more practical to log over long periods of time. [6.5, 7.0, 2000] &lt;em&gt;Updated 7-10-2006&lt;/em&gt;&lt;/p&gt; &lt;p class="asterisk"&gt;*****&lt;/p&gt; &lt;p&gt;When monitoring your server using NT Server 4.0's "Performance Monitor," or Windows 2000's "Performance" tool, &lt;strong&gt;keep in mind that how often you set these tools to collect counter data affects the amount of overhead experienced by your server&lt;/strong&gt;. For example, the default counter collection interval for displaying near real-time charts of performance counters for these two tools is 1 second. If you increase this to .5 seconds, then overhead is essentially doubled. But if you decrease it to 5 seconds, then overhead is substantially reduced).&lt;/p&gt; &lt;p&gt;So how often should you collect performance counter data? It depends on what your goals are. In some cases, you need to collect data in near real-time (every second), and it other cases, collecting it every 5, 15, 30 seconds is adequate. The key is to not collect data more often than you really need to. Personally, when I collect performance counter data to display as a chart, I use 36-second intervals. Experience has proven to me that this time interval best meets my needs for monitor SQL Server's performance, and it also allows exactly one hour of chart data to be on the screen at one time. Of course, your mileage may vary. [6.5, 7.0, 2000] &lt;em&gt;Updated 7-10-2006&lt;/em&gt;&lt;/p&gt; &lt;p class="asterisk"&gt;*****&lt;/p&gt; &lt;p&gt;When monitoring a SQL Server using Performance Monitor, &lt;strong&gt;don't run Performance Monitor on the same server you are monitoring&lt;/strong&gt;. Instead, run it on a different server or workstation and remotely monitor the SQL Server. Running Performance Monitor on the same server you are monitoring will skew the results.&lt;/p&gt; Along the same train of thought, don't run both Performance Monitor and Profiler at the same time, even if you are running them remotely. This is too much overhead and will cause your SQL Server to suffer some performance degradation. In addition, running both together can cause Performance Monitor to produce less than accurate data because of the overhead of Profiler running at the same time. [6.5, 7.0, 2000] &lt;em&gt;&lt;/em&gt;&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7516218764458010868-8825525583144193652?l=all-about-database.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://all-about-database.blogspot.com/feeds/8825525583144193652/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7516218764458010868&amp;postID=8825525583144193652' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7516218764458010868/posts/default/8825525583144193652'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7516218764458010868/posts/default/8825525583144193652'/><link rel='alternate' type='text/html' href='http://all-about-database.blogspot.com/2008/09/using-performance-monitor.html' title='Using Performance Monitor'/><author><name>firman.arrow</name><uri>http://www.blogger.com/profile/14656110271655711161</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7516218764458010868.post-2537995714587465788</id><published>2008-09-16T08:06:00.001-07:00</published><updated>2008-09-23T21:39:58.428-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='more'/><category scheme='http://www.blogger.com/atom/ns#' term='Server Tuning'/><category scheme='http://www.blogger.com/atom/ns#' term='SQL Questions  - Answers'/><title type='text'>SQL Server Tuning</title><content type='html'>&lt;div class="clsQA" id="qa3"&gt;&lt;span style="font-size:180%;"&gt;Q&lt;/span&gt; : I have SQL Server 2000 with approximately 45 databases on a single instance. These databases are used by multiple ASP applications running on my public Web site. SQL Server regularly reaches 100 percent CPU utilization and requires reboots. How should I start troubleshooting and repairing this problem?&lt;/div&gt;&lt;br /&gt;&lt;span class="fullpost"&gt;&lt;br /&gt;&lt;div class="clsQA" id="qa4"&gt;&lt;span style="font-size:180%;"&gt;A : &lt;/span&gt;Start with rebuilding all of your indexes. It’s easy, assuming you have memory configured correctly. Then run SQL Profiler for a few hours and capture a snapshot of your standard business T-SQL traffic. Run this trace through the Index Tuning Wizard (ITW) that comes built into SQL Server 2000. It will take a look at the queries and make recommendations for indexes to improve performance.&lt;/div&gt;           &lt;div class="ArticleNormalPara"&gt;If you have SQL Server 2005 installed, you can use the Database Tuning Advisor (DTA) which is the successor to ITW, to tune your workload. DTA can tune SQL Server 2000 as well. It tunes a database on a production server by offloading most of the tuning load onto a test server. DTA uses the production server hardware configuration information, without actually copying the data from the production server to the test server. It only copies the metadata and necessary statistics.&lt;/div&gt;&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7516218764458010868-2537995714587465788?l=all-about-database.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://all-about-database.blogspot.com/feeds/2537995714587465788/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7516218764458010868&amp;postID=2537995714587465788' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7516218764458010868/posts/default/2537995714587465788'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7516218764458010868/posts/default/2537995714587465788'/><link rel='alternate' type='text/html' href='http://all-about-database.blogspot.com/2008/09/sql-server-tuning_16.html' title='SQL Server Tuning'/><author><name>firman.arrow</name><uri>http://www.blogger.com/profile/14656110271655711161</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7516218764458010868.post-9033563123362601936</id><published>2008-09-16T07:59:00.000-07:00</published><updated>2008-09-16T08:04:34.951-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='more'/><category scheme='http://www.blogger.com/atom/ns#' term='Server Tuning'/><category scheme='http://www.blogger.com/atom/ns#' term='SQL Questions  - Answers'/><title type='text'>Finding the Bottleneck</title><content type='html'>&lt;div class="clsQA" id="qa9"&gt;&lt;span style="font-size:180%;"&gt;Q&lt;/span&gt; : When I run one particular query, CPU usage will reach 100 percent after a few seconds, and the server will be shut down in a few minutes. I can reproduce it every time. We have hundreds of millions of unique values in the table in question.&lt;/div&gt;           &lt;div class="ArticleNormalPara"&gt;Is this a hardware issue or a software issue? I can’t get any information from Event Log. The server is an HP AMD 64-bit machine. I am using SQL Server 2005 (64-bit).&lt;/div&gt;&lt;br /&gt;&lt;span class="fullpost"&gt;&lt;br /&gt;&lt;div class="clsQA" id="qa10"&gt;&lt;span style="font-size:180%;"&gt;A&lt;/span&gt; : If the system really is crashing (bugchecking) then you will need to take a look in the memory.dmp file. First check the Startup and Recovery settings (in the Control Panel) and make sure it’s set to create at least a kernel memory dump. If that is already set, then find the memory.dmp file and save it to another machine. The next step is to download the Debugging Tools for Windows (windbg) from microsoft.com/whdc/devtools/debugging. Make sure to select the right package for your system (in this case you want the 64-bit versions), and then choose the x64 package.&lt;/div&gt;           &lt;div class="ArticleNormalPara"&gt;When the debugger is installed, run windbg and set the symbol path to the Microsoft Symbol Server. To do this, type Ctrl+S and enter a valid symbol path. I usually use something like srv*c:\symcache*http://msdl.microsoft.com/download/symbols. For more information on this, see &lt;a href="http://www.microsoft.com/whdc/devtools/debugging/debugstart.mspx"&gt;Debugging Tools and Symbols: Getting Started&lt;/a&gt;.&lt;/div&gt;           &lt;div class="ArticleNormalPara"&gt;Next, open the memory.dmp file in your favorite Windows debugger and run !analyze -v. This will tell you what the bugcheck was and give more details on why the server crashed.&lt;/div&gt;&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7516218764458010868-9033563123362601936?l=all-about-database.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://all-about-database.blogspot.com/feeds/9033563123362601936/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7516218764458010868&amp;postID=9033563123362601936' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7516218764458010868/posts/default/9033563123362601936'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7516218764458010868/posts/default/9033563123362601936'/><link rel='alternate' type='text/html' href='http://all-about-database.blogspot.com/2008/09/finding-bottleneck.html' title='Finding the Bottleneck'/><author><name>firman.arrow</name><uri>http://www.blogger.com/profile/14656110271655711161</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7516218764458010868.post-7255203075521232330</id><published>2008-09-16T07:52:00.000-07:00</published><updated>2008-09-16T08:03:40.991-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='more'/><category scheme='http://www.blogger.com/atom/ns#' term='Server Tuning'/><category scheme='http://www.blogger.com/atom/ns#' term='SQL Questions  - Answers'/><title type='text'>Log Shipping or Mirroring?</title><content type='html'>&lt;span style="font-size:180%;"&gt;Q&lt;/span&gt; : Is it true that once mirroring becomes available, there is no reason to use log shipping anymore? It seems that mirroring is less functional than log shipping if you want to use the mirrored database for reporting. It also seems that mirroring only allows you to query against a database snapshot, which represents a point in time. Log shipping, on the other hand, appears to allow you to query against current data. I know the data can be out of date depending on how often you ship the logs, but this seems better than having to constantly create new snapshots. In other words, shipping the logs every hour seems a lot better than using mirroring and creating a new snapshot each hour.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class="fullpost"&gt;&lt;br /&gt;&lt;span style="font-size:180%;"&gt;A&lt;/span&gt; : If reporting is needed, log shipping is not necessarily better in this scenario than mirroring and using snapshots. The log shipping secondary must be restored with the WITH STANDBY option in order to allow read-only access. The data is static at that point in time up to the last transaction log loaded, just like a database snapshot represents a point in time. If you need more up-to-date data to query against with log shipping, the secondary database must have transaction logs applied, which means users cannot access the database while that is occurring. Log shipping is not really a reporting solution.&lt;br /&gt;&lt;br /&gt;&lt;div class="ArticleNormalPara"&gt;Database mirroring and using a snapshot on the mirror may be easier to administer since you are not affecting the process like you are with log shipping. You are just creating a new snapshot on a periodic basis. The problem, though, with mirroring and snapshots is that since each snapshot is named, either each new snapshot created would have a different name (which may impact the reporting application) or the old snapshot would need to be deleted and then created again (which would impact the availability of the reporting solution). So to some degree, both log shipping and mirroring have different advantages as reporting solutions in addition to their intended use for availability and disaster recovery. But with log shipping, reporting is unavailable when you apply a new transaction log to refresh data, and with database mirroring, reporting is unavailable when you drop and recreate the existing snapshot, or create an additional snapshot to make it more current.&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;         &lt;div class="ArticleNormalPara"&gt;Log shipping is based on backup and restore, and supports multiple secondaries, each of which can have its own schedule or delay, which can also account for other problems, such as user error. Database mirroring can be simpler to implement. When using its synchronous mode, the mirror database is more up-to-date (with no data loss) than its log shipping counterpart, which is only as good as the last log that has been applied to the secondary. With the right configuration, mirroring also supports an automatic failover scenario. Database mirroring, unlike log shipping, can only have one mirror database. Both database mirroring and log shipping provide protection at the database level, while failover clustering provides protection at the instance level. A geographically dispersed cluster could work over distances for availability purposes, but this can be an expensive solution. Database mirroring and log shipping can work with clustering to provide extremely high availability for your SQL Server™ instances.&lt;/div&gt;&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7516218764458010868-7255203075521232330?l=all-about-database.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://all-about-database.blogspot.com/feeds/7255203075521232330/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7516218764458010868&amp;postID=7255203075521232330' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7516218764458010868/posts/default/7255203075521232330'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7516218764458010868/posts/default/7255203075521232330'/><link rel='alternate' type='text/html' href='http://all-about-database.blogspot.com/2008/09/log-shipping-or-mirroring.html' title='Log Shipping or Mirroring?'/><author><name>firman.arrow</name><uri>http://www.blogger.com/profile/14656110271655711161</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7516218764458010868.post-235161719726846730</id><published>2008-09-13T08:33:00.000-07:00</published><updated>2008-09-13T09:36:37.048-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='more'/><category scheme='http://www.blogger.com/atom/ns#' term='Firebird'/><title type='text'>Install Firebird 2.0.3 Database Server On Ubuntu 7.10</title><content type='html'>&lt;p&gt;This tutorial explains how you can install the &lt;a target="_blank" mce_real_href="http://www.firebirdsql.org/" href="http://www.firebirdsql.org/"&gt;Firebird database server&lt;/a&gt; (version 2.0.3) on an Ubuntu 7.10 server.&amp;nbsp;&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;&lt;b&gt;Step 1&lt;/b&gt;. Prepare an nice Ubuntu server : fire a "The Perfect Setup - Ubuntu"&lt;br /&gt;search on HowtoForge and follow the steps according to the OS version&lt;br /&gt;you are decided to go on.&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;&lt;b&gt;Step 2.&lt;/b&gt; Do the libstdc++5 installation (before installing Firebird) &lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p class="command"&gt;&lt;br /&gt;user@machine:~# sudo apt-get install libstdc++5&lt;/p&gt;&lt;br /&gt;&lt;span class="fullpost"&gt;&lt;br /&gt;&lt;p&gt;&lt;b&gt;Step 3.&lt;/b&gt; Download and expand Firebird installation kit from&lt;br /&gt;&lt;a target="_blank" mce_real_href="http://firebirdsql.org" href="http://firebirdsql.org"&gt;http://firebirdsql.org&lt;/a&gt; (downloads) or source force: the 2.0.3 NTPL&lt;br /&gt;superserver.&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;- for 32 bit OS:&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p class="command"&gt;&lt;br /&gt;&lt;br /&gt;user@machine:~# sudo wget http://downloads.sourceforge.net/firebird/FirebirdSS-2.0.3.12981-1.nptl.i686.tar.gz&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p class="command"&gt;&lt;br /&gt;user@machine:~# sudo tar -xvf FirebirdSS-2.0.3.12981-1.nptl.i686.tar.gz&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p class="command"&gt;user@machine:~# cd cd FirebirdSS-2.0.3.12981-1.i686&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;&lt;br /&gt;- for 64 bit OS:&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p class="command"&gt;&lt;br /&gt;user@machine:~# sudo wget http://downloads.sourceforge.net/firebird/FirebirdSS-2.0.3.12981-1.amd64.tar.gz&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p class="command"&gt;user@machine:~# sudo tar -xvf FirebirdSS-2.0.3.12981-1.amd64.tar.gz&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p class="command"&gt;user@machine:~# cd FirebirdSS-2.0.3.12981-1.amd64&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;&lt;br /&gt;(remark: is NTPL by default for 64 bit OS)&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;&lt;b&gt;Step 4.&lt;/b&gt; Install Firebird.&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p class="command"&gt;&lt;br /&gt;&lt;br /&gt;- user@machine:~# sudo ./install.sh&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;&lt;br /&gt;(remark : install script will prompt you twice for the SYSDBA password&lt;/p&gt;&lt;br /&gt;              &lt;script type="text/javascript"&gt;&lt;br /&gt;&lt;!--&lt;br /&gt;document.write('&lt;div align="center"&gt;');&lt;br /&gt;//--&gt;&lt;br /&gt;&lt;/script&gt;&lt;div align="center"&gt;&lt;br /&gt;&lt;!-- Begin Adify tag for "HowtoForge Rectangle B" Ad Space (300x250) ID #4098007 --&gt;&lt;br /&gt;&lt;script type="text/javascript"&gt;&lt;br /&gt;    sr_adspace_id = 4098007;&lt;br /&gt;    sr_adspace_width = 300;&lt;br /&gt;    sr_adspace_height = 250;&lt;br /&gt;    sr_adspace_type = "graphic";&lt;br /&gt;    sr_ad_new_window = true;&lt;br /&gt;&lt;br /&gt;&lt;/script&gt;&lt;br /&gt;&lt;script type="text/javascript" src="http://ad.afy11.net/srad.js?azId=4098007"&gt;&lt;br /&gt;&lt;/script&gt;&lt;br /&gt;&lt;!-- End Adify tag for "HowtoForge Rectangle B" Ad Space (300x250) ID #4098007 --&gt;&lt;br /&gt;&lt;script type="text/javascript"&gt;&lt;br /&gt;&lt;!--&lt;br /&gt;document.write('&lt;/div&gt;');&lt;br /&gt;//--&gt;&lt;br /&gt;&lt;/script&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;&lt;b&gt;Step 5.&lt;/b&gt; Do some UDF's housekeeping.&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;&lt;br /&gt;- if you have UDF's please (sudo) put them in&amp;nbsp; &lt;span class="system"&gt;/opt/firebird/UDF/&lt;/span&gt;&lt;br /&gt;folder. Of course the UDF's must be compiled according to OS version&lt;br /&gt;....&lt;br&gt;&lt;br /&gt;- change rights on UDF's folder:&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p class="command"&gt;&lt;br /&gt;user@machine:~# sudo chown root:root /opt/firebird/UDF/*&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p class="command"&gt;user@machine:~# sudo chmod 444 /opt/firebird/UDF/*&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;&lt;br /&gt;- and restart firebird :&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p class="command"&gt;&lt;br /&gt;user@machine:~# sudo /etc/init.d/firebird reload&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;&lt;b&gt;Optional step 6.&lt;/b&gt; Move your data:&lt;br&gt;&lt;br /&gt;&lt;br /&gt;- do a backup on the old machine:&lt;/p&gt;&lt;br /&gt;&lt;p class="command"&gt;&lt;br /&gt;&amp;lt;put path (include ending slash at the end) to gbak here&amp;gt;gbak&amp;nbsp; -b&lt;br /&gt;-t -v -USER SYSDBA -PASS &amp;lt;put SYSDBA password here&amp;gt; &amp;lt;put path&lt;br /&gt;and name of the database here&amp;gt; &amp;lt;put path and name of the backup&lt;br /&gt;file here&amp;gt;&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;&lt;br /&gt;- make home of the new database on the new machine:&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p class="command"&gt;&lt;br /&gt;user@machine:~# sudo mkdir &amp;lt;put path to the database location here&amp;gt;&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;&lt;br /&gt;- and give Firebird the needed rights there:&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p class="command"&gt;&lt;br /&gt;user@machine:~# sudo chown firebird:firebird &amp;lt;put path to the database (include ending slash at the end) location here&amp;gt;&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;&amp;nbsp;- move the backup file from old to the new machine in the &lt;i&gt;&amp;lt;put path&lt;br /&gt;to the database (include ending slash at the end) location here&amp;gt;&lt;/i&gt;&lt;br /&gt;&lt;br /&gt;folder and restore data:&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p class="command"&gt;&amp;lt;put path (include ending slash at the end) to gbak here&amp;gt;gbak&amp;nbsp; -c&lt;br /&gt;-v -t -USER&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p class="command"&gt; SYSDBA -PASS &amp;lt;put SYSDBA password here&amp;gt; &amp;lt;put path and name of the backup&lt;br /&gt;file here&amp;gt; &amp;lt;put path&lt;br /&gt;and name of the database here&amp;gt;&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;&amp;nbsp;Enjoy.&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7516218764458010868-235161719726846730?l=all-about-database.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://all-about-database.blogspot.com/feeds/235161719726846730/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7516218764458010868&amp;postID=235161719726846730' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7516218764458010868/posts/default/235161719726846730'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7516218764458010868/posts/default/235161719726846730'/><link rel='alternate' type='text/html' href='http://all-about-database.blogspot.com/2008/09/install-firebird-203-database-server-on.html' title='Install Firebird 2.0.3 Database Server On Ubuntu 7.10'/><author><name>firman.arrow</name><uri>http://www.blogger.com/profile/14656110271655711161</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7516218764458010868.post-7491365155368445194</id><published>2008-09-13T08:30:00.000-07:00</published><updated>2008-09-13T08:55:42.227-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Replication'/><category scheme='http://www.blogger.com/atom/ns#' term='SQL Server 2000'/><category scheme='http://www.blogger.com/atom/ns#' term='SQL Server'/><title type='text'>SQL Server 2000 - Merge Replication</title><content type='html'>By : D J Nagendra&lt;br /&gt;&lt;h2&gt;Introduction&lt;/h2&gt;&lt;br /&gt;&lt;p&gt;&lt;b&gt;Replication&lt;/b&gt; is the process of sharing data between databases in different locations. Using replication, we can create copies of the database and share the copy with different users so that they can make changes to their local copy of database and later synchronize the changes to the source database.&lt;/p&gt;&lt;br /&gt;&lt;p&gt;Microsoft SQL Server replication uses &lt;b&gt;publisher&lt;/b&gt;, &lt;b&gt;distributor&lt;/b&gt; and &lt;b&gt;subscriber&lt;/b&gt; entities.&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class="fullpost"&gt;&lt;br /&gt;&lt;p&gt;&lt;b&gt;Publisher&lt;/b&gt; is a server that makes the data available for subscription to other servers. In addition to that, publisher also identifies what data has changed at the subscriber during the synchronizing process. Publisher contains &lt;b&gt;publication(s)&lt;/b&gt;.&lt;/p&gt;&lt;br /&gt;&lt;p&gt;&lt;b&gt;Subscriber&lt;/b&gt; is a server that receives and maintains the published data. Modifications to the data at subscriber can be propagated back to the publisher.&lt;/p&gt;&lt;br /&gt;&lt;p&gt;&lt;b&gt;Distributor&lt;/b&gt; is the server that manages the flow of data through the replication system. Two types of distributors are present, one is remote distributor and the other one local distributor. Remote distributor is separate from publisher and is configured as distributor for replication. Local distributor is a server that is configured as publisher and distributor.&lt;/p&gt;&lt;br /&gt;&lt;p&gt;&lt;b&gt;Agents&lt;/b&gt; are the processes that are responsible for copying and distributing data between publisher and subscriber. There are different types of agents supporting different types of replication.&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;An &lt;b&gt;article&lt;/b&gt; can be any database object, like Tables (Column filtered or Row filtered), Views, Indexed views, Stored Procedures, and User defined functions.&lt;/p&gt;&lt;br /&gt;&lt;p&gt;&lt;b&gt;Publication&lt;/b&gt; is a collection of articles.&lt;/p&gt;&lt;br /&gt;&lt;p&gt;&lt;b&gt;Subscription&lt;/b&gt; is a request for copy of data or database objects to be replicated.&lt;/p&gt;&lt;br /&gt;&lt;h4&gt;Types of Subscription:&lt;/h4&gt;&lt;br /&gt;&lt;p&gt;Changes to the subscriptions at the publisher can be replicated to subscribers via PUSH subscription or PULL subscription. With &lt;i&gt;Push&lt;/i&gt; subscription, the publisher is responsible for synchronizing all the changes to the subscriber without subscriber asking for those changes. With &lt;i&gt;Pull&lt;/i&gt; subscription, the subscriber initiates the replication instead of the publisher.&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;h2&gt;Replication Types&lt;/h2&gt;&lt;br /&gt;&lt;p&gt;Microsoft SQL Server 2000 supports the following types of replication:&lt;/p&gt;&lt;br /&gt;&lt;ul&gt;&lt;br /&gt;&lt;li&gt;Snapshot Replication &lt;br /&gt;&lt;/li&gt;&lt;li&gt;Transactional Replication &lt;br /&gt;&lt;/li&gt;&lt;li&gt;Merge Replication &lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;&lt;h3&gt;Snapshot Replication&lt;/h3&gt;&lt;br /&gt;&lt;ul&gt;&lt;br /&gt;&lt;li&gt;Snapshot replication is also known as static replication. Snapshot replication copies and distributes data and database objects exactly as they appear at the current moment in time. &lt;br /&gt;&lt;/li&gt;&lt;li&gt;Subscribers are updated with complete modified data and not by individual transactions, and are not continuous in nature. &lt;br /&gt;&lt;/li&gt;&lt;li&gt;This type is mostly used when the amount of data to be replicated is small and data/DB objects are static or does not change frequently. &lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;&lt;h3&gt;Transactional Replication&lt;/h3&gt;&lt;br /&gt;&lt;br /&gt;&lt;ul&gt;&lt;br /&gt;&lt;li&gt;Transactional replication is also known as dynamic replication. In transactional replication, modifications to the publication at the publisher are propagated to the subscriber incrementally. &lt;br /&gt;&lt;/li&gt;&lt;li&gt;Publisher and the subscriber are always in synchronization and should always be connected. &lt;br /&gt;&lt;/li&gt;&lt;li&gt;This type is mostly used when subscribers always need the latest data for processing. &lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;&lt;h3&gt;Merge replication&lt;/h3&gt;&lt;br /&gt;&lt;p&gt;It allows making autonomous changes to replicated data on the Publisher and on the Subscriber. With merge replication, SQL Server captures all incremental data changes in the source and in the target databases, and reconciles conflicts according to rules you configure or using a custom resolver you create. Merge replication is best used when you want to support autonomous changes on the replicated data on the Publisher and on the Subscriber.&lt;/p&gt;&lt;br /&gt;&lt;p&gt;Replication agents involved in merge replication are snapshot agent and merge agent.&lt;/p&gt;&lt;br /&gt;&lt;p&gt;Implement merge replication if, changes are made constantly at the publisher and subscribing servers, and must be merged in the end.&lt;/p&gt;&lt;br /&gt;&lt;p&gt;By default, the publisher wins all conflicts that it has with subscribers because it has the highest priority. Conflict resolver can be customized.&lt;/p&gt;&lt;br /&gt;&lt;h2&gt;Necessary steps to be taken before doing replication process:&lt;/h2&gt;&lt;br /&gt;&lt;br /&gt;&lt;ol&gt;&lt;br /&gt;&lt;li&gt;Before starting the replication process, change the log on account for the MSSQLSERVER service as “This account”. Use any SQL login account which is a member of sysadmin server role. Please see the screenshot for more information. Don’t forget to restart the MSSQLSERVER service. &lt;br /&gt;&lt;p&gt;&lt;img src="MergeReplication/image001.gif" height="461" width="410"&gt;&lt;/p&gt;&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Adequate disk space should be allocated for publisher, distribution and subscriber’s databases. &lt;br /&gt;&lt;/li&gt;&lt;li&gt;Use &lt;i&gt;NOT FOR REPLICATION&lt;/i&gt; option when defining Identity columns. &lt;/li&gt;&lt;/ol&gt;&lt;br /&gt;&lt;h2&gt;Step by Step Procedure for Merge Replication setup&lt;/h2&gt;&lt;br /&gt;&lt;ol&gt;&lt;br /&gt;&lt;li&gt;Open SQL Server Enterprise Manager and select Tools menu -&amp;gt; Replication -&amp;gt; Configure Publishing, Subscribers, and Distribution… &lt;br /&gt;&lt;br /&gt;&lt;ol type="a"&gt;&lt;br /&gt;&lt;li&gt;Configure the appropriate server as publisher or distributor. &lt;br /&gt;&lt;p&gt;&lt;img src="MergeReplication/image002.gif" height="447" width="403"&gt;&lt;/p&gt;&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Enable the appropriate database for merge replication. &lt;br /&gt;&lt;p&gt;&lt;img src="MergeReplication/image003.gif" height="446" width="402"&gt;&lt;/p&gt;&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Enable the appropriate server as subscriber. &lt;br /&gt;&lt;p&gt;&lt;img src="MergeReplication/image004.gif" height="446" width="400"&gt;&lt;/p&gt;&lt;/li&gt;&lt;/ol&gt;&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Open SQL Server Enterprise Manager and select the appropriate SQL Server Group for which replication needs to be done, then select Tools menu -&amp;gt; Replication -&amp;gt; Create and Manage Publications. &lt;br /&gt;&lt;p&gt;&lt;img src="MergeReplication/image005.gif" height="318" width="513"&gt;&lt;/p&gt;&lt;br /&gt;&lt;/li&gt;&lt;li&gt;This will open a dialog box for “Create and Manage Publications on respective server”. Select the appropriate database and then click “Create Publication”. This will open “Create Publication Wizard”. Just click &lt;i&gt;Next&lt;/i&gt;. &lt;br /&gt;&lt;p&gt;&lt;img src="MergeReplication/image006.jpg" height="386" width="525"&gt;&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;/li&gt;&lt;li&gt;It will ask to choose a Distributor for the selected server. Select “Make Server its own Distributor; SQL Server will create a distribution database and a log”. Then click &lt;i&gt;Next&lt;/i&gt;. &lt;br /&gt;&lt;p&gt;&lt;img src="MergeReplication/image007.gif" height="386" width="525"&gt;&lt;/p&gt;&lt;br /&gt;&lt;/li&gt;&lt;li&gt;It will ask for the Snapshot folder path. Browse and select the appropriate path for Snapshot folder and then click &lt;i&gt;Next&lt;/i&gt;. &lt;br /&gt;&lt;p&gt;&lt;b&gt;Note&lt;/b&gt;: Create one folder in the Publisher machine and share the folder, then give full permissions for the user through which you logged in. Make sure that you are able to access this folder from the Subscriber machine also. If you are not able to access, give full permissions to that shared folder for the appropriate user in the Publisher machine. The Snapshot folder should be in the Publisher machine.&lt;/p&gt;&lt;br /&gt;&lt;p&gt;&lt;img src="MergeReplication/image008.gif" height="386" width="525"&gt;&lt;/p&gt;&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Choose the database which you want to publish and Click Next. &lt;br /&gt;&lt;p&gt;&lt;img src="MergeReplication/image009.gif" height="386" width="525"&gt;&lt;/p&gt;&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Select the Publication Type as “Merge Publication”. &lt;br /&gt;&lt;p&gt;&lt;img src="MergeReplication/image010.jpg" height="386" width="525"&gt;&lt;/p&gt;&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Specify the Subscriber Types. Select “Servers running SQL Server 2000”. Then click &lt;i&gt;Next&lt;/i&gt;. &lt;br /&gt;&lt;br /&gt;&lt;p&gt;&lt;img src="MergeReplication/image011.jpg" height="386" width="525"&gt;&lt;/p&gt;&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Select the Object Types (like Tables, Stored Procedures and Views) which you want to publish, and click &lt;i&gt;Next&lt;/i&gt;. &lt;br /&gt;&lt;p&gt;&lt;img src="MergeReplication/image012.jpg" height="386" width="525"&gt;&lt;/p&gt;&lt;br /&gt;&lt;/li&gt;&lt;li&gt;It will show some issues which may require some changes at later stages in order to work as expected. Just click &lt;i&gt;Next&lt;/i&gt;. &lt;br /&gt;&lt;p&gt;&lt;img src="MergeReplication/image013.jpg" height="386" width="525"&gt;&lt;/p&gt;&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Give Publication Name and click &lt;i&gt;Next&lt;/i&gt;. &lt;br /&gt;&lt;p&gt;&lt;img src="MergeReplication/image014.gif" height="386" width="525"&gt;&lt;/p&gt;&lt;br /&gt;&lt;/li&gt;&lt;li&gt;It will ask to customize the properties of the Publication. Select “Yes, I will define data filters, enable anonymous subscriptions, or customize other properties”. Then click &lt;i&gt;Next&lt;/i&gt;. &lt;br /&gt;&lt;p&gt;&lt;img src="MergeReplication/image015.gif" height="386" width="526"&gt;&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Then, it will ask “How do you want to filter this publication?” Don’t select any thing here. Just click &lt;i&gt;Next&lt;/i&gt;. &lt;br /&gt;&lt;p&gt;&lt;img src="MergeReplication/image016.jpg" height="386" width="525"&gt;&lt;/p&gt;&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Then, it will ask “Whether you want to allow anonymous subscription to this publication?”. Select “No, allow only named subscriptions”, and click &lt;i&gt;Next&lt;/i&gt;. &lt;br /&gt;&lt;p&gt;&lt;img src="MergeReplication/image017.jpg" height="386" width="525"&gt;&lt;/p&gt;&lt;br /&gt;&lt;/li&gt;&lt;li&gt;It will show “Set Snapshot Agent Schedule” dialog box. Change the Snapshot Agent Schedule as per your requirement, then select “Create the first snapshot immediately”. And click &lt;i&gt;Next&lt;/i&gt;. &lt;br /&gt;&lt;p&gt;&lt;img src="MergeReplication/image018.jpg" height="386" width="525"&gt;&lt;/p&gt;&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Click Finish to create a Publication. &lt;br /&gt;&lt;p&gt;&lt;img src="MergeReplication/image019.gif" height="386" width="525"&gt;&lt;/p&gt;&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Finally, it will show “SQL Server Enterprise Manager successfully created publication ‘pub1’ from database ‘db1’. Just click &lt;i&gt;Close&lt;/i&gt;. &lt;br /&gt;&lt;br /&gt;&lt;p&gt;&lt;img src="MergeReplication/image020.jpg" height="259" width="381"&gt;&lt;/p&gt;&lt;br /&gt;&lt;p&gt;&lt;img src="MergeReplication/image021.jpg" height="259" width="381"&gt;&lt;/p&gt;&lt;br /&gt;&lt;/li&gt;&lt;li&gt;It will show the dialog box “Create and Manage Publications on respective Server”. Now go to the respective created Publication and click “Push New Subscription”. &lt;br /&gt;&lt;p&gt;&lt;img src="MergeReplication/image022.gif" height="332" width="537"&gt;&lt;/p&gt;&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Before doing “Push New Subscription”, create new SQL Server Registration for Subscriber machine in the Publisher machine’s SQL Server Enterprise manager with SQL Authentication mode. For this, there should be one common SQL login name in both Publisher and Subscriber machines. Set server roles for this user as System Administrator, Process Administrator and Bulk Insert Administrators, and give database access to the respective database for which you want to perform replication. &lt;br /&gt;&lt;/li&gt;&lt;li&gt;Go to “Push New Subscription” wizard. This will open “Push Subscription Wizard”. Just Click Next. &lt;br /&gt;&lt;p&gt;&lt;img src="MergeReplication/image023.gif" height="386" width="503"&gt;&lt;/p&gt;&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Choose one or more subscribers from Enabled Subscribers and click &lt;i&gt;Next&lt;/i&gt;. (Note: It will show the Subscriber’s SQL Server name under Enable Subscribers only if you do step 19.) &lt;br /&gt;&lt;p&gt;&lt;img src="MergeReplication/image024.gif" height="386" width="503"&gt;&lt;/p&gt;&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Choose Subscription (destination) database name by browsing and clicking &lt;i&gt;Next&lt;/i&gt;. (Note: You can create new database if you want by clicking &lt;i&gt;Create New&lt;/i&gt;). &lt;br /&gt;&lt;p&gt;&lt;img src="MergeReplication/image025.jpg" height="386" width="503"&gt;&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;/li&gt;&lt;li&gt;“Set Merge Agent Schedule”. Change the Schedule as per your requirement and click &lt;i&gt;Next&lt;/i&gt;. &lt;br /&gt;&lt;p&gt;&lt;img src="MergeReplication/image026.jpg" height="386" width="503"&gt;&lt;/p&gt;&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Specify whether the Subscription(s) needs to be initialized or not. Select “Yes, initialize the schema and data” as well as select “Start the Snapshot Agent to begin the initialization process immediately”, and click &lt;i&gt;Next&lt;/i&gt;. &lt;br /&gt;&lt;p&gt;&lt;img src="MergeReplication/image027.jpg" height="386" width="503"&gt;&lt;/p&gt;&lt;br /&gt;&lt;/li&gt;&lt;li&gt;“Set Subscription Priority” as “Use the Publisher as a proxy for the Subscriber when resolving conflicts”, and click &lt;i&gt;Next&lt;/i&gt;. &lt;br /&gt;&lt;p&gt;&lt;img src="MergeReplication/image028.jpg" height="386" width="503"&gt;&lt;/p&gt;&lt;br /&gt;&lt;/li&gt;&lt;li&gt;It will show the status of the SQLSERVERAGENT service as running. Just click &lt;i&gt;Next&lt;/i&gt;. &lt;br /&gt;&lt;p&gt;&lt;img src="MergeReplication/image029.gif" height="387" width="503"&gt;&lt;/p&gt;&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Click &lt;i&gt;Finish&lt;/i&gt; to complete the Push Subscription. &lt;br /&gt;&lt;br /&gt;&lt;p&gt;&lt;img src="MergeReplication/image030.gif" height="386" width="503"&gt;&lt;/p&gt;&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Finally, it will show “Subscriptions were created successfully at the following Subscribers:”. Just click &lt;i&gt;Close&lt;/i&gt;. &lt;br /&gt;&lt;p&gt;&lt;img src="MergeReplication/image031.gif" height="384" width="381"&gt;&lt;/p&gt;&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Now, in the SQL Server Enterprise Manager, go to the appropriate SQL Server Group and go to “Replication Monitor -&amp;gt; Publishers -&amp;gt; Respective Server -&amp;gt; Publication Name”. In the right pane, you will see the snapshot agent. Just right click and select “Start Agent”. Refresh it once. Then right click on the respective publication name and select “Start Synchronizing”. It will merge the necessary data. Refresh it once. &lt;/li&gt;&lt;/ol&gt;&lt;br /&gt;&lt;h4&gt;Important Note:&lt;/h4&gt;&lt;br /&gt;&lt;p&gt;SQL Server 2000 replication will not support full-text indexing. But, enable full-text indexing at the subscriber machine manually. This can be done by Full-text indexing wizard. Select the appropriate table and enable the required fields in that table as full-text indexed. Then, create a new catalog or else use the existing catalog and schedule it, if needed. Once this is done, go to that particular catalog and right click and select “Start full population”. The status will be displayed as “population in progress”.&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;h3&gt;Advantages in Replication:&lt;/h3&gt;&lt;br /&gt;&lt;p&gt;Users can avail the following advantages by using replication process:&lt;/p&gt;&lt;br /&gt;&lt;ul&gt;&lt;br /&gt;&lt;li&gt;Users working in different geographic locations can work with their local copy of data thus allowing greater autonomy. &lt;br /&gt;&lt;/li&gt;&lt;li&gt;Database replication can also supplement your disaster-recovery plans by duplicating the data from a local database server to a remote database server. If the primary server fails, your applications can switch to the replicated copy of the data and continue operations. &lt;br /&gt;&lt;/li&gt;&lt;li&gt;You can automatically back up a database by keeping a replica on a different computer. Unlike traditional backup methods that prevent users from getting access to a database during backup, replication allows you to continue making changes online. &lt;br /&gt;&lt;/li&gt;&lt;li&gt;You can replicate a database on additional network servers and reassign users to balance the loads across those servers. You can also give users who need constant access to a database their own replica, thereby reducing the total network traffic. &lt;br /&gt;&lt;/li&gt;&lt;li&gt;Database-replication logs the selected database transactions to a set of internal replication-management tables, which can then be synchronized to the source database. Database replication is different from file replication, which essentially copies files. &lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;&lt;h3&gt;Replication Performance Tuning Tips:&lt;/h3&gt;&lt;br /&gt;&lt;ul&gt;&lt;br /&gt;&lt;li&gt;By distributing partitions of data to different Subscribers. &lt;br /&gt;&lt;/li&gt;&lt;li&gt;When running SQL Server replication on a dedicated server, consider setting the minimum memory amount for SQL Server to use from the default value of 0 to a value closer to what SQL Server normally uses. &lt;br /&gt;&lt;/li&gt;&lt;li&gt;Don’t publish more data than you need. Try to use Row filter and Column filter options wherever possible as explained above. &lt;br /&gt;&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Avoid creating triggers on tables that contain subscribed data. &lt;br /&gt;&lt;/li&gt;&lt;li&gt;Applications that are updated frequently are not good candidates for database replication. &lt;br /&gt;&lt;/li&gt;&lt;li&gt;For best performance, avoid replicating columns in your publications that include &lt;code lang="sql"&gt;&lt;span class="code-keyword"&gt;TEXT&lt;/span&gt;&lt;/code&gt;, &lt;code lang="sql"&gt;&lt;span class="code-keyword"&gt;NTEXT&lt;/span&gt;&lt;/code&gt; or &lt;code lang="sql"&gt;&lt;span class="code-keyword"&gt;IMAGE&lt;/span&gt;&lt;/code&gt; data types. &lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;&lt;h2&gt;Conclusion&lt;/h2&gt;&lt;br /&gt;&lt;p&gt;In a nutshell, replication is the capability to reliably duplicate data from a source database to one or more destination databases. SQL Server 2000 gives you the power for replication design, implementation, monitoring, and administration. This gives you the functionality and flexibility needed for distributing copy of data and maintaining data consistency among the distributed. You can automatically distribute data from one SQL Server to many different SQL Servers through ODBC (Open Database Connectivity) or OLE DB. SQL Server replication provides update replication capabilities such as Immediate Updating Subscribers and merges replication. With all the new enhancements to SQL Server replication, the number of possible applications and business scenarios is mind-boggling.&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7516218764458010868-7491365155368445194?l=all-about-database.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://all-about-database.blogspot.com/feeds/7491365155368445194/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7516218764458010868&amp;postID=7491365155368445194' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7516218764458010868/posts/default/7491365155368445194'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7516218764458010868/posts/default/7491365155368445194'/><link rel='alternate' type='text/html' href='http://all-about-database.blogspot.com/2008/09/sql-server-2000-merge-replication.html' title='SQL Server 2000 - Merge Replication'/><author><name>firman.arrow</name><uri>http://www.blogger.com/profile/14656110271655711161</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7516218764458010868.post-5555256294477773014</id><published>2008-09-13T01:46:00.000-07:00</published><updated>2008-09-13T02:33:48.256-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='PHP'/><category scheme='http://www.blogger.com/atom/ns#' term='more'/><category scheme='http://www.blogger.com/atom/ns#' term='SQL Server'/><title type='text'>Accessing SQL Server Databases with PHP</title><content type='html'>&lt;p&gt;SQL Server Technical Article&lt;br /&gt;&lt;br /&gt;Writer: Brian Swan&lt;br /&gt;Published: August 2008&lt;br /&gt;Applies to: SQL Server 2005 and SQL Server 2008 – all editions&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:130%;"&gt;Summary:&lt;/span&gt; &lt;/p&gt;&lt;p&gt;The SQL Server 2005 Driver for PHP is a Microsoft-supported extension of PHP 5 that provides data access to SQL Server 2005 and SQL Server 2008. The extension provides a procedural interface for accessing data in all editions of SQL Server 2005 and SQL Server 2008. The SQL Server 2005 Driver for PHP API provides a comprehensive data access solution from PHP, and includes support for many features including Windows Authentication, transactions, parameter binding, streaming, metadata access, connection pooling, and error handling. This paper discusses how to use several of these features by closely examining parts of the &lt;a id="ctl00_rs1_mainContentContainer_ctl01" onclick="javascript:Track('ctl00_rs1_mainContentContainer_ctl00ctl00_rs1_mainContentContainer_ctl01',this);" href="http://go.microsoft.com/fwlink/?LinkId=124498"&gt;Example Application&lt;/a&gt; in the SQL Server 2005 Driver for PHP product documentation in MSDN. This paper assumes that the reader is familiar with programming in PHP, that the reader has a computer that meets the &lt;a id="ctl00_rs1_mainContentContainer_ctl02" onclick="javascript:Track('ctl00_rs1_mainContentContainer_ctl00ctl00_rs1_mainContentContainer_ctl02',this);" href="http://go.microsoft.com/fwlink/?LinkId=124500"&gt;System Requirements&lt;/a&gt; listed for using the driver, and that the &lt;a id="ctl00_rs1_mainContentContainer_ctl03" onclick="javascript:Track('ctl00_rs1_mainContentContainer_ctl00ctl00_rs1_mainContentContainer_ctl03',this);" href="http://go.microsoft.com/fwlink/?LinkID=67739"&gt;AdventureWorks&lt;/a&gt; example database is installed from CodePlex.&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;br /&gt;&lt;span class="fullpost"&gt;&lt;br /&gt;&lt;a name="_Toc144986095"&gt;&lt;span style="font-size:130%;"&gt;Introduction&lt;/span&gt;&lt;/a&gt; &lt;p class="Text" style="LINE-HEIGHT: normal"&gt;The Microsoft SQL Server 2005 Driver for PHP allows PHP developers to access data in SQL Server 2005 and SQL Server 2008 databases. The driver includes support for Windows and SQL Server Authentication methods, transactions, parameter binding, streaming, metadata access, connection pooling, and error handling. &lt;/p&gt;&lt;p class="Text" style="LINE-HEIGHT: normal"&gt;This paper explains how to load and configure the driver, and discusses how to leverage several of the features mentioned above. Parts of the &lt;a id="ctl00_rs1_mainContentContainer_ctl04" onclick="javascript:Track('ctl00_rs1_mainContentContainer_ctl00ctl00_rs1_mainContentContainer_ctl04',this);" href="http://go.microsoft.com/fwlink/?LinkId=124498"&gt;Example Application&lt;/a&gt; in the product documentation will be used to demonstrate these programming scenarios.&lt;/p&gt;&lt;p class="Text" style="LINE-HEIGHT: normal"&gt;For more information about a complete list of driver features and functions, see the &lt;a id="ctl00_rs1_mainContentContainer_ctl05" onclick="javascript:Track('ctl00_rs1_mainContentContainer_ctl00ctl00_rs1_mainContentContainer_ctl05',this);" href="http://msdn.microsoft.com/en-us/library/cc296152.aspx"&gt;API Reference&lt;/a&gt; in the SQL Server 2005 Driver for PHP product documentation in MSDN. The prefix for all driver functions is &lt;b&gt;sqlsrv&lt;/b&gt;.&lt;/p&gt;&lt;p class="Text" style="LINE-HEIGHT: normal"&gt;The SQL Server Driver for PHP relies on the Microsoft SQL Server 2005 ODBC Driver to handle the low-level communication with SQL Server. As a result, the SQL Server Driver for PHP is only supported on Windows. Microsoft provides support for this driver under its normal support methods. While the source code for this driver has been made available on the &lt;a id="ctl00_rs1_mainContentContainer_ctl06" onclick="javascript:Track('ctl00_rs1_mainContentContainer_ctl00ctl00_rs1_mainContentContainer_ctl06',this);" href="http://go.microsoft.com/fwlink/?LinkId=123025"&gt;codeplex.com&lt;/a&gt; website, Microsoft supports only the signed version of the driver from the MSDN download site.&lt;/p&gt;&lt;p class="Text" style="LINE-HEIGHT: normal"&gt;This paper assumes that the reader is familiar with programming in PHP, that the reader has a computer that meets the &lt;a id="ctl00_rs1_mainContentContainer_ctl07" onclick="javascript:Track('ctl00_rs1_mainContentContainer_ctl00ctl00_rs1_mainContentContainer_ctl07',this);" href="http://go.microsoft.com/fwlink/?LinkId=124500"&gt;System Requirements&lt;/a&gt; listed for using the driver, and that the &lt;a id="ctl00_rs1_mainContentContainer_ctl08" onclick="javascript:Track('ctl00_rs1_mainContentContainer_ctl00ctl00_rs1_mainContentContainer_ctl08',this);" href="http://go.microsoft.com/fwlink/?LinkID=67739"&gt;AdventureWorks&lt;/a&gt; example database is installed.&lt;/p&gt;&lt;h2 style="MARGIN-LEFT: 0in"&gt;&lt;a name="_Toc204499736"&gt;&lt;!----&gt;&lt;/a&gt;&lt;a name="_Ref203363679"&gt;Loading the Driver&lt;/a&gt;&lt;/h2&gt;&lt;p class="Text" style="LINE-HEIGHT: normal"&gt;You can download the SQL Server 2005 Driver for PHP at the &lt;a id="ctl00_rs1_mainContentContainer_ctl09" onclick="javascript:Track('ctl00_rs1_mainContentContainer_ctl00ctl00_rs1_mainContentContainer_ctl09',this);" href="http://go.microsoft.com/fwlink/?LinkId=124503"&gt;Microsoft Download Center&lt;/a&gt;. Included in the download are two .dll files: &lt;strong&gt;php_sqlsrv.dll&lt;/strong&gt; and &lt;strong&gt;php_sqlsrv_ts.dll&lt;/strong&gt;. &lt;/p&gt;&lt;p class="Text" style="LINE-HEIGHT: normal"&gt;These files correspond to the non-thread-safe and thread-safe versions of the driver respectively. &lt;/p&gt;&lt;p class="Text" style="LINE-HEIGHT: normal"&gt;Loading the SQL Server 2005 Driver for PHP is similar to loading any PHP extension:&lt;/p&gt;&lt;p class="Text" style="MARGIN-LEFT: 0.5in; TEXT-INDENT: -0.25in; LINE-HEIGHT: normal"&gt;1.&lt;span style="FONT: 7pt 'Times New Roman'; font-size-adjust: none; font-stretch: normal"&gt;     &lt;/span&gt;Put the extension file (php_sqlsrv.dll or php_sqlsrv_ts.dll) in the PHP extension&lt;br /&gt;directory. If you are running the non-thread-safe version of PHP (php5.dll),&lt;br /&gt;you should use the non-thread-safe version of the driver (php_sqlsrv.dll).&lt;br /&gt;Similarly, if you are running the thread-safe version of PHP (php5ts.dll), you&lt;br /&gt;should use the thread-safe version of the driver (php_sqlsrv_ts.dll).&lt;/p&gt;&lt;p class="Text" style="MARGIN-LEFT: 0.5in; TEXT-INDENT: -0.25in; LINE-HEIGHT: normal"&gt;2.&lt;span style="FONT: 7pt 'Times New Roman'; font-size-adjust: none; font-stretch: normal"&gt;     &lt;/span&gt;Modify the php.ini file to include the extension.&lt;/p&gt;&lt;p class="Text" style="MARGIN-LEFT: 0.5in; LINE-HEIGHT: normal"&gt;Depending on which version of the driver you want to load (non-thread-safe or thread-safe), you will need to add one of the following lines to the Dynamic Extensions section&lt;br /&gt;of your php.ini file:&lt;/p&gt;&lt;p class="Text" style="MARGIN-LEFT: 0.5in; LINE-HEIGHT: normal"&gt;&lt;span style="COLOR: rgb(0,32,96);font-family:'Courier New';" &gt;extension=php_sqlsrv.dll &lt;/span&gt;&lt;/p&gt;&lt;p class="Text" style="MARGIN-LEFT: 0.5in; LINE-HEIGHT: normal"&gt;-or- &lt;/p&gt;&lt;p class="Text" style="MARGIN-LEFT: 0.5in; LINE-HEIGHT: normal"&gt;&lt;span style="COLOR: rgb(0,32,96);font-family:'Courier New';" &gt;extension=php_sqlsrv_ts.dll&lt;/span&gt;&lt;/p&gt;&lt;p class="Text" style="MARGIN-LEFT: 0.5in; LINE-HEIGHT: normal"&gt;&lt;span style="color:windowtext;"&gt;See Figure 1 below for more detail.&lt;/span&gt;&lt;/p&gt;&lt;p class="Text" style="MARGIN-LEFT: 0.5in; TEXT-INDENT: -0.25in; LINE-HEIGHT: normal"&gt;3.&lt;span style="FONT: 7pt 'Times New Roman'; font-size-adjust: none; font-stretch: normal"&gt; &lt;/span&gt;Restart the Web server.&lt;/p&gt;&lt;p class="Text" style="LINE-HEIGHT: normal"&gt;&lt;/p&gt;&lt;p class="Text" style="LINE-HEIGHT: normal"&gt;For more information, see &lt;a id="ctl00_rs1_mainContentContainer_ctl10" onclick="javascript:Track('ctl00_rs1_mainContentContainer_ctl00ctl00_rs1_mainContentContainer_ctl10',this);" href="http://go.microsoft.com/fwlink/?LinkId=124506"&gt;Loading the Driver&lt;/a&gt; in the product documentation.&lt;/p&gt;&lt;h2 style="MARGIN-LEFT: 0in"&gt;&lt;a name="_Toc204499737"&gt;&lt;!----&gt;&lt;/a&gt;&lt;a name="_Ref204490434"&gt;Configuring the Driver&lt;/a&gt;&lt;/h2&gt;&lt;p class="Text" style="LINE-HEIGHT: normal"&gt;The SQL Server 2005 Driver for PHP has three configuration options: &lt;/p&gt;&lt;p class="Text" style="MARGIN-LEFT: 0.5in; TEXT-INDENT: -0.25in; LINE-HEIGHT: normal"&gt;&lt;span style="font-family:Symbol;"&gt;·&lt;span style="FONT: 7pt 'Times New Roman'; font-size-adjust: none; font-stretch: normal"&gt; &lt;/span&gt;&lt;/span&gt;&lt;b&gt;LogSubsystems&lt;/b&gt;&lt;/p&gt;&lt;p class="Text" style="MARGIN-LEFT: 0.5in; LINE-HEIGHT: normal"&gt;Use this option to turn the logging of subsystems on or off. The default setting is &lt;b&gt;SQLSRV_LOG_SYSTEM_OFF &lt;/b&gt;(logging is turned off by default).&lt;/p&gt;&lt;p class="Text" style="MARGIN-LEFT: 0.5in; TEXT-INDENT: -0.25in; LINE-HEIGHT: normal"&gt;&lt;span style="font-family:Symbol;"&gt;·&lt;span style="FONT: 7pt 'Times New Roman'; font-size-adjust: none; font-stretch: normal"&gt; &lt;/span&gt;&lt;/span&gt;&lt;b&gt;LogSeverity&lt;/b&gt;&lt;/p&gt;&lt;p class="Text" style="MARGIN-LEFT: 0.5in; LINE-HEIGHT: normal"&gt;Use this option to specify what to log after logging has been turned on. The default setting is &lt;b&gt;SQLSRV_LOG_SEVERITY_ERROR &lt;/b&gt;(only errors are logged by default after logging has been turned on).&lt;/p&gt;&lt;p class="Text" style="MARGIN-LEFT: 0.5in; TEXT-INDENT: -0.25in; LINE-HEIGHT: normal"&gt;&lt;span style="font-family:Symbol;"&gt;·&lt;span style="FONT: 7pt 'Times New Roman'; font-size-adjust: none; font-stretch: normal"&gt; &lt;/span&gt;&lt;/span&gt;&lt;b&gt;WarningsReturnAsErrors&lt;/b&gt;&lt;/p&gt;&lt;p class="Text" style="MARGIN-LEFT: 0.5in; LINE-HEIGHT: normal"&gt;By default, the SQL Server 2005 Driver for PHP treats warnings generated by &lt;b&gt;sqlsrv&lt;/b&gt; functions as errors. Use the &lt;b&gt;WarningsReturnAsErrors&lt;/b&gt; option to change this&lt;br /&gt;behavior. The default setting for this option is &lt;b&gt;true&lt;/b&gt; (1).&lt;/p&gt;&lt;p class="Text" style="MARGIN-LEFT: 1in; LINE-HEIGHT: normal"&gt;&lt;b&gt;Note&lt;/b&gt; There&lt;br /&gt;are exceptions to this rule. For example, the warning generated by changing the&lt;br /&gt;database context is never treated as an error. &lt;/p&gt;&lt;p class="Text" style="LINE-HEIGHT: normal"&gt;&lt;/p&gt;&lt;p class="Text" style="LINE-HEIGHT: normal"&gt;For more information about these options and settings, see &lt;a id="ctl00_rs1_mainContentContainer_ctl11" onclick="javascript:Track('ctl00_rs1_mainContentContainer_ctl00ctl00_rs1_mainContentContainer_ctl11',this);" href="http://go.microsoft.com/fwlink/?LinkId=124509"&gt;Configuring the Driver&lt;/a&gt; in the product documentation.&lt;/p&gt;&lt;p class="Text" style="LINE-HEIGHT: normal"&gt;&lt;/p&gt;&lt;p class="Text" style="LINE-HEIGHT: normal"&gt;Settings for the configuration options can be set in the php.ini file, or they can be set in a PHP script with the &lt;a id="ctl00_rs1_mainContentContainer_ctl12" onclick="javascript:Track('ctl00_rs1_mainContentContainer_ctl00ctl00_rs1_mainContentContainer_ctl12',this);" href="http://go.microsoft.com/fwlink/?LinkId=124511"&gt;&lt;b&gt;sqlsrv_configure&lt;/b&gt;&lt;/a&gt; function. The figure below shows the Dynamic Extensions section of the php.ini file modified to load the thread-safe version of the driver, log activity on&lt;br /&gt;all subsystems, log all activity (errors, warnings, and notices), and turn off&lt;br /&gt;the &lt;b&gt;WarningsReturnAsErrors&lt;/b&gt; behavior.&lt;/p&gt;&lt;p class="Figure"&gt;&lt;img id="Picture 0" height="412" alt="Figure1.jpg" src="http://i.technet.microsoft.com/Cc793139.sql_access_via_phpFig1%28en-US,SQL.90%29.jpg" width="595" border="0" /&gt;&lt;/p&gt;&lt;p class="Text" style="LINE-HEIGHT: normal"&gt;&lt;b&gt;&lt;span style="color:windowtext;"&gt;Figure&lt;br /&gt;1: The Dynamic Extensions section of the php.ini.&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;&lt;p class="Text" style="LINE-HEIGHT: normal"&gt;&lt;/p&gt;&lt;p class="Text" style="LINE-HEIGHT: normal"&gt;For more information about how to change the default settings, see &lt;a id="ctl00_rs1_mainContentContainer_ctl13" onclick="javascript:Track('ctl00_rs1_mainContentContainer_ctl00ctl00_rs1_mainContentContainer_ctl13',this);" href="http://go.microsoft.com/fwlink/?LinkId=124513"&gt;Logging Activity&lt;/a&gt; and &lt;a id="ctl00_rs1_mainContentContainer_ctl14" onclick="javascript:Track('ctl00_rs1_mainContentContainer_ctl00ctl00_rs1_mainContentContainer_ctl14',this);" href="http://go.microsoft.com/fwlink/?LinkId=124515"&gt;How to: Configure Error and Warning Handling&lt;/a&gt; in the product documentation.&lt;/p&gt;&lt;p class="Text" style="LINE-HEIGHT: normal"&gt;One way to be sure that the driver is loaded and to see the configuration settings is to run a script that calls the &lt;a id="ctl00_rs1_mainContentContainer_ctl15" onclick="javascript:Track('ctl00_rs1_mainContentContainer_ctl00ctl00_rs1_mainContentContainer_ctl15',this);" href="http://go.microsoft.com/fwlink/?LinkId=124532"&gt;&lt;b&gt;phpinfo&lt;/b&gt;()&lt;/a&gt; function. To do this, follow these steps:&lt;/p&gt;&lt;p class="Text" style="MARGIN-LEFT: 0.5in; TEXT-INDENT: -0.25in; LINE-HEIGHT: normal"&gt;1.&lt;span style="FONT: 7pt 'Times New Roman'; font-size-adjust: none; font-stretch: normal"&gt; &lt;/span&gt;Open a text file&lt;br /&gt;and copy the following code into it:&lt;/p&gt;&lt;p class="Text" style="MARGIN-LEFT: 0.5in; LINE-HEIGHT: normal"&gt;&lt;span style="COLOR: rgb(0,32,96);font-family:'Courier New';" &gt;&amp;lt;?php phpinfo(); ?&amp;gt;&lt;/span&gt;&lt;/p&gt;&lt;p class="Text" style="MARGIN-LEFT: 0.5in; TEXT-INDENT: -0.25in; LINE-HEIGHT: normal"&gt;2.&lt;span style="FONT: 7pt 'Times New Roman'; font-size-adjust: none; font-stretch: normal"&gt; &lt;/span&gt;Save the file as&lt;br /&gt;info.php in your Web server’s root directory.&lt;/p&gt;&lt;p class="Text" style="MARGIN-LEFT: 0.5in; TEXT-INDENT: -0.25in; LINE-HEIGHT: normal"&gt;3.&lt;span style="FONT: 7pt 'Times New Roman'; font-size-adjust: none; font-stretch: normal"&gt; &lt;/span&gt;Open a browser&lt;br /&gt;and go to &lt;a id="ctl00_rs1_mainContentContainer_ctl16" onclick="javascript:Track('ctl00_rs1_mainContentContainer_ctl00ctl00_rs1_mainContentContainer_ctl16',this);" href="http://localhost/info.php"&gt;http://localhost/info.php&lt;/a&gt;.&lt;/p&gt;&lt;p class="Text" style="MARGIN-LEFT: 0.5in; TEXT-INDENT: -0.25in; LINE-HEIGHT: normal"&gt;4.&lt;span style="FONT: 7pt 'Times New Roman'; font-size-adjust: none; font-stretch: normal"&gt; &lt;/span&gt;Scroll down the&lt;br /&gt;resulting page to find the &lt;b&gt;sqlsrv&lt;/b&gt; section.&lt;/p&gt;&lt;p class="Text" style="LINE-HEIGHT: normal"&gt;The following figure shows the &lt;b&gt;sqlsrv&lt;/b&gt; section of the &lt;a id="ctl00_rs1_mainContentContainer_ctl17" onclick="javascript:Track('ctl00_rs1_mainContentContainer_ctl00ctl00_rs1_mainContentContainer_ctl17',this);" href="http://go.microsoft.com/fwlink/?LinkId=124532"&gt;&lt;b&gt;phpinfo&lt;/b&gt;()&lt;/a&gt; page. This output confirms the driver is loaded and the configuration settings are set to default values.&lt;/p&gt;&lt;p class="Figure"&gt;&lt;img id="Picture 2" height="193" alt="Figure 2.jpg" src="http://i.technet.microsoft.com/Cc793139.sql_access_via_phpFig2%28en-US,SQL.90%29.jpg" width="595" border="0" /&gt;&lt;/p&gt;&lt;p class="Text" style="LINE-HEIGHT: normal"&gt;&lt;b&gt;Figure 2: The sqlsrv section of the &lt;/b&gt;&lt;a id="ctl00_rs1_mainContentContainer_ctl18" onclick="javascript:Track('ctl00_rs1_mainContentContainer_ctl00ctl00_rs1_mainContentContainer_ctl18',this);" href="http://go.microsoft.com/fwlink/?LinkId=124532"&gt;phpinfo()&lt;/a&gt;&lt;b&gt; page. &lt;a name="_Ref203364266"&gt;&lt;!----&gt;&lt;/a&gt;&lt;a name="_Toc144986100"&gt;&lt;!----&gt;&lt;/a&gt;&lt;a name="_Creating_a_Connection"&gt;&lt;!----&gt;&lt;/a&gt;&lt;/b&gt;&lt;/p&gt;&lt;p class="Text" style="LINE-HEIGHT: normal"&gt;&lt;b&gt;&lt;/b&gt;&lt;/p&gt;&lt;h2 style="MARGIN-LEFT: 0in"&gt;&lt;a name="_Toc204499738"&gt;&lt;!----&gt;&lt;/a&gt;&lt;a name="_Ref203530695"&gt;Creating a Connection&lt;/a&gt;&lt;/h2&gt;&lt;p class="Text" style="LINE-HEIGHT: normal"&gt;The &lt;a id="ctl00_rs1_mainContentContainer_ctl19" onclick="javascript:Track('ctl00_rs1_mainContentContainer_ctl00ctl00_rs1_mainContentContainer_ctl19',this);" href="http://msdn.microsoft.com/en-us/library/cc296161.aspx"&gt;&lt;b&gt;sqlsrv_connect&lt;/b&gt;&lt;/a&gt; function is used to establish a connection to the server. The code shown here (from the &lt;a id="ctl00_rs1_mainContentContainer_ctl20" onclick="javascript:Track('ctl00_rs1_mainContentContainer_ctl00ctl00_rs1_mainContentContainer_ctl20',this);" href="http://go.microsoft.com/fwlink/?LinkId=124498"&gt;Example Application&lt;/a&gt; in the product documentation) establishes a connection to the local server and specifies the &lt;b&gt;AdventureWorks&lt;/b&gt; database as the database&lt;br /&gt;in use:&lt;/p&gt;&lt;p class="Text" style="LINE-HEIGHT: normal"&gt;&lt;span style="COLOR: rgb(0,32,96);font-family:'Courier New';" &gt;$serverName = "(local)";&lt;/span&gt;&lt;/p&gt;&lt;p class="Text" style="MARGIN-LEFT: 0.5in; LINE-HEIGHT: normal"&gt;&lt;span style="COLOR: rgb(0,32,96);font-family:'Courier New';" &gt;$connectionOptions =&lt;br /&gt;array("Database"=&amp;gt;"AdventureWorks");&lt;/span&gt;&lt;/p&gt;&lt;p class="Text" style="MARGIN-LEFT: 0.5in; LINE-HEIGHT: normal"&gt;&lt;span style="COLOR: rgb(0,32,96);font-family:'Courier New';" &gt;/* Connect using Windows&lt;br /&gt;Authentication. */&lt;/span&gt;&lt;/p&gt;&lt;p class="Text" style="MARGIN-LEFT: 0.5in; LINE-HEIGHT: normal"&gt;&lt;span style="COLOR: rgb(0,32,96);font-family:'Courier New';" &gt;$conn = sqlsrv_connect($serverName, $connectionOptions);&lt;br /&gt;&lt;/span&gt;&lt;span style="COLOR: rgb(0,32,96);font-family:'Courier New';" &gt;if( $conn === false )&lt;br /&gt;&lt;/span&gt;&lt;span style="COLOR: rgb(0,32,96);font-family:'Courier New';" &gt;{ die( FormatErrors( sqlsrv_errors() ) ); }&lt;/span&gt;&lt;/p&gt;&lt;p class="Text" style="LINE-HEIGHT: normal"&gt;&lt;/p&gt;&lt;p class="Text" style="LINE-HEIGHT: normal"&gt;By default, the &lt;b&gt;sqlsrv_connect&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;function uses Windows Authentication to establish a connection. In most&lt;br /&gt;scenarios, this means that the Web server's process identity or thread identity&lt;br /&gt;(if the Web server is using impersonation) is used to connect to the server,&lt;br /&gt;not an end-user's identity.&lt;/p&gt;&lt;p class="Text" style="LINE-HEIGHT: normal"&gt;The &lt;b&gt;sqlsrv_connect&lt;/b&gt; function&lt;br /&gt;accepts two parameters: &lt;i&gt;$serverName&lt;/i&gt; and &lt;i&gt;$connectionOptions&lt;/i&gt;&lt;br /&gt;(optional). &lt;/p&gt;&lt;p class="Text" style="MARGIN-LEFT: 0.5in; TEXT-INDENT: -0.25in; LINE-HEIGHT: normal"&gt;&lt;span style="font-family:Symbol;"&gt;·&lt;span style="FONT: 7pt 'Times New Roman'; font-size-adjust: none; font-stretch: normal"&gt; &lt;/span&gt;&lt;/span&gt;&lt;i&gt;&lt;span style="TEXT-DECORATION: underline"&gt;$serverName&lt;/span&gt;&lt;/i&gt; – This required parameter is used to&lt;br /&gt;specify the name of the server to which you want to connect. In the code above,&lt;br /&gt;a connection is established to the local server. This parameter can also be use&lt;br /&gt;to specify a SQL Server instance or a port number. For example:&lt;/p&gt;&lt;p class="Text" style="MARGIN-LEFT: 1.5in; LINE-HEIGHT: normal"&gt;&lt;span style="COLOR: rgb(0,32,96);font-family:'Courier New';" &gt;$serverName = "myServer\instanceName";&lt;/span&gt;&lt;/p&gt;&lt;p class="Text" style="MARGIN-LEFT: 1.5in; LINE-HEIGHT: normal"&gt;-or-&lt;/p&gt;&lt;p class="Text" style="MARGIN-LEFT: 1.5in; LINE-HEIGHT: normal"&gt;&lt;span style="COLOR: rgb(0,32,96);font-family:'Courier New';" &gt;$serverName = "myServer,&lt;br /&gt;1521";&lt;/span&gt;&lt;/p&gt;&lt;p class="Text" style="MARGIN-LEFT: 0.5in; TEXT-INDENT: -0.25in; LINE-HEIGHT: normal"&gt;&lt;span style="font-family:Symbol;"&gt;·&lt;span style="FONT: 7pt 'Times New Roman'; font-size-adjust: none; font-stretch: normal"&gt; &lt;/span&gt;&lt;/span&gt;&lt;i&gt;&lt;span style="TEXT-DECORATION: underline"&gt;$connectionOptions&lt;/span&gt; - This&lt;/i&gt; optional parameter is an&lt;br /&gt;array of key-value pairs that set options on the connection. In the code above,&lt;br /&gt;the database is set to &lt;b&gt;AdventureWorks&lt;/b&gt; for the connection. Other options&lt;br /&gt;include &lt;i&gt;ConnectionPooling&lt;/i&gt;, &lt;i&gt;Encrypt&lt;/i&gt;, &lt;i&gt;UID&lt;/i&gt;, and &lt;i&gt;PWD&lt;/i&gt;.&lt;br /&gt;For more information, see &lt;a id="ctl00_rs1_mainContentContainer_ctl21" onclick="javascript:Track('ctl00_rs1_mainContentContainer_ctl00ctl00_rs1_mainContentContainer_ctl21',this);" href="http://go.microsoft.com/fwlink/?LinkId=124520"&gt;&lt;b&gt;sqlsrv_connect&lt;/b&gt;&lt;/a&gt;&lt;b&gt; &lt;/b&gt;in the product documentation.&lt;/p&gt;&lt;p class="Text" style="MARGIN-LEFT: 1in; LINE-HEIGHT: normal"&gt;&lt;b&gt;Note&lt;/b&gt; The &lt;i&gt;UID&lt;/i&gt; and &lt;i&gt;PWD&lt;/i&gt; options must be set in the &lt;i&gt;$connectionOptions&lt;/i&gt;&lt;br /&gt;parameter to log into the server with SQL Server Authentication.&lt;/p&gt;&lt;p class="Text" style="MARGIN: 0in 0in 0pt; LINE-HEIGHT: normal"&gt;For more information about creating a connection, see &lt;a id="ctl00_rs1_mainContentContainer_ctl22" onclick="javascript:Track('ctl00_rs1_mainContentContainer_ctl00ctl00_rs1_mainContentContainer_ctl22',this);" href="http://go.microsoft.com/fwlink/?LinkId=124521"&gt;Connecting to the Server&lt;/a&gt;&lt;br /&gt;in the product documentation.&lt;/p&gt;&lt;p class="Text" style="MARGIN: 0in 0in 0pt; LINE-HEIGHT: normal"&gt;&lt;/p&gt;&lt;p class="Text" style="MARGIN: 0in 0in 0pt 0.5in; LINE-HEIGHT: normal"&gt;&lt;b&gt;Note&lt;/b&gt; The &lt;b&gt;FormatErrors&lt;/b&gt; function that is shown in the example is a custom&lt;br /&gt;function for formatting error output. It is described in the Handling Errors&lt;br /&gt;and Warnings section later in this paper.&lt;/p&gt;&lt;h3 style="MARGIN-LEFT: 0in"&gt;&lt;a name="_Toc204499739"&gt;&lt;!----&gt;&lt;/a&gt;&lt;a name="_Ref204492870"&gt;Connection Pooling&lt;/a&gt;&lt;/h3&gt;&lt;p class="MsoNormal" style="MARGIN-BOTTOM: 0pt; LINE-HEIGHT: normal"&gt;&lt;span style="font-family:'Verdana','sans-serif';font-size:10;color:black;"&gt;&lt;span style="font-size:130%;"&gt;The SQL Server 2005&lt;/span&gt; &lt;span style="font-size:100%;"&gt;Driver for PHP is designed to use ODBC&lt;br /&gt;connection pooling. By default, connection pooling is enabled. When you connect&lt;br /&gt;to a server, the driver attempts to use a pooled connection before it creates a&lt;br /&gt;new one. If an equivalent connection is not found in the pool, a new connection&lt;br /&gt;is created and added to the pool. The driver determines whether connections are&lt;br /&gt;equivalent based on a comparison of connection strings. Calling &lt;/span&gt;&lt;/span&gt;&lt;a id="ctl00_rs1_mainContentContainer_ctl23" onclick="javascript:Track('ctl00_rs1_mainContentContainer_ctl00ctl00_rs1_mainContentContainer_ctl23',this);" href="http://go.microsoft.com/fwlink/?LinkId=124533"&gt;&lt;b&gt;&lt;span style="font-family:'Verdana','sans-serif';"&gt;sqlsrv_close&lt;/span&gt;&lt;/b&gt;&lt;/a&gt;&lt;span style="font-family:'Verdana','sans-serif';color:black;"&gt; on a&lt;br /&gt;connection returns the connection to the pool. However, if the connection was&lt;br /&gt;created with the &lt;i&gt;ConnectionPooling&lt;/i&gt; attribute set to false (see the &lt;/span&gt;Creating&lt;br /&gt;a Connection section&lt;span style="font-family:'Verdana','sans-serif';color:black;"&gt;), calling &lt;b&gt;sqlsrv_close&lt;/b&gt; closes the connection.&lt;/span&gt;&lt;/p&gt;&lt;p class="MsoNormal" style="MARGIN-BOTTOM: 0pt; LINE-HEIGHT: normal"&gt;&lt;span style="font-family:'Verdana','sans-serif';color:black;"&gt;&lt;/span&gt;&lt;/p&gt;&lt;p class="MsoNormal" style="MARGIN: 0in 0in 0pt 0.5in; LINE-HEIGHT: normal"&gt;&lt;b&gt;&lt;i&gt;&lt;span style="font-family:'Verdana','sans-serif';color:black;"&gt;Note&lt;/span&gt;&lt;/i&gt;&lt;/b&gt;&lt;i&gt;&lt;span style="font-family:'Verdana','sans-serif';color:black;"&gt; &lt;/span&gt;&lt;/i&gt;&lt;span style="font-family:'Verdana','sans-serif';color:black;"&gt;The&lt;br /&gt;first time you execute a query on a connection that was retrieved from a pool,&lt;br /&gt;the driver tells the server to reset the connection prior to executing the&lt;br /&gt;query. Resetting the connection returns the connection to its original state.&lt;br /&gt;For example, resetting the connection deletes any temporary objects and rolls&lt;br /&gt;back any pending transactions.&lt;/span&gt;&lt;/p&gt;&lt;p class="MsoNormal" style="MARGIN-BOTTOM: 0pt; LINE-HEIGHT: normal"&gt;&lt;span style="font-family:'Verdana','sans-serif';color:black;"&gt;&lt;/span&gt;&lt;/p&gt;&lt;p style="MARGIN: 0in 0in 0pt; LINE-HEIGHT: normal"&gt;&lt;span style="FONT-WEIGHT: normal;font-family:'Verdana','sans-serif';color:black;"  &gt;For more information, see &lt;/span&gt;&lt;a id="ctl00_rs1_mainContentContainer_ctl24" onclick="javascript:Track('ctl00_rs1_mainContentContainer_ctl00ctl00_rs1_mainContentContainer_ctl24',this);" href="http://go.microsoft.com/fwlink/?LinkId=124522"&gt;&lt;span style="FONT-WEIGHT: normal;font-family:'Verdana','sans-serif';" &gt;Connection&lt;br /&gt;Pooling&lt;/span&gt;&lt;/a&gt;&lt;span style="FONT-WEIGHT: normal;font-family:'Verdana','sans-serif';color:black;"  &gt; in the product documentation.&lt;/span&gt;&lt;/p&gt;&lt;h2 style="MARGIN-LEFT: 0in"&gt;&lt;a name="_Ref203530563"&gt;&lt;!----&gt;&lt;/a&gt;&lt;a name="_Toc204499740"&gt;&lt;!----&gt;&lt;/a&gt;&lt;a name="_Ref203551805"&gt;&lt;span style="font-size:100%;"&gt;Executing a Query&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p class="Text" style="LINE-HEIGHT: normal"&gt;The SQL Server 2005 Driver for PHP&lt;br /&gt;provides two options for executing queries: the &lt;a id="ctl00_rs1_mainContentContainer_ctl25" onclick="javascript:Track('ctl00_rs1_mainContentContainer_ctl00ctl00_rs1_mainContentContainer_ctl25',this);" href="http://go.microsoft.com/fwlink/?LinkId=124534"&gt;&lt;b&gt;sqlsrv_query&lt;/b&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;function, or the combination of the &lt;a id="ctl00_rs1_mainContentContainer_ctl26" onclick="javascript:Track('ctl00_rs1_mainContentContainer_ctl00ctl00_rs1_mainContentContainer_ctl26',this);" href="http://go.microsoft.com/fwlink/?LinkId=124535"&gt;&lt;b&gt;sqlsrv_prepare&lt;/b&gt;&lt;/a&gt;&lt;br /&gt;and &lt;a id="ctl00_rs1_mainContentContainer_ctl27" onclick="javascript:Track('ctl00_rs1_mainContentContainer_ctl00ctl00_rs1_mainContentContainer_ctl27',this);" href="http://go.microsoft.com/fwlink/?LinkId=124536"&gt;&lt;b&gt;sqlsrv_execute&lt;/b&gt;&lt;/a&gt;&lt;br /&gt;functions. &lt;/p&gt;&lt;p class="Text" style="LINE-HEIGHT: normal"&gt;The &lt;b&gt;sqlsrv_query&lt;/b&gt; function does&lt;br /&gt;both statement preparation and execution with one function call and is best&lt;br /&gt;suited for executing one-time queries. An alternate method for executing&lt;br /&gt;queries (a method well-suited for executing a query multiple times with&lt;br /&gt;different parameter values) is the combination &lt;b&gt;sqlsrv_prepare&lt;/b&gt; and &lt;b&gt;sqlsrv_execute&lt;/b&gt;.&lt;br /&gt;This option breaks statement preparation and execution into two function calls.&lt;br /&gt;For more information, see &lt;a id="ctl00_rs1_mainContentContainer_ctl28" onclick="javascript:Track('ctl00_rs1_mainContentContainer_ctl00ctl00_rs1_mainContentContainer_ctl28',this);" href="http://msdn.microsoft.com/en-us/library/cc296154.aspx"&gt;Comparing&lt;br /&gt;Execution Functions&lt;/a&gt; in the product documentation.&lt;/p&gt;&lt;p class="Text" style="LINE-HEIGHT: normal"&gt;The general programming pattern for&lt;br /&gt;either option requires doing the following before calling &lt;b&gt;sqlsrv_query&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;or &lt;b&gt;sqlsrv_prepare&lt;/b&gt;/&lt;b&gt;sqlsrv_execute&lt;/b&gt;:&lt;/p&gt;&lt;p class="Text" style="MARGIN-LEFT: 0.5in; TEXT-INDENT: -0.25in; LINE-HEIGHT: normal"&gt;&lt;span style="font-family:Symbol;"&gt;·&lt;span style="FONT: 7pt 'Times New Roman'; font-size-adjust: none; font-stretch: normalfont-size:100%;" &gt;&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;Establishing a connection to the server (see the Creating a&lt;br /&gt;Connection section)&lt;/p&gt;&lt;p class="Text" style="MARGIN-LEFT: 0.5in; TEXT-INDENT: -0.25in; LINE-HEIGHT: normal"&gt;&lt;span style="font-family:Symbol;"&gt;·&lt;span style="FONT: 7pt 'Times New Roman'; font-size-adjust: none; font-stretch: normalfont-size:100%;" &gt;&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;Defining a Transact-SQL statement&lt;/p&gt;&lt;p class="Text" style="MARGIN-LEFT: 0.5in; TEXT-INDENT: -0.25in; LINE-HEIGHT: normal"&gt;&lt;span style="font-family:Symbol;"&gt;·&lt;span style="FONT: 7pt 'Times New Roman'; font-size-adjust: none; font-stretch: normalfont-size:100%;" &gt;&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;Providing an array of parameter values (only required for&lt;br /&gt;parameterized queries)&lt;/p&gt;&lt;p class="Text" style="MARGIN-LEFT: 0.5in; TEXT-INDENT: -0.25in; LINE-HEIGHT: normal"&gt;&lt;span style="font-family:Symbol;"&gt;·&lt;span style="FONT: 7pt 'Times New Roman'; font-size-adjust: none; font-stretch: normalfont-size:100%;" &gt; &lt;/span&gt;&lt;/span&gt;Setting options on the connection (optional)&lt;/p&gt;&lt;p class="Text" style="LINE-HEIGHT: normal"&gt;&lt;/p&gt;&lt;p class="Text" style="LINE-HEIGHT: normal"&gt;The following code (from the &lt;a id="ctl00_rs1_mainContentContainer_ctl29" onclick="javascript:Track('ctl00_rs1_mainContentContainer_ctl00ctl00_rs1_mainContentContainer_ctl29',this);" href="http://go.microsoft.com/fwlink/?LinkId=124498"&gt;Example Application&lt;/a&gt; in the product documentation) demonstrates the use of the &lt;b&gt;sqlsrv_query&lt;/b&gt; function:&lt;/p&gt;&lt;p class="Text" style="LINE-HEIGHT: normal"&gt;&lt;/p&gt;&lt;p class="Text" style="TEXT-INDENT: 0.5in; LINE-HEIGHT: normal"&gt;&lt;span style="COLOR: rgb(0,32,96);font-family:'Courier New';" &gt;$tsql = "SELECT ProductID, Name, Color, Size, ListPrice &lt;/span&gt;&lt;span style="COLOR: rgb(0,32,96);font-family:'Courier New';" &gt;FROM Production.Product &lt;/span&gt;&lt;span style="COLOR: rgb(0,32,96);font-family:'Courier New';" &gt;WHERE&lt;br /&gt;Name LIKE '%' + ? + '%' AND ListPrice &amp;gt; 0.0";&lt;br /&gt;&lt;/span&gt;&lt;span style="COLOR: rgb(0,32,96);font-family:'Courier New';" &gt;$params = array( $_REQUEST['query'] );&lt;br /&gt;&lt;/span&gt;&lt;span style="COLOR: rgb(0,32,96);font-family:'Courier New';" &gt;$getProducts = sqlsrv_query(&lt;br /&gt;$conn, $tsql, $params);&lt;/span&gt;&lt;/p&gt;&lt;p class="Text" style="LINE-HEIGHT: normal"&gt;&lt;span style="COLOR: rgb(0,32,96);font-family:'Courier New';" &gt;if ( $getProducts === false)&lt;/span&gt;&lt;span style="COLOR: rgb(0,32,96);font-family:'Courier New';" &gt;{ die( FormatErrors( sqlsrv_errors() ) ); }&lt;/span&gt;&lt;/p&gt;&lt;p class="Text" style="LINE-HEIGHT: normal"&gt;&lt;/p&gt;&lt;p class="Text" style="LINE-HEIGHT: normal"&gt;The &lt;b&gt;sqlsrv_query&lt;/b&gt; and &lt;b&gt;sqlsrv_prepare&lt;/b&gt; functions each accept four parameters: &lt;i&gt;$conn&lt;/i&gt;, &lt;i&gt;$tsql&lt;/i&gt;, &lt;i&gt;$params&lt;/i&gt; (optional), and &lt;i&gt;$options&lt;/i&gt; (optional, not shown).&lt;/p&gt;&lt;p class="Text" style="MARGIN-LEFT: 0.5in; TEXT-INDENT: -0.25in; LINE-HEIGHT: normal"&gt;&lt;span style="font-family:Symbol;"&gt;·&lt;span style="FONT: 7pt 'Times New Roman'; font-size-adjust: none; font-stretch: normalfont-size:100%;" &gt; &lt;/span&gt;&lt;/span&gt;&lt;i&gt;&lt;span style="TEXT-DECORATION: underline"&gt;$conn&lt;/span&gt;&lt;/i&gt; – This required parameter is a PHP connection&lt;br /&gt;resource created with the &lt;b&gt;sqlsrv_connect&lt;/b&gt; function (see the Creating a&lt;br /&gt;Connection section).&lt;/p&gt;&lt;p class="Text" style="MARGIN-LEFT: 0.5in; TEXT-INDENT: -0.25in; LINE-HEIGHT: normal"&gt;&lt;span style="font-family:Symbol;"&gt;·&lt;span style="FONT: 7pt 'Times New Roman'; font-size-adjust: none; font-stretch: normalfont-size:100%;" &gt; &lt;/span&gt;&lt;/span&gt;&lt;i&gt;&lt;span style="TEXT-DECORATION: underline"&gt;$tsql&lt;/span&gt;&lt;/i&gt; – This required parameter is a string that&lt;br /&gt;defines a Transact-SQL query. Question marks (?) are used as placeholders for&lt;br /&gt;parameters.&lt;/p&gt;&lt;p class="Text" style="MARGIN-LEFT: 0.5in; TEXT-INDENT: -0.25in; LINE-HEIGHT: normal"&gt;&lt;span style="font-family:Symbol;"&gt;·&lt;span style="FONT: 7pt 'Times New Roman'; font-size-adjust: none; font-stretch: normalfont-size:100%;" &gt; &lt;/span&gt;&lt;/span&gt;&lt;i&gt;&lt;span style="TEXT-DECORATION: underline"&gt;$params&lt;/span&gt;&lt;/i&gt; – This optional parameter is an array of values that correspond (in order) to the parameter placeholders (question marks) in the query defined by the &lt;i&gt;$tsql&lt;/i&gt; parameter. Each value in the &lt;i&gt;$params&lt;/i&gt; array can be a literal value (such as 5), a PHP variable (such as $myVar), or an array with the following structure:&lt;/p&gt;&lt;p class="Text" style="MARGIN-LEFT: 0.5in; TEXT-INDENT: 0.5in; LINE-HEIGHT: normal"&gt;&lt;span style="COLOR: rgb(0,32,96);font-family:'Courier New';" &gt;array($value [, $direction [, $phpType [, $sqlType]]])&lt;/span&gt;&lt;/p&gt;&lt;p class="Text" style="MARGIN-LEFT: 0.5in; LINE-HEIGHT: normal"&gt;&lt;span style="color:windowtext;"&gt;This array is used to specify the parameter value, the&lt;br /&gt;parameter direction (in the case where the parameter is being passed to a&lt;br /&gt;stored procedure), the PHP type of the parameter, and the SQL Server type of a&lt;br /&gt;value sent to the server. For more information about this array, see the &lt;/span&gt;Sending Images to the Server&lt;span style="color:windowtext;"&gt; section. For more&lt;br /&gt;information, see &lt;/span&gt;&lt;a id="ctl00_rs1_mainContentContainer_ctl30" onclick="javascript:Track('ctl00_rs1_mainContentContainer_ctl00ctl00_rs1_mainContentContainer_ctl30',this);" href="http://go.microsoft.com/fwlink/?LinkId=124524"&gt;Using&lt;br /&gt;Directional Parameters&lt;/a&gt;&lt;span style="color:windowtext;"&gt;, &lt;/span&gt;&lt;a id="ctl00_rs1_mainContentContainer_ctl31" onclick="javascript:Track('ctl00_rs1_mainContentContainer_ctl00ctl00_rs1_mainContentContainer_ctl31',this);" href="http://go.microsoft.com/fwlink/?LinkId=124526"&gt;How to: Send Data as a&lt;br /&gt;Stream&lt;/a&gt;&lt;span style="color:windowtext;"&gt;, and &lt;/span&gt;&lt;a id="ctl00_rs1_mainContentContainer_ctl32" onclick="javascript:Track('ctl00_rs1_mainContentContainer_ctl00ctl00_rs1_mainContentContainer_ctl32',this);" href="http://go.microsoft.com/fwlink/?LinkId=124527"&gt;How to: Specify SQL Server&lt;br /&gt;Data Types&lt;/a&gt;&lt;span style="color:windowtext;"&gt; in the product documentation.&lt;/span&gt;&lt;/p&gt;&lt;p class="Text" style="MARGIN-LEFT: 0.5in; TEXT-INDENT: -0.25in; LINE-HEIGHT: normal"&gt;&lt;span style="font-family:Symbol;color:windowtext;"&gt;·&lt;span style="FONT: 7pt 'Times New Roman'; font-size-adjust: none; font-stretch: normalfont-size:100%;" &gt; &lt;/span&gt;&lt;/span&gt;&lt;i&gt;&lt;span style="TEXT-DECORATION: underline"&gt;$options&lt;/span&gt;&lt;/i&gt; – This optional parameter (not shown in&lt;br /&gt;the example) is an associative array that sets properties on the query. Two&lt;br /&gt;keys are supported: &lt;i&gt;QueryTimeout&lt;/i&gt; and &lt;i&gt;SendStreamParamsAtExec&lt;/i&gt;. The &lt;i&gt;QueryTimeout&lt;/i&gt;&lt;br /&gt;key sets the maximum time in seconds that a query is allowed to run. The &lt;i&gt;SendStreamParamsAtExec&lt;/i&gt;&lt;br /&gt;key determines if all stream data is sent at the time of query execution or if&lt;br /&gt;subsequent calls to &lt;a id="ctl00_rs1_mainContentContainer_ctl33" onclick="javascript:Track('ctl00_rs1_mainContentContainer_ctl00ctl00_rs1_mainContentContainer_ctl33',this);" href="http://go.microsoft.com/fwlink/?LinkId=124537"&gt;&lt;b&gt;sqlsrv_send_stream_data&lt;/b&gt;&lt;/a&gt;&lt;br /&gt;are necessary to send all stream data. For more information, see &lt;a id="ctl00_rs1_mainContentContainer_ctl34" onclick="javascript:Track('ctl00_rs1_mainContentContainer_ctl00ctl00_rs1_mainContentContainer_ctl34',this);" href="http://go.microsoft.com/fwlink/?LinkId=124526"&gt;How to: Send Data as a&lt;br /&gt;Stream&lt;/a&gt;.&lt;/p&gt;&lt;h2 style="MARGIN-LEFT: 0in"&gt;&lt;a name="_Toc204499741"&gt;&lt;span style="font-size:100%;"&gt;Retrieving Data from the&lt;br /&gt;Server&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p class="Text" style="LINE-HEIGHT: normal"&gt;The general pattern for retrieving data with the SQL Server 2005 Driver for PHP involves defining and executing a query (see the Executing a Query section) and then using one of the following three options to retrieve data from the result set.&lt;/p&gt;&lt;p class="Text" style="MARGIN-LEFT: 0.5in; TEXT-INDENT: -0.25in; LINE-HEIGHT: normal"&gt;&lt;span style="font-family:Symbol;"&gt;·&lt;span style="FONT: 7pt 'Times New Roman'; font-size-adjust: none; font-stretch: normalfont-size:100%;" &gt; &lt;/span&gt;&lt;/span&gt;The &lt;a id="ctl00_rs1_mainContentContainer_ctl35" onclick="javascript:Track('ctl00_rs1_mainContentContainer_ctl00ctl00_rs1_mainContentContainer_ctl35',this);" href="http://go.microsoft.com/fwlink/?LinkId=124539"&gt;&lt;b&gt;sqlsrv_fetch_array&lt;/b&gt;&lt;/a&gt;&lt;br /&gt;function (retrieves a row of data as an array).&lt;/p&gt;&lt;p class="Text" style="MARGIN-LEFT: 0.5in; TEXT-INDENT: -0.25in; LINE-HEIGHT: normal"&gt;&lt;span style="font-family:Symbol;"&gt;·&lt;span style="FONT: 7pt 'Times New Roman'; font-size-adjust: none; font-stretch: normalfont-size:100%;" &gt; &lt;/span&gt;&lt;/span&gt;The &lt;a id="ctl00_rs1_mainContentContainer_ctl36" onclick="javascript:Track('ctl00_rs1_mainContentContainer_ctl00ctl00_rs1_mainContentContainer_ctl36',this);" href="http://go.microsoft.com/fwlink/?LinkId=124540"&gt;&lt;b&gt;sqlsrv_fetch_object&lt;/b&gt;&lt;/a&gt;&lt;br /&gt;function (retrieves a row of data as a PHP object).&lt;/p&gt;&lt;p class="Text" style="MARGIN-LEFT: 0.5in; TEXT-INDENT: -0.25in; LINE-HEIGHT: normal"&gt;&lt;span style="font-family:Symbol;"&gt;·&lt;span style="FONT: 7pt 'Times New Roman'; font-size-adjust: none; font-stretch: normalfont-size:100%;" &gt; &lt;/span&gt;&lt;/span&gt;The combination of the &lt;a id="ctl00_rs1_mainContentContainer_ctl37" onclick="javascript:Track('ctl00_rs1_mainContentContainer_ctl00ctl00_rs1_mainContentContainer_ctl37',this);" href="http://go.microsoft.com/fwlink/?LinkId=124541"&gt;&lt;b&gt;sqlsrv_fetch&lt;/b&gt;&lt;/a&gt; and &lt;a id="ctl00_rs1_mainContentContainer_ctl38" onclick="javascript:Track('ctl00_rs1_mainContentContainer_ctl00ctl00_rs1_mainContentContainer_ctl38',this);" href="http://go.microsoft.com/fwlink/?LinkId=124542"&gt;&lt;b&gt;sqlsrv_get_field&lt;/b&gt;&lt;/a&gt; functions (retrieves a single field from a row of data).&lt;/p&gt;&lt;p class="MsoListParagraph" style="MARGIN-BOTTOM: 0pt; LINE-HEIGHT: normal"&gt;&lt;b&gt;&lt;i&gt;&lt;span style="font-family:'Verdana','sans-serif';color:black;"&gt;Note&lt;/span&gt;&lt;/i&gt;&lt;/b&gt;&lt;i&gt;&lt;span style="font-family:'Verdana','sans-serif';color:black;"&gt; &lt;/span&gt;&lt;/i&gt;&lt;span style="font-family:'Verdana','sans-serif';color:black;"&gt;These functions provide forward-only&lt;br /&gt;access to the rows of a result set.&lt;/span&gt;&lt;/p&gt;&lt;p class="Text" style="MARGIN-LEFT: 0.5in; LINE-HEIGHT: normal"&gt;&lt;/p&gt;&lt;p class="Text" style="LINE-HEIGHT: normal"&gt;When choosing which option to use, consider the following:&lt;/p&gt;&lt;p class="Text" style="MARGIN-LEFT: 0.5in; TEXT-INDENT: -0.25in; LINE-HEIGHT: normal"&gt;&lt;span style="font-family:Symbol;"&gt;·&lt;span style="FONT: 7pt 'Times New Roman'; font-size-adjust: none; font-stretch: normalfont-size:100%;" &gt; &lt;/span&gt;&lt;/span&gt;The &lt;b&gt;sqlsrv_fetch_array&lt;/b&gt; and &lt;b&gt;sqlsrv_fetch_object&lt;/b&gt;&lt;br /&gt;functions pull an entire row of data into script memory. This may not be desirable&lt;br /&gt;for rows that contain large amounts of data.&lt;/p&gt;&lt;p style="MARGIN: 0in 0in 0pt 0.5in; TEXT-INDENT: -0.25in; LINE-HEIGHT: normal"&gt;&lt;span style="FONT-WEIGHT: normal;font-family:Symbol;color:black;"  &gt;·&lt;span style="FONT: 7pt 'Times New Roman'; font-size-adjust: none; font-stretch: normalfont-size:100%;" &gt; &lt;/span&gt;&lt;/span&gt;&lt;span style="FONT-WEIGHT: normal;font-family:'Verdana','sans-serif';color:black;"  &gt;Data returned by the &lt;/span&gt;&lt;span style="font-family:'Verdana','sans-serif';color:black;"&gt;sqlsrv_fetch_array&lt;/span&gt;&lt;span style="FONT-WEIGHT: normal;font-family:'Verdana','sans-serif';color:black;"  &gt; and &lt;/span&gt;&lt;span style="font-family:'Verdana','sans-serif';color:black;"&gt;sqlsrv_fetch_object&lt;/span&gt;&lt;span style="FONT-WEIGHT: normal;font-family:'Verdana','sans-serif';color:black;"  &gt; functions&lt;/span&gt;&lt;span style="font-family:'Verdana','sans-serif';color:black;"&gt; &lt;/span&gt;&lt;span style="FONT-WEIGHT: normal;font-family:'Verdana','sans-serif';color:black;"  &gt;will be typed according to the defaults PHP data types&lt;br /&gt;assigned by the driver. For more information, see &lt;/span&gt;&lt;a id="ctl00_rs1_mainContentContainer_ctl39" onclick="javascript:Track('ctl00_rs1_mainContentContainer_ctl00ctl00_rs1_mainContentContainer_ctl39',this);" href="http://go.microsoft.com/fwlink/?LinkId=124529"&gt;&lt;span style="FONT-WEIGHT: normal;font-family:'Verdana','sans-serif';" &gt;Default PHP Data&lt;br /&gt;Types&lt;/span&gt;&lt;/a&gt;&lt;span style="FONT-WEIGHT: normal;font-family:'Verdana','sans-serif';color:black;"  &gt; in the product documentation. &lt;/span&gt;&lt;/p&gt;&lt;p class="Text" style="MARGIN-LEFT: 0.5in; TEXT-INDENT: -0.25in; LINE-HEIGHT: normal"&gt;&lt;span style="font-family:Symbol;"&gt;·&lt;span style="FONT: 7pt 'Times New Roman'; font-size-adjust: none; font-stretch: normalfont-size:100%;" &gt; &lt;/span&gt;&lt;/span&gt;Using the combination of &lt;b&gt;sqlsrv_fetch&lt;/b&gt; and &lt;b&gt;sqlsrv_get_field&lt;/b&gt;&lt;br /&gt;allows you to specify the PHP data type of the returned data, including&lt;br /&gt;specification of the data as a stream.&lt;/p&gt;&lt;p class="Text" style="LINE-HEIGHT: normal"&gt;For more information about data retrieval functionality, see &lt;a id="ctl00_rs1_mainContentContainer_ctl40" onclick="javascript:Track('ctl00_rs1_mainContentContainer_ctl00ctl00_rs1_mainContentContainer_ctl40',this);" href="http://go.microsoft.com/fwlink/?LinkId=124518"&gt;Comparing Data Retrieval Functions&lt;/a&gt; in the product documentation.&lt;/p&gt;&lt;p class="Text" style="LINE-HEIGHT: normal"&gt;For more information about retrieving data with the SQL Server 2005 Driver for PHP see &lt;a id="ctl00_rs1_mainContentContainer_ctl41" onclick="javascript:Track('ctl00_rs1_mainContentContainer_ctl00ctl00_rs1_mainContentContainer_ctl41',this);" href="http://go.microsoft.com/fwlink/?LinkId=124516"&gt;Retrieving Data&lt;/a&gt; in the product documentation.&lt;/p&gt;&lt;h3 style="MARGIN-LEFT: 0in"&gt;&lt;a name="_Toc204499742"&gt;&lt;span style="font-size:100%;"&gt;Retrieving Data as an Array&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p class="Text" style="LINE-HEIGHT: normal"&gt;In this section, we examine code in the &lt;a id="ctl00_rs1_mainContentContainer_ctl42" onclick="javascript:Track('ctl00_rs1_mainContentContainer_ctl00ctl00_rs1_mainContentContainer_ctl42',this);" href="http://go.microsoft.com/fwlink/?LinkId=124498"&gt;Example Application&lt;/a&gt; that retrieves data as an array. The following code uses the &lt;b&gt;sqlsrv_fetch_array&lt;/b&gt; function to retrieve one row at a time from a result set. Each row is retrieved as an associative array and is passed to the custom function &lt;b&gt;PopulateProductsTable&lt;/b&gt; for processing: &lt;/p&gt;&lt;p class="Text" style="LINE-HEIGHT: normal"&gt;&lt;/p&gt;&lt;p class="Text" style="LINE-HEIGHT: normal"&gt;&lt;span style="COLOR: rgb(0,32,96);font-family:'Courier New';" &gt;$productCount = 0;&lt;br /&gt;w&lt;/span&gt;&lt;span style="COLOR: rgb(0,32,96);font-family:'Courier New';" &gt;hile( $row = sqlsrv_fetch_array( $getProducts, SQLSRV_FETCH_ASSOC))&lt;br /&gt;&lt;/span&gt;&lt;span style="COLOR: rgb(0,32,96);font-family:'Courier New';" &gt;{&lt;br /&gt;&lt;/span&gt;&lt;span style="COLOR: rgb(0,32,96);font-family:'Courier New';" &gt;PopulateProductsTable(&lt;br /&gt;$row );&lt;br /&gt;&lt;/span&gt;&lt;span style="COLOR: rgb(0,32,96);font-family:'Courier New';" &gt;$productCount++;&lt;br /&gt;&lt;/span&gt;&lt;span style="COLOR: rgb(0,32,96);font-family:'Courier New';" &gt;}&lt;/span&gt; &lt;/p&gt;&lt;p class="Text" style="LINE-HEIGHT: normal"&gt;&lt;span style="COLOR: rgb(0,32,96);font-family:'Courier New';" &gt;&lt;/span&gt;&lt;/p&gt;&lt;p class="Text" style="LINE-HEIGHT: normal"&gt;The &lt;b&gt;sqlsrv_fetch_array&lt;/b&gt; function&lt;br /&gt;accepts two parameters, &lt;i&gt;$stmt&lt;/i&gt; and &lt;i&gt;$fetchType&lt;/i&gt; (optional):&lt;/p&gt;&lt;p class="Text" style="MARGIN-LEFT: 0.5in; TEXT-INDENT: -0.25in; LINE-HEIGHT: normal"&gt;&lt;span style="font-family:Symbol;"&gt;·&lt;span style="FONT: 7pt 'Times New Roman'; font-size-adjust: none; font-stretch: normalfont-size:100%;" &gt; &lt;/span&gt;&lt;/span&gt;The &lt;i&gt;$stmt&lt;/i&gt; parameter is a PHP statement resource created&lt;br /&gt;with &lt;b&gt;sqlsrv_query&lt;/b&gt; or &lt;b&gt;sqlsrv_execute&lt;/b&gt; (see the Executing a Query&lt;br /&gt;section).&lt;/p&gt;&lt;p class="Text" style="MARGIN-LEFT: 0.5in; TEXT-INDENT: -0.25in; LINE-HEIGHT: normal"&gt;&lt;span style="font-family:Symbol;"&gt;·&lt;span style="FONT: 7pt 'Times New Roman'; font-size-adjust: none; font-stretch: normalfont-size:100%;" &gt; &lt;/span&gt;&lt;/span&gt;The &lt;i&gt;$fetchType&lt;/i&gt; parameter (optional) is a driver-defined&lt;br /&gt;constant that specifies what type of array will be returned: associative,&lt;br /&gt;numeric, or both. The corresponding constants are SQLSRV_FETCH_ASSOC,&lt;br /&gt;SQLSRV_FETCH_NUMERIC, and SQLSRV_FETCH_BOTH. By default, an array with both&lt;br /&gt;types of indices is returned.&lt;/p&gt;&lt;h3 style="MARGIN-LEFT: 0in"&gt;&lt;a name="_Toc204499743"&gt;&lt;!----&gt;&lt;/a&gt;&lt;a name="_Ref203899973"&gt;&lt;span style="font-size:100%;"&gt;Retrieving Images&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p class="Text" style="LINE-HEIGHT: normal"&gt;In this section, we examine code in the &lt;a id="ctl00_rs1_mainContentContainer_ctl43" onclick="javascript:Track('ctl00_rs1_mainContentContainer_ctl00ctl00_rs1_mainContentContainer_ctl43',this);" href="http://go.microsoft.com/fwlink/?LinkId=124498"&gt;Example Application&lt;/a&gt; that retrieves an image from the server. The code below executes a query that retrieves an image from the server, specifies that the returned data be retrieved as a binary stream, and dumps the data to the Web page using the PHP &lt;a id="ctl00_rs1_mainContentContainer_ctl44" onclick="javascript:Track('ctl00_rs1_mainContentContainer_ctl00ctl00_rs1_mainContentContainer_ctl44',this);" href="http://go.microsoft.com/fwlink/?LinkId=124514"&gt;&lt;b&gt;fpassthru&lt;/b&gt;&lt;/a&gt;&lt;br /&gt;function:&lt;/p&gt;&lt;p class="Text" style="LINE-HEIGHT: normal"&gt;&lt;/p&gt;&lt;p class="Text" style="MARGIN-LEFT: 0.5in; LINE-HEIGHT: normal"&gt;&lt;span style="COLOR: rgb(0,32,96);font-family:'Courier New';" &gt;/* Get the product picture for&lt;br /&gt;a given product ID. */&lt;/span&gt;&lt;/p&gt;&lt;p class="Text" style="MARGIN-LEFT: 0.5in; LINE-HEIGHT: normal"&gt;&lt;span style="COLOR: rgb(0,32,96);font-family:'Courier New';" &gt;$tsql = "SELECT LargePhoto&lt;/span&gt;&lt;span style="COLOR: rgb(0,32,96);font-family:'Courier New';" &gt;&lt;br /&gt;FROM Production.ProductPhoto AS p&lt;/span&gt;&lt;span style="COLOR: rgb(0,32,96);font-family:'Courier New';" &gt;&lt;br /&gt;JOIN Production.ProductProductPhoto AS q&lt;/span&gt;&lt;span style="COLOR: rgb(0,32,96);font-family:'Courier New';" &gt;&lt;br /&gt;ON p.ProductPhotoID = q.ProductPhotoID&lt;/span&gt;&lt;span style="COLOR: rgb(0,32,96);font-family:'Courier New';" &gt;&lt;br /&gt;WHERE ProductID = ?";&lt;/span&gt;&lt;/p&gt;&lt;p class="Text" style="MARGIN-LEFT: 0.5in; LINE-HEIGHT: normal"&gt;&lt;span style="COLOR: rgb(0,32,96);font-family:'Courier New';" &gt;&lt;/span&gt;&lt;/p&gt;&lt;p class="Text" style="MARGIN-LEFT: 0.5in; LINE-HEIGHT: normal"&gt;&lt;span style="COLOR: rgb(0,32,96);font-family:'Courier New';" &gt;$params = array($_REQUEST['productId']);&lt;/span&gt;&lt;/p&gt;&lt;p class="Text" style="MARGIN-LEFT: 0.5in; LINE-HEIGHT: normal"&gt;&lt;span style="COLOR: rgb(0,32,96);font-family:'Courier New';" &gt;&lt;/span&gt;&lt;/p&gt;&lt;p class="Text" style="MARGIN-LEFT: 0.5in; LINE-HEIGHT: normal"&gt;&lt;span style="COLOR: rgb(0,32,96);font-family:'Courier New';" &gt;/* Execute the query. */&lt;/span&gt;&lt;/p&gt;&lt;p class="Text" style="MARGIN-LEFT: 0.5in; LINE-HEIGHT: normal"&gt;&lt;span style="COLOR: rgb(0,32,96);font-family:'Courier New';" &gt;$stmt = sqlsrv_query($conn, $tsql, $params);&lt;/span&gt;&lt;/p&gt;&lt;p class="Text" style="MARGIN-LEFT: 0.5in; LINE-HEIGHT: normal"&gt;&lt;span style="COLOR: rgb(0,32,96);font-family:'Courier New';" &gt;if( $stmt === false )&lt;br /&gt;&lt;/span&gt;&lt;span style="COLOR: rgb(0,32,96);font-family:'Courier New';" &gt;{&lt;br /&gt;&lt;/span&gt;&lt;span style="COLOR: rgb(0,32,96);font-family:'Courier New';" &gt;echo "Error in statement execution.&amp;lt;/br&amp;gt;";&lt;br /&gt;&lt;/span&gt;&lt;span style="COLOR: rgb(0,32,96);font-family:'Courier New';" &gt;die(&lt;br /&gt;print_r( sqlsrv_errors(), true));&lt;br /&gt;&lt;/span&gt;&lt;span style="COLOR: rgb(0,32,96);font-family:'Courier New';" &gt;}&lt;/span&gt;&lt;/p&gt;&lt;p class="Text" style="MARGIN-LEFT: 0.5in; LINE-HEIGHT: normal"&gt;&lt;span style="COLOR: rgb(0,32,96);font-family:'Courier New';" &gt;&lt;/span&gt;&lt;/p&gt;&lt;p class="Text" style="MARGIN-LEFT: 0.5in; LINE-HEIGHT: normal"&gt;&lt;span style="COLOR: rgb(0,32,96);font-family:'Courier New';" &gt;/* Retrieve the image as a binary stream. */&lt;/span&gt;&lt;/p&gt;&lt;p class="Text" style="MARGIN-LEFT: 0.5in; LINE-HEIGHT: normal"&gt;&lt;span style="COLOR: rgb(0,32,96);font-family:'Courier New';" &gt;$fieldIndex = 0;&lt;br /&gt;&lt;/span&gt;&lt;span style="COLOR: rgb(0,32,96);font-family:'Courier New';" &gt;$getAsType = SQLSRV_PHPTYPE_STREAM(SQLSRV_ENC_BINARY);&lt;/span&gt;&lt;/p&gt;&lt;p class="Text" style="MARGIN-LEFT: 0.5in; LINE-HEIGHT: normal"&gt;&lt;span style="COLOR: rgb(0,32,96);font-family:'Courier New';" &gt;if ( sqlsrv_fetch( $stmt ) )&lt;br /&gt;&lt;/span&gt;&lt;span style="COLOR: rgb(0,32,96);font-family:'Courier New';" &gt;{&lt;br /&gt;&lt;/span&gt;&lt;span style="COLOR: rgb(0,32,96);font-family:'Courier New';" &gt;$image = sqlsrv_get_field( $stmt, $fieldIndex, $getAsType);&lt;br /&gt;&lt;/span&gt;&lt;span style="COLOR: rgb(0,32,96);font-family:'Courier New';" &gt;fpassthru($image);&lt;br /&gt;&lt;/span&gt;&lt;span style="COLOR: rgb(0,32,96);font-family:'Courier New';" &gt;}&lt;br /&gt;&lt;/span&gt;&lt;span style="COLOR: rgb(0,32,96);font-family:'Courier New';" &gt;else&lt;br /&gt;&lt;/span&gt;&lt;span style="COLOR: rgb(0,32,96);font-family:'Courier New';" &gt;{&lt;br /&gt;&lt;/span&gt;&lt;span style="COLOR: rgb(0,32,96);font-family:'Courier New';" &gt;echo&lt;br /&gt;"Error in retrieving data.&amp;lt;/br&amp;gt;";&lt;/span&gt;&lt;/p&gt;&lt;p class="Text" style="MARGIN-LEFT: 0.5in; LINE-HEIGHT: normal"&gt;&lt;span style="COLOR: rgb(0,32,96);font-family:'Courier New';" &gt;die(print_r( sqlsrv_errors(), true));&lt;/span&gt;&lt;span style="COLOR: rgb(0,32,96);font-family:'Courier New';" &gt;}&lt;/span&gt;&lt;/p&gt;&lt;p class="Text" style="MARGIN-LEFT: 0.5in; LINE-HEIGHT: normal"&gt;&lt;/p&gt;&lt;p class="Text" style="LINE-HEIGHT: normal"&gt;The code above defines a parameterized Transact-SQL query &lt;i&gt;($tsql&lt;/i&gt;), specifies the parameter value (&lt;i&gt;$params&lt;/i&gt;), and executes the query with the &lt;b&gt;sqlsrv_query&lt;/b&gt; function (see the Executing&lt;br /&gt;a Query section). The result set is consumed by calling &lt;a id="ctl00_rs1_mainContentContainer_ctl45" onclick="javascript:Track('ctl00_rs1_mainContentContainer_ctl00ctl00_rs1_mainContentContainer_ctl45',this);" href="http://go.microsoft.com/fwlink/?LinkId=124541"&gt;&lt;b&gt;sqlsrv_fetch&lt;/b&gt;&lt;/a&gt; (to make the next row of the result set available for reading) followed by calling &lt;a id="ctl00_rs1_mainContentContainer_ctl46" onclick="javascript:Track('ctl00_rs1_mainContentContainer_ctl00ctl00_rs1_mainContentContainer_ctl46',this);" href="http://go.microsoft.com/fwlink/?LinkId=124542"&gt;&lt;b&gt;sqlsrv_get_field&lt;/b&gt;&lt;/a&gt;&lt;b&gt; &lt;/b&gt;(to read one field in the active row). The &lt;b&gt;sqlsrv_fetch&lt;/b&gt; function takes a single parameter (&lt;i&gt;$stmt&lt;/i&gt; in the example) that is a PHP resource created by &lt;b&gt;sqlsrv_query&lt;/b&gt; or &lt;b&gt;sqlsrv_execute&lt;/b&gt;. The &lt;b&gt;sqlsrv_get_field&lt;/b&gt; takes three parameters, &lt;i&gt;$stmt&lt;/i&gt;, &lt;i&gt;$fieldIndex&lt;/i&gt;, and &lt;i&gt;$getAsType&lt;/i&gt; optional):&lt;/p&gt;&lt;p class="Text" style="MARGIN-LEFT: 39pt; TEXT-INDENT: -0.25in; LINE-HEIGHT: normal"&gt;&lt;span style="font-family:Symbol;"&gt;·&lt;span style="FONT: 7pt 'Times New Roman'; font-size-adjust: none; font-stretch: normalfont-size:100%;" &gt; &lt;/span&gt;&lt;/span&gt;The &lt;i&gt;$stmt&lt;/i&gt; parameter is a PHP resource corresponding to an&lt;br /&gt;executed statement. The next (or first) row of data is made available to the &lt;b&gt;sqlsrv_get_field&lt;/b&gt; function by first passing the &lt;i&gt;$stmt&lt;/i&gt; parameter to the &lt;b&gt;sqlsrv_fetch&lt;/b&gt; function.&lt;/p&gt;&lt;p class="Text" style="MARGIN-LEFT: 39pt; TEXT-INDENT: -0.25in; LINE-HEIGHT: normal"&gt;&lt;span style="font-family:Symbol;"&gt;·&lt;span style="FONT: 7pt 'Times New Roman'; font-size-adjust: none; font-stretch: normalfont-size:100%;" &gt; &lt;/span&gt;&lt;/span&gt;The &lt;i&gt;$fieldIndex&lt;/i&gt; specifies the index of the field to be retrieved. Indices start at zero.&lt;/p&gt;&lt;p class="Text" style="MARGIN-LEFT: 39pt; TEXT-INDENT: -0.25in; LINE-HEIGHT: normal"&gt;&lt;span style="font-family:Symbol;"&gt;·&lt;span style="FONT: 7pt 'Times New Roman'; font-size-adjust: none; font-stretch: normalfont-size:100%;" &gt; &lt;/span&gt;&lt;/span&gt;The &lt;i&gt;$getAsType&lt;/i&gt; parameter (optional) is used to specify the PHP type (and encoding, in this case) of the returned data. If this parameter is not provided, data will be returned according to its default PHP type. For more information, see &lt;a id="ctl00_rs1_mainContentContainer_ctl47" onclick="javascript:Track('ctl00_rs1_mainContentContainer_ctl00ctl00_rs1_mainContentContainer_ctl47',this);" href="http://go.microsoft.com/fwlink/?LinkId=124529"&gt;Default PHP Data Types&lt;/a&gt; in the product documentation.&lt;/p&gt;&lt;p class="Text" style="LINE-HEIGHT: normal"&gt;For more information about retrieving images and binary/large data, see &lt;a id="ctl00_rs1_mainContentContainer_ctl48" onclick="javascript:Track('ctl00_rs1_mainContentContainer_ctl00ctl00_rs1_mainContentContainer_ctl48',this);" href="http://go.microsoft.com/fwlink/?LinkId=124512"&gt;Retrieving Data as a Stream&lt;/a&gt; in the product documentation.&lt;/p&gt;&lt;p class="Text" style="LINE-HEIGHT: normal"&gt;Retrieving data with the combination if the &lt;b&gt;sqlsrv_fetch&lt;/b&gt; and &lt;b&gt;sqlsrv_get_field&lt;/b&gt; functions can be used to specify the PHP type of returned data, not only for retrieving data as a stream. For more information, see &lt;a id="ctl00_rs1_mainContentContainer_ctl49" onclick="javascript:Track('ctl00_rs1_mainContentContainer_ctl00ctl00_rs1_mainContentContainer_ctl49',this);" href="http://msdn.microsoft.com/en-us/library/cc296208.aspx"&gt;How to: Specify PHP Data Types&lt;/a&gt; in the product documentation.&lt;/p&gt;&lt;h2 style="MARGIN-LEFT: 0in"&gt;&lt;a name="_Toc204499744"&gt;&lt;span style="font-size:100%;"&gt;Sending Data to the Server&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p class="Text" style="LINE-HEIGHT: normal"&gt;The general pattern for sending data to the server involves executing an appropriate Transact-SQL query (such as an UPDATE or INSERT query) with the &lt;b&gt;sqlsrv_query&lt;/b&gt; function or the combination of the &lt;b&gt;sqlsrv_prepare&lt;/b&gt; and &lt;b&gt;sqlsrv_execute&lt;/b&gt; functions (see the Executing a Query section). For example, the code below (from the &lt;a id="ctl00_rs1_mainContentContainer_ctl50" onclick="javascript:Track('ctl00_rs1_mainContentContainer_ctl00ctl00_rs1_mainContentContainer_ctl50',this);" href="http://go.microsoft.com/fwlink/?LinkId=124498"&gt;Example Application&lt;/a&gt; in the product documentation) sends a product review to the server using the combination of the &lt;b&gt;sqlsrv_prepare&lt;/b&gt; and &lt;b&gt;sqlsrv_execute&lt;/b&gt; functions: &lt;/p&gt;&lt;p class="Text" style="LINE-HEIGHT: normal"&gt;&lt;/p&gt;&lt;p class="Text" style="MARGIN-LEFT: 0.5in; LINE-HEIGHT: normal"&gt;&lt;span style="COLOR: rgb(0,32,96);font-family:'Courier New';" &gt;/*Prepend the review so it can be opened as a stream.*/&lt;/span&gt;&lt;/p&gt;&lt;p class="Text" style="MARGIN-LEFT: 0.5in; LINE-HEIGHT: normal"&gt;&lt;span style="COLOR: rgb(0,32,96);font-family:'Courier New';" &gt;$comments = "data://text/plain,".$_REQUEST['comments'];&lt;br /&gt;&lt;/span&gt;&lt;span style="COLOR: rgb(0,32,96);font-family:'Courier New';" &gt;$stream = fopen( $comments, "r" );&lt;/span&gt;&lt;/p&gt;&lt;p class="Text" style="MARGIN-LEFT: 0.5in; LINE-HEIGHT: normal"&gt;&lt;span style="COLOR: rgb(0,32,96);font-family:'Courier New';" &gt;$tsql = "INSERT INTO&lt;br /&gt;Production.ProductReview (ProductID,&lt;/span&gt;&lt;span style="COLOR: rgb(0,32,96);font-family:'Courier New';" &gt;ReviewerName,&lt;/span&gt;&lt;span style="COLOR: rgb(0,32,96);font-family:'Courier New';" &gt;ReviewDate,&lt;/span&gt;&lt;span style="COLOR: rgb(0,32,96);font-family:'Courier New';" &gt;EmailAddress,&lt;br /&gt;&lt;/span&gt;&lt;span style="COLOR: rgb(0,32,96);font-family:'Courier New';" &gt;Rating,&lt;/span&gt;&lt;span style="COLOR: rgb(0,32,96);font-family:'Courier New';" &gt;Comments)&lt;br /&gt;&lt;/span&gt;&lt;span style="COLOR: rgb(0,32,96);font-family:'Courier New';" &gt;VALUES (?,?,?,?,?,?)";&lt;/span&gt;&lt;/p&gt;&lt;p class="Text" style="MARGIN-LEFT: 0.5in; LINE-HEIGHT: normal"&gt;&lt;span style="COLOR: rgb(0,32,96);font-family:'Courier New';" &gt;&lt;/span&gt;&lt;/p&gt;&lt;p class="Text" style="MARGIN-LEFT: 0.5in; LINE-HEIGHT: normal"&gt;&lt;span style="COLOR: rgb(0,32,96);font-family:'Courier New';" &gt;$params = array($_REQUEST['productid'],&lt;br /&gt;&lt;/span&gt;&lt;span style="COLOR: rgb(0,32,96);font-family:'Courier New';" &gt;$_REQUEST['name'],&lt;br /&gt;&lt;/span&gt;&lt;span style="COLOR: rgb(0,32,96);font-family:'Courier New';" &gt;date("Y-m-d"),&lt;br /&gt;&lt;/span&gt;&lt;span style="COLOR: rgb(0,32,96);font-family:'Courier New';" &gt;$_REQUEST['email'],&lt;br /&gt;&lt;/span&gt;&lt;span style="COLOR: rgb(0,32,96);font-family:'Courier New';" &gt;$_REQUEST['rating'],&lt;br /&gt;&lt;/span&gt;&lt;span style="COLOR: rgb(0,32,96);font-family:'Courier New';" &gt;$stream);&lt;/span&gt;&lt;/p&gt;&lt;p class="Text" style="MARGIN-LEFT: 0.5in; LINE-HEIGHT: normal"&gt;&lt;span style="COLOR: rgb(0,32,96);font-family:'Courier New';" &gt;&lt;/span&gt;&lt;/p&gt;&lt;p class="Text" style="MARGIN-LEFT: 0.5in; LINE-HEIGHT: normal"&gt;&lt;span style="COLOR: rgb(0,32,96);font-family:'Courier New';" &gt;/* Prepare and execute the statement. */&lt;/span&gt;&lt;/p&gt;&lt;p class="Text" style="MARGIN-LEFT: 0.5in; LINE-HEIGHT: normal"&gt;&lt;span style="COLOR: rgb(0,32,96);font-family:'Courier New';" &gt;$insertReview =&lt;br /&gt;sqlsrv_prepare($conn, $tsql, $params);&lt;/span&gt;&lt;/p&gt;&lt;p class="Text" style="MARGIN-LEFT: 0.5in; LINE-HEIGHT: normal"&gt;&lt;span style="COLOR: rgb(0,32,96);font-family:'Courier New';" &gt;if( $insertReview === false )&lt;br /&gt;&lt;/span&gt;&lt;span style="COLOR: rgb(0,32,96);font-family:'Courier New';" &gt;{ die( FormatErrors( sqlsrv_errors() ) ); }&lt;/span&gt;&lt;/p&gt;&lt;p class="Text" style="MARGIN-LEFT: 0.5in; LINE-HEIGHT: normal"&gt;&lt;span style="COLOR: rgb(0,32,96);font-family:'Courier New';" &gt;/* By default, all stream data is sent at the time of query execution. */&lt;/span&gt;&lt;/p&gt;&lt;p class="Text" style="MARGIN-LEFT: 0.5in; LINE-HEIGHT: normal"&gt;&lt;span style="COLOR: rgb(0,32,96);font-family:'Courier New';" &gt;if( sqlsrv_execute($insertReview) === false )&lt;br /&gt;&lt;/span&gt;&lt;span style="COLOR: rgb(0,32,96);font-family:'Courier New';" &gt;{ die( FormatErrors( sqlsrv_errors() ) ); }&lt;/span&gt;&lt;/p&gt;&lt;p class="Text" style="LINE-HEIGHT: normal"&gt;&lt;/p&gt;&lt;p class="Text" style="LINE-HEIGHT: normal"&gt;The parameters for &lt;b&gt;sqlsrv_prepare&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;are the same as those for &lt;b&gt;sqlsrv_query&lt;/b&gt; (see the Executing a Query&lt;br /&gt;section). The &lt;b&gt;sqlsrv_execute&lt;/b&gt; function takes one parameter (&lt;i&gt;$insertReview&lt;/i&gt;&lt;br /&gt;in the example) that is a PHP resource specifying the prepared statement to be&lt;br /&gt;executed. &lt;/p&gt;&lt;p class="Text" style="MARGIN-LEFT: 0.5in; LINE-HEIGHT: normal"&gt;&lt;b&gt;Note&lt;/b&gt;&lt;br /&gt;The query in the example could have been executed with the &lt;b&gt;sqlsrv_query&lt;/b&gt;&lt;br /&gt;function. The recommended practice for executing a single query is to use the &lt;b&gt;sqlsrv_query&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;function. The use of &lt;b&gt;sqlsrv_prepare&lt;/b&gt; and &lt;b&gt;sqlsrv_execute&lt;/b&gt; in the&lt;br /&gt;example is done to demonstrate how these functions are used together.)&lt;/p&gt;&lt;p class="Text" style="LINE-HEIGHT: normal"&gt;This example highlights the driver’s&lt;br /&gt;streaming capabilities. The customer’s comments (&lt;i&gt;$comments&lt;/i&gt;) are opened&lt;br /&gt;as a text stream &lt;i&gt;($stream&lt;/i&gt;), which is a parameter in the query. By&lt;br /&gt;default, all stream data is sent to the server at the time of query execution.&lt;br /&gt;However, the driver also provides functionality that allows up to 8KB of stream&lt;br /&gt;data to be sent to the server at a time.&lt;span class="MsoCommentReference"&gt; &lt;/span&gt;For&lt;br /&gt;more information, see the Sending Images to the Server section below or &lt;a id="ctl00_rs1_mainContentContainer_ctl51" onclick="javascript:Track('ctl00_rs1_mainContentContainer_ctl00ctl00_rs1_mainContentContainer_ctl51',this);" href="http://go.microsoft.com/fwlink/?LinkId=124526"&gt;How to: Send Data as a&lt;br /&gt;Stream&lt;/a&gt; in the product documentation.&lt;/p&gt;&lt;p class="Text" style="LINE-HEIGHT: normal"&gt;For more information about sending&lt;br /&gt;data to the server with the SQL Server 2005 Driver for PHP, see &lt;a id="ctl00_rs1_mainContentContainer_ctl52" onclick="javascript:Track('ctl00_rs1_mainContentContainer_ctl00ctl00_rs1_mainContentContainer_ctl52',this);" href="http://go.microsoft.com/fwlink/?LinkId=124508"&gt;Updating Data&lt;/a&gt; in the&lt;br /&gt;product documentation.&lt;/p&gt;&lt;h3 style="MARGIN-LEFT: 0in"&gt;&lt;a name="_Toc204499745"&gt;&lt;!----&gt;&lt;/a&gt;&lt;a name="_Ref203922394"&gt;&lt;span style="font-size:100%;"&gt;Sending&lt;br /&gt;Images to the Server&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p class="Text" style="LINE-HEIGHT: normal"&gt;In this section, we examine code in&lt;br /&gt;the &lt;a id="ctl00_rs1_mainContentContainer_ctl53" onclick="javascript:Track('ctl00_rs1_mainContentContainer_ctl00ctl00_rs1_mainContentContainer_ctl53',this);" href="http://go.microsoft.com/fwlink/?LinkId=124498"&gt;Example Application&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;that sends an image to the server as a binary stream. The following code opens&lt;br /&gt;an image as a stream and then sends the file up to the server in parts up to 8KB&lt;br /&gt;at a time:&lt;/p&gt;&lt;p class="Text" style="MARGIN-LEFT: 0.5in; LINE-HEIGHT: normal"&gt;&lt;span style="COLOR: rgb(0,32,96);font-family:'Courier New';" &gt;$tsql = "INSERT INTO Production.ProductPhoto&lt;br /&gt;(LargePhoto)&lt;/span&gt;&lt;span style="COLOR: rgb(0,32,96);font-family:'Courier New';" &gt;VALUES (?);&lt;br /&gt;&lt;/span&gt;&lt;span style="COLOR: rgb(0,32,96);font-family:'Courier New';" &gt;SELECT SCOPE_IDENTITY() AS PhotoID";&lt;br /&gt;&lt;/span&gt;&lt;span style="COLOR: rgb(0,32,96);font-family:'Courier New';" &gt;$fileStream =&lt;br /&gt;fopen($_FILES['file']['tmp_name'], "r");&lt;br /&gt;&lt;/span&gt;&lt;span style="COLOR: rgb(0,32,96);font-family:'Courier New';" &gt;$params = array(&lt;/span&gt;&lt;span style="COLOR: rgb(0,32,96);font-family:'Courier New';" &gt;array(&lt;/span&gt;&lt;span style="COLOR: rgb(0,32,96);font-family:'Courier New';" &gt;$fileStream, &lt;/span&gt;&lt;span style="COLOR: rgb(0,32,96);font-family:'Courier New';" &gt;SQLSRV_PARAM_IN,&lt;/span&gt;&lt;span style="COLOR: rgb(0,32,96);font-family:'Courier New';" &gt;SQLSRV_PHPTYPE_STREAM(SQLSRV_ENC_BINARY),&lt;/span&gt;&lt;span style="COLOR: rgb(0,32,96);font-family:'Courier New';" &gt;SQLSRV_SQLTYPE_VARBINARY('max')&lt;/span&gt;&lt;span style="COLOR: rgb(0,32,96);font-family:'Courier New';" &gt;)&lt;/span&gt;&lt;span style="COLOR: rgb(0,32,96);font-family:'Courier New';" &gt;);&lt;/span&gt;&lt;/p&gt;&lt;p class="Text" style="MARGIN-LEFT: 0.5in; LINE-HEIGHT: normal"&gt;&lt;span style="COLOR: rgb(0,32,96);font-family:'Courier New';" &gt;/* Turn off the default behavior of sending all stream data &lt;/span&gt;&lt;span style="COLOR: rgb(0,32,96);font-family:'Courier New';" &gt;to the server at the time of query execution. */&lt;/span&gt;&lt;/p&gt;&lt;p class="Text" style="TEXT-INDENT: 0.5in; LINE-HEIGHT: normal"&gt;&lt;span style="COLOR: rgb(0,32,96);font-family:'Courier New';" &gt;$options = array("SendStreamParamsAtExec"=&amp;gt;0);&lt;br /&gt;&lt;/span&gt;&lt;span style="COLOR: rgb(0,32,96);font-family:'Courier New';" &gt;$uploadPic = sqlsrv_prepare($conn, $tsql, $params, $options);&lt;/span&gt;&lt;/p&gt;&lt;p class="Text" style="MARGIN-LEFT: 0.5in; LINE-HEIGHT: normal"&gt;&lt;span style="COLOR: rgb(0,32,96);font-family:'Courier New';" &gt;if( $uploadPic === false )&lt;br /&gt;&lt;/span&gt;&lt;span style="COLOR: rgb(0,32,96);font-family:'Courier New';" &gt;{ die( FormatErrors( sqlsrv_errors() ) );}&lt;/span&gt;&lt;/p&gt;&lt;p class="Text" style="MARGIN-LEFT: 0.5in; LINE-HEIGHT: normal"&gt;&lt;span style="COLOR: rgb(0,32,96);font-family:'Courier New';" &gt;if( sqlsrv_execute($uploadPic) === false )&lt;br /&gt;&lt;/span&gt;&lt;span style="COLOR: rgb(0,32,96);font-family:'Courier New';" &gt;{ die( FormatErrors( sqlsrv_errors() ) ); }&lt;/span&gt;&lt;/p&gt;&lt;p class="Text" style="MARGIN-LEFT: 0.5in; LINE-HEIGHT: normal"&gt;&lt;span style="COLOR: rgb(0,32,96);font-family:'Courier New';" &gt;&lt;/span&gt;&lt;/p&gt;&lt;p class="Text" style="MARGIN-LEFT: 0.5in; LINE-HEIGHT: normal"&gt;&lt;span style="COLOR: rgb(0,32,96);font-family:'Courier New';" &gt;/* Stream data to the database&lt;br /&gt;in chunks. */&lt;/span&gt;&lt;/p&gt;&lt;p class="Text" style="MARGIN-LEFT: 0.5in; LINE-HEIGHT: normal"&gt;&lt;span style="COLOR: rgb(0,32,96);font-family:'Courier New';" &gt;while( $success = sqlsrv_send_stream_data( $uploadPic))&lt;/span&gt;&lt;/p&gt;&lt;p class="Text" style="MARGIN-LEFT: 0.5in; LINE-HEIGHT: normal"&gt;&lt;span style="COLOR: rgb(0,32,96);font-family:'Courier New';" &gt;{&lt;/span&gt;&lt;/p&gt;&lt;p class="Text" style="MARGIN-LEFT: 0.5in; LINE-HEIGHT: normal"&gt;&lt;span style="COLOR: rgb(0,32,96);font-family:'Courier New';" &gt;}&lt;/span&gt;&lt;/p&gt;&lt;p class="Text" style="LINE-HEIGHT: normal"&gt;&lt;/p&gt;&lt;p class="Text" style="LINE-HEIGHT: normal"&gt;As noted in the previous example, the&lt;br /&gt;general pattern for sending data to the server involves executing an&lt;br /&gt;appropriate Transact-SQL query (such as an UPDATE or INSERT statement). The&lt;br /&gt;notable differences in this example are described here:&lt;/p&gt;&lt;p class="Text" style="MARGIN-LEFT: 0.5in; TEXT-INDENT: -0.25in; LINE-HEIGHT: normal"&gt;&lt;span style="font-family:Symbol;color:windowtext;"&gt;·&lt;span style="FONT: 7pt 'Times New Roman'; font-size-adjust: none; font-stretch: normalfont-size:100%;" &gt; &lt;/span&gt;&lt;/span&gt;Types are specified in the in the &lt;i&gt;$params&lt;/i&gt; array. The PHP&lt;br /&gt;type must be specified here so that the driver will interpret the data as a&lt;br /&gt;binary stream. The SQL Server type must be specified so that the server will&lt;br /&gt;interpret the incoming data correctly.&lt;/p&gt;&lt;p class="Text" style="MARGIN-LEFT: 0.5in; LINE-HEIGHT: normal"&gt;For more information,&lt;br /&gt;see &lt;a id="ctl00_rs1_mainContentContainer_ctl54" onclick="javascript:Track('ctl00_rs1_mainContentContainer_ctl00ctl00_rs1_mainContentContainer_ctl54',this);" href="http://go.microsoft.com/fwlink/?LinkId=124510"&gt;How to: Specify PHP&lt;br /&gt;Data Types&lt;/a&gt; and &lt;a id="ctl00_rs1_mainContentContainer_ctl55" onclick="javascript:Track('ctl00_rs1_mainContentContainer_ctl00ctl00_rs1_mainContentContainer_ctl55',this);" href="http://go.microsoft.com/fwlink/?LinkId=124527"&gt;How&lt;br /&gt;to: Specify SQL Server Data Types&lt;/a&gt; in the product documentation.&lt;/p&gt;&lt;p class="Text" style="MARGIN-LEFT: 0.5in; TEXT-INDENT: -0.25in; LINE-HEIGHT: normal"&gt;&lt;span style="font-family:Symbol;"&gt;·&lt;span style="FONT: 7pt 'Times New Roman'; font-size-adjust: none; font-stretch: normalfont-size:100%;" &gt; &lt;/span&gt;&lt;/span&gt;The &lt;i&gt;$options&lt;/i&gt; parameter is used when calling &lt;a id="ctl00_rs1_mainContentContainer_ctl56" onclick="javascript:Track('ctl00_rs1_mainContentContainer_ctl00ctl00_rs1_mainContentContainer_ctl56',this);" href="http://go.microsoft.com/fwlink/?LinkId=124535"&gt;&lt;b&gt;sqlsrv_prepare&lt;/b&gt;&lt;/a&gt;.&lt;br /&gt;The default behavior of sending all stream data to the server at the time of&lt;br /&gt;query execution is turned off by setting "SendStreamParamsAtExec" to&lt;br /&gt;0 in the &lt;i&gt;$options&lt;/i&gt; parameter. When this behavior is turned off, calls to &lt;a id="ctl00_rs1_mainContentContainer_ctl57" onclick="javascript:Track('ctl00_rs1_mainContentContainer_ctl00ctl00_rs1_mainContentContainer_ctl57',this);" href="http://go.microsoft.com/fwlink/?LinkId=124537"&gt;&lt;b&gt;sqlsrv_send_stream data&lt;/b&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;(after query execution) are required to send stream data to the server (see&lt;br /&gt;next bullet).&lt;/p&gt;&lt;p class="Text" style="MARGIN-LEFT: 0.5in; TEXT-INDENT: -0.25in; LINE-HEIGHT: normal"&gt;&lt;span style="font-family:Symbol;"&gt;·&lt;span style="FONT: 7pt 'Times New Roman'; font-size-adjust: none; font-stretch: normalfont-size:100%;" &gt; &lt;/span&gt;&lt;/span&gt;The &lt;a id="ctl00_rs1_mainContentContainer_ctl58" onclick="javascript:Track('ctl00_rs1_mainContentContainer_ctl00ctl00_rs1_mainContentContainer_ctl58',this);" href="http://go.microsoft.com/fwlink/?LinkId=124537"&gt;&lt;b&gt;sqlsrv_send_stream&lt;br /&gt;data&lt;/b&gt;&lt;/a&gt; function is used to send data up to 8KB of stream data to the&lt;br /&gt;server at a time. Turning off the default behavior of sending all stream data&lt;br /&gt;to the server at once and using &lt;b&gt;sqlsrv_send_stream_data&lt;/b&gt; to send stream&lt;br /&gt;data allows for flexibility in application design. For example, it allows an&lt;br /&gt;application to present users with a progress bar when uploading a large image.&lt;/p&gt;&lt;h2 style="MARGIN-LEFT: 0in"&gt;&lt;a name="_Ref206305715"&gt;&lt;!----&gt;&lt;/a&gt;&lt;a name="_Toc204499746"&gt;&lt;span style="font-size:100%;"&gt;Moving&lt;br /&gt;Through Groups of Result Sets&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p class="Text" style="LINE-HEIGHT: normal"&gt;The SQL Server 2005 Driver for PHP&lt;br /&gt;provides the &lt;a id="ctl00_rs1_mainContentContainer_ctl59" onclick="javascript:Track('ctl00_rs1_mainContentContainer_ctl00ctl00_rs1_mainContentContainer_ctl59',this);" href="http://go.microsoft.com/fwlink/?LinkId=124507"&gt;&lt;b&gt;sqlsrv_next_result&lt;/b&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;function for moving forward through multiple results returned by batch queries&lt;br /&gt;or stored procedures. This function makes the next result set, row count, or&lt;br /&gt;output parameter of an active statement available for reading.&lt;/p&gt;&lt;p class="Text" style="LINE-HEIGHT: normal"&gt;The code below demonstrates how to use&lt;br /&gt;&lt;b&gt;sqlsrv_next_result&lt;/b&gt; to move through results. The code here is taken from&lt;br /&gt;the &lt;a id="ctl00_rs1_mainContentContainer_ctl60" onclick="javascript:Track('ctl00_rs1_mainContentContainer_ctl00ctl00_rs1_mainContentContainer_ctl60',this);" href="http://go.microsoft.com/fwlink/?LinkId=124498"&gt;Example Application&lt;/a&gt;&lt;br /&gt;in the product documentation and immediately follows the code in the previous&lt;br /&gt;example. The &lt;i&gt;$uploadPic&lt;/i&gt; statement corresponds to a batch query in the&lt;br /&gt;previous example. The code here moves to the second result in that statement&lt;br /&gt;and uses the retrieved value to execute a query that associates the new&lt;br /&gt;ProductPhotoID with a ProductID:&lt;/p&gt;&lt;p class="Text" style="LINE-HEIGHT: normal"&gt;&lt;span style="COLOR: rgb(0,32,96);font-family:'Courier New';" &gt;/*Skip the open (first) result&lt;br /&gt;set (rows affected). */&lt;/span&gt;&lt;/p&gt;&lt;p class="Text" style="LINE-HEIGHT: normal"&gt;&lt;span style="COLOR: rgb(0,32,96);font-family:'Courier New';" &gt;$next_result =&lt;br /&gt;sqlsrv_next_result($uploadPic);&lt;/span&gt;&lt;/p&gt;&lt;p class="Text" style="LINE-HEIGHT: normal"&gt;&lt;span style="COLOR: rgb(0,32,96);font-family:'Courier New';" &gt;if( $next_result === false )&lt;br /&gt;&lt;/span&gt;&lt;span style="COLOR: rgb(0,32,96);font-family:'Courier New';" &gt;{ die( FormatErrors( sqlsrv_errors() ) ); }&lt;/span&gt;&lt;/p&gt;&lt;p class="Text" style="LINE-HEIGHT: normal"&gt;&lt;span style="COLOR: rgb(0,32,96);font-family:'Courier New';" &gt;&lt;/span&gt;&lt;/p&gt;&lt;p class="Text" style="LINE-HEIGHT: normal"&gt;&lt;span style="COLOR: rgb(0,32,96);font-family:'Courier New';" &gt;/* Fetch the next result&lt;br /&gt;set. */&lt;/span&gt;&lt;/p&gt;&lt;p class="Text" style="LINE-HEIGHT: normal"&gt;&lt;span style="COLOR: rgb(0,32,96);font-family:'Courier New';" &gt;if( sqlsrv_fetch($uploadPic)=== false)&lt;br /&gt;&lt;/span&gt;&lt;span style="COLOR: rgb(0,32,96);font-family:'Courier New';" &gt;{ die( FormatErrors( sqlsrv_errors() ) ); }&lt;/span&gt;&lt;/p&gt;&lt;p class="Text" style="LINE-HEIGHT: normal"&gt;&lt;span style="COLOR: rgb(0,32,96);font-family:'Courier New';" &gt;&lt;/span&gt;&lt;/p&gt;&lt;p class="Text" style="LINE-HEIGHT: normal"&gt;&lt;span style="COLOR: rgb(0,32,96);font-family:'Courier New';" &gt;/* Get the first field - the&lt;br /&gt;identity from INSERT. */&lt;/span&gt;&lt;/p&gt;&lt;p class="Text" style="LINE-HEIGHT: normal"&gt;&lt;span style="COLOR: rgb(0,32,96);font-family:'Courier New';" &gt;$photoID = sqlsrv_get_field($uploadPic, 0);&lt;/span&gt;&lt;/p&gt;&lt;p class="Text" style="LINE-HEIGHT: normal"&gt;&lt;span style="COLOR: rgb(0,32,96);font-family:'Courier New';" &gt;&lt;/span&gt;&lt;/p&gt;&lt;p class="Text" style="LINE-HEIGHT: normal"&gt;&lt;span style="COLOR: rgb(0,32,96);font-family:'Courier New';" &gt;/* Associate the new photoID&lt;br /&gt;with the productID. */&lt;/span&gt;&lt;/p&gt;&lt;p class="Text" style="LINE-HEIGHT: normal"&gt;&lt;span style="COLOR: rgb(0,32,96);font-family:'Courier New';" &gt;$tsql = "UPDATE Production.ProductProductPhoto&lt;br /&gt;&lt;/span&gt;&lt;span style="COLOR: rgb(0,32,96);font-family:'Courier New';" &gt;SET ProductPhotoID = ?&lt;br /&gt;&lt;/span&gt;&lt;span style="COLOR: rgb(0,32,96);font-family:'Courier New';" &gt;WHERE ProductID = ?";&lt;/span&gt;&lt;/p&gt;&lt;p class="Text" style="LINE-HEIGHT: normal"&gt;&lt;span style="COLOR: rgb(0,32,96);font-family:'Courier New';" &gt;$params = array($photoID, $_REQUEST['productid']);&lt;/span&gt;&lt;/p&gt;&lt;p class="Text" style="LINE-HEIGHT: normal"&gt;&lt;span style="COLOR: rgb(0,32,96);font-family:'Courier New';" &gt;if( sqlsrv_query($conn, $tsql, $params) === false )&lt;br /&gt;&lt;/span&gt;&lt;span style="COLOR: rgb(0,32,96);font-family:'Courier New';" &gt;{ die( FormatErrors( sqlsrv_errors() ) ); }&lt;/span&gt;&lt;/p&gt;&lt;p class="Text" style="LINE-HEIGHT: normal"&gt;&lt;/p&gt;&lt;p class="Text" style="LINE-HEIGHT: normal"&gt;The required parameter for the &lt;b&gt;sqlsrv_next_result&lt;/b&gt;&lt;br /&gt;function is a PHP resource corresponding to an active statement. It is not&lt;br /&gt;necessary to call &lt;b&gt;sqlsrv_next_result&lt;/b&gt; to access the first result of a&lt;br /&gt;statement. This function will return &lt;b&gt;null&lt;/b&gt; if there are no more results&lt;br /&gt;on the statement.&lt;/p&gt;&lt;p class="Text" style="LINE-HEIGHT: normal"&gt;For more information about moving&lt;br /&gt;through results, see &lt;a id="ctl00_rs1_mainContentContainer_ctl61" onclick="javascript:Track('ctl00_rs1_mainContentContainer_ctl00ctl00_rs1_mainContentContainer_ctl61',this);" href="http://msdn.microsoft.com/en-us/library/cc626309.aspx"&gt;How to: Work with&lt;br /&gt;Multiple Result Sets&lt;/a&gt; and &lt;a id="ctl00_rs1_mainContentContainer_ctl62" onclick="javascript:Track('ctl00_rs1_mainContentContainer_ctl00ctl00_rs1_mainContentContainer_ctl62',this);" href="http://go.microsoft.com/fwlink/?LinkId=124504"&gt;How to: Detect Empty&lt;br /&gt;Result Sets&lt;/a&gt; in the product documentation.&lt;/p&gt;&lt;p class="Text" style="LINE-HEIGHT: normal"&gt;&lt;/p&gt;&lt;h2 style="MARGIN-LEFT: 0in"&gt;&lt;a name="_Toc204499747"&gt;&lt;!----&gt;&lt;/a&gt;&lt;a name="_Ref204490636"&gt;&lt;span style="font-size:100%;"&gt;Handling&lt;br /&gt;Errors and Warnings&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p class="Text" style="LINE-HEIGHT: normal"&gt;The SQL Server 2005 Driver for PHP&lt;br /&gt;provides the &lt;a id="ctl00_rs1_mainContentContainer_ctl63" onclick="javascript:Track('ctl00_rs1_mainContentContainer_ctl00ctl00_rs1_mainContentContainer_ctl63',this);" href="http://go.microsoft.com/fwlink/?LinkId=124502"&gt;&lt;b&gt;sqlsrv_errors&lt;/b&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;function for retrieving details about errors and warnings. If an error occurs&lt;br /&gt;in any &lt;b&gt;sqlsrv&lt;/b&gt; function, the function returns &lt;b&gt;false&lt;/b&gt; and the error&lt;br /&gt;details are added to the error collection. The &lt;b&gt;sqlsrv_errors&lt;/b&gt; function&lt;br /&gt;provides access to this error collection.&lt;/p&gt;&lt;p class="Text" style="MARGIN-LEFT: 0.5in; LINE-HEIGHT: normal"&gt;&lt;b&gt;Note&lt;/b&gt;&lt;br /&gt;By default, warnings are treated as errors with a few exceptions: warnings that&lt;br /&gt;correspond to the SQLSTATE values 01000, 01001, 01003, and 01S02 are never&lt;br /&gt;treated as errors. This default behavior can be changed so that warnings are&lt;br /&gt;not treated as errors. For more information, see &lt;a id="ctl00_rs1_mainContentContainer_ctl64" onclick="javascript:Track('ctl00_rs1_mainContentContainer_ctl00ctl00_rs1_mainContentContainer_ctl64',this);" href="http://go.microsoft.com/fwlink/?LinkId=124515"&gt;How to: Configure Error&lt;br /&gt;and Warning Handling&lt;/a&gt; in the product documentation and the Configuring the&lt;br /&gt;Driver section. &lt;/p&gt;&lt;p class="Text" style="LINE-HEIGHT: normal"&gt;The general pattern for using &lt;b&gt;sqlsrv_errors&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;is to check the return value of a &lt;b&gt;sqlsrv&lt;/b&gt; function and then handle errors&lt;br /&gt;accordingly. This code from the &lt;a id="ctl00_rs1_mainContentContainer_ctl65" onclick="javascript:Track('ctl00_rs1_mainContentContainer_ctl00ctl00_rs1_mainContentContainer_ctl65',this);" href="http://go.microsoft.com/fwlink/?LinkId=124498"&gt;Example Application&lt;/a&gt; in&lt;br /&gt;the product documentation demonstrates the pattern:&lt;/p&gt;&lt;p class="Text" style="MARGIN: 0in 0in 0pt; LINE-HEIGHT: normal"&gt;&lt;/p&gt;&lt;p class="Text" style="MARGIN: 0in 0in 0pt; TEXT-INDENT: 0.5in; LINE-HEIGHT: normal"&gt;&lt;span style="COLOR: rgb(0,32,96);font-family:'Courier New';" &gt;if( sqlsrv_execute($insertReview) === false )&lt;/span&gt;&lt;/p&gt;&lt;p class="Text" style="MARGIN: 0in 0in 0pt; LINE-HEIGHT: normal"&gt;&lt;span style="COLOR: rgb(0,32,96);font-family:'Courier New';" &gt;{ die( FormatErrors( sqlsrv_errors() ) ); }&lt;/span&gt;&lt;/p&gt;&lt;p class="Text" style="MARGIN: 0in 0in 0pt; LINE-HEIGHT: normal"&gt;&lt;/p&gt;&lt;p class="Text" style="LINE-HEIGHT: normal"&gt;The &lt;b&gt;sqlsrv_errors&lt;/b&gt; function&lt;br /&gt;returns a collection of arrays, one array for each error that occurred. Each&lt;br /&gt;array contains detailed error information. The custom function &lt;b&gt;FormatErrors&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;in the &lt;a id="ctl00_rs1_mainContentContainer_ctl66" onclick="javascript:Track('ctl00_rs1_mainContentContainer_ctl00ctl00_rs1_mainContentContainer_ctl66',this);" href="http://go.microsoft.com/fwlink/?LinkId=124498"&gt;Example&lt;br /&gt;Application&lt;/a&gt; simply iterates through the collection of arrays and displays&lt;br /&gt;error information:&lt;/p&gt;&lt;p class="Text" style="LINE-HEIGHT: normal"&gt;&lt;/p&gt;&lt;p class="Text" style="MARGIN: 0in 0in 0pt 0.5in; LINE-HEIGHT: normal"&gt;&lt;span style="COLOR: rgb(0,32,96);font-family:'Courier New';" &gt;function FormatErrors( $errors )&lt;/span&gt;&lt;/p&gt;&lt;p class="Text" style="MARGIN: 0in 0in 0pt 0.5in; LINE-HEIGHT: normal"&gt;&lt;span style="COLOR: rgb(0,32,96);font-family:'Courier New';" &gt;{&lt;/span&gt;&lt;/p&gt;&lt;p class="Text" style="MARGIN: 0in 0in 0pt 0.5in; LINE-HEIGHT: normal"&gt;&lt;span style="COLOR: rgb(0,32,96);font-family:'Courier New';" &gt;/* Display errors. */&lt;/span&gt;&lt;/p&gt;&lt;p class="Text" style="MARGIN: 0in 0in 0pt 0.5in; LINE-HEIGHT: normal"&gt;&lt;span style="COLOR: rgb(0,32,96);font-family:'Courier New';" &gt;echo "Error information: &amp;lt;br/&amp;gt;";&lt;/span&gt;&lt;/p&gt;&lt;p class="Text" style="MARGIN: 0in 0in 0pt 0.5in; LINE-HEIGHT: normal"&gt;&lt;span style="COLOR: rgb(0,32,96);font-family:'Courier New';" &gt;&lt;/span&gt;&lt;/p&gt;&lt;p class="Text" style="MARGIN: 0in 0in 0pt 0.5in; LINE-HEIGHT: normal"&gt;&lt;span style="COLOR: rgb(0,32,96);font-family:'Courier New';" &gt;foreach ( $errors as $error&lt;br /&gt;)&lt;/span&gt;&lt;/p&gt;&lt;p class="Text" style="MARGIN: 0in 0in 0pt 0.5in; LINE-HEIGHT: normal"&gt;&lt;span style="COLOR: rgb(0,32,96);font-family:'Courier New';" &gt;{&lt;/span&gt;&lt;/p&gt;&lt;p class="Text" style="MARGIN: 0in 0in 0pt 0.5in; LINE-HEIGHT: normal"&gt;&lt;span style="COLOR: rgb(0,32,96);font-family:'Courier New';" &gt;echo "SQLSTATE: ".$error['SQLSTATE']."&amp;lt;br/&amp;gt;";&lt;/span&gt;&lt;/p&gt;&lt;p class="Text" style="MARGIN: 0in 0in 0pt 0.5in; LINE-HEIGHT: normal"&gt;&lt;span style="COLOR: rgb(0,32,96);font-family:'Courier New';" &gt;echo "Code:&lt;br /&gt;".$error['code']."&amp;lt;br/&amp;gt;";&lt;/span&gt;&lt;/p&gt;&lt;p class="Text" style="MARGIN: 0in 0in 0pt 0.5in; LINE-HEIGHT: normal"&gt;&lt;span style="COLOR: rgb(0,32,96);font-family:'Courier New';" &gt;echo "Message: ".$error['message']."&amp;lt;br/&amp;gt;";&lt;/span&gt;&lt;/p&gt;&lt;p class="Text" style="MARGIN: 0in 0in 0pt 0.5in; LINE-HEIGHT: normal"&gt;&lt;span style="COLOR: rgb(0,32,96);font-family:'Courier New';" &gt;}&lt;/span&gt;&lt;/p&gt;&lt;p class="Text" style="MARGIN: 0in 0in 0pt 0.5in; LINE-HEIGHT: normal"&gt;&lt;span style="COLOR: rgb(0,32,96);font-family:'Courier New';" &gt;}&lt;/span&gt;&lt;/p&gt;&lt;p class="Text" style="MARGIN: 0in 0in 0pt; LINE-HEIGHT: normal"&gt;&lt;/p&gt;&lt;p class="Text" style="LINE-HEIGHT: normal"&gt;When evaluating the return value of a &lt;b&gt;sqlsrv&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;function, it is best to use the PHP triple equals operator (===). This is&lt;br /&gt;because all &lt;b&gt;sqlsrv&lt;/b&gt; functions return &lt;b&gt;false&lt;/b&gt; if an error occurs. For&lt;br /&gt;&lt;b&gt;sqlsrv&lt;/b&gt; functions that could return some value that PHP evaluates to &lt;b&gt;false&lt;/b&gt;&lt;br /&gt;it is important to use the triple equals operator to force a literal&lt;br /&gt;comparison. For example, &lt;b&gt;sqlsrv_fetch&lt;/b&gt; could return &lt;b&gt;null&lt;/b&gt; if there&lt;br /&gt;are no more rows in a result set. In this case, using a double equals operator&lt;br /&gt;(==) to check for an error (&lt;span style="font-family:'Courier New';"&gt;$result == &lt;b&gt;false&lt;/b&gt;&lt;/span&gt;)&lt;br /&gt;would evaluate to &lt;b&gt;true&lt;/b&gt;, resulting in unexpected program flow.&lt;/p&gt;&lt;p class="Text" style="LINE-HEIGHT: normal"&gt;For more information, see &lt;a id="ctl00_rs1_mainContentContainer_ctl67" onclick="javascript:Track('ctl00_rs1_mainContentContainer_ctl00ctl00_rs1_mainContentContainer_ctl67',this);" href="http://go.microsoft.com/fwlink/?LinkId=124499"&gt;Handling Errors and&lt;br /&gt;Warnings&lt;/a&gt; in the product documentation.&lt;/p&gt;&lt;h2 style="MARGIN-LEFT: 0in"&gt;&lt;a name="_Toc204499748"&gt;&lt;!----&gt;&lt;/a&gt;&lt;a name="_Ref204494458"&gt;&lt;span style="font-size:100%;"&gt;Resources&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p class="Text" style="LINE-HEIGHT: normal"&gt;The following resources are available for&lt;br /&gt;developing applications with the SQL Server 2005 Driver for PHP:&lt;/p&gt;&lt;p class="Text" style="MARGIN-LEFT: 0.5in; TEXT-INDENT: -0.25in; LINE-HEIGHT: normal"&gt;&lt;span style="font-family:Symbol;"&gt;·&lt;span style="FONT: 7pt 'Times New Roman'; font-size-adjust: none; font-stretch: normalfont-size:100%;" &gt; &lt;/span&gt;&lt;/span&gt;Download site: &lt;a id="ctl00_rs1_mainContentContainer_ctl68" onclick="javascript:Track('ctl00_rs1_mainContentContainer_ctl00ctl00_rs1_mainContentContainer_ctl68',this);" href="http://go.microsoft.com/fwlink/?LinkId=124503"&gt;SQL Server 2005 Driver for PHP in the Microsoft Download Center&lt;/a&gt;&lt;/p&gt;&lt;p class="Text" style="MARGIN-LEFT: 0.5in; TEXT-INDENT: -0.25in; LINE-HEIGHT: normal"&gt;&lt;span style="font-family:Symbol;"&gt;·&lt;span style="FONT: 7pt 'Times New Roman'; font-size-adjust: none; font-stretch: normalfont-size:100%;" &gt; &lt;/span&gt;&lt;/span&gt;Peer-to-peer support: &lt;a id="ctl00_rs1_mainContentContainer_ctl69" onclick="javascript:Track('ctl00_rs1_mainContentContainer_ctl00ctl00_rs1_mainContentContainer_ctl69',this);" href="http://go.microsoft.com/fwlink/?LinkId=101875"&gt;SQL Server Driver for PHP in the MSDN Forums&lt;/a&gt; &lt;/p&gt;&lt;p class="Text" style="MARGIN-LEFT: 0.5in; TEXT-INDENT: -0.25in; LINE-HEIGHT: normal"&gt;&lt;span style="font-family:Symbol;"&gt;·&lt;span style="FONT: 7pt 'Times New Roman'; font-size-adjust: none; font-stretch: normalfont-size:100%;" &gt; &lt;/span&gt;&lt;/span&gt;Online documentation: &lt;a id="ctl00_rs1_mainContentContainer_ctl70" onclick="javascript:Track('ctl00_rs1_mainContentContainer_ctl00ctl00_rs1_mainContentContainer_ctl70',this);" href="http://go.microsoft.com/fwlink/?linkid=119889"&gt;SQL Server 2005 Driver for PHP Documentation in MSDN Library&lt;/a&gt; &lt;/p&gt;&lt;p class="Text" style="MARGIN-LEFT: 0.5in; TEXT-INDENT: -0.25in; LINE-HEIGHT: normal"&gt;&lt;span style="font-family:Symbol;"&gt;·&lt;span style="FONT: 7pt 'Times New Roman'; font-size-adjust: none; font-stretch: normalfont-size:100%;" &gt; &lt;/span&gt;&lt;/span&gt;Source code: &lt;a id="ctl00_rs1_mainContentContainer_ctl71" onclick="javascript:Track('ctl00_rs1_mainContentContainer_ctl00ctl00_rs1_mainContentContainer_ctl71',this);" href="http://go.microsoft.com/fwlink/?LinkId=123025"&gt;Microsoft SQL Server 2005 Driver for PHP in CodePlex&lt;/a&gt; &lt;/p&gt;&lt;p class="Text" style="LINE-HEIGHT: normal"&gt;&lt;/p&gt;&lt;h2 style="MARGIN-LEFT: 0in"&gt;&lt;a name="_Toc204499749"&gt;&lt;span style="font-size:100%;"&gt;Conclusion&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p class="Text"&gt;The SQL Server 2005 Driver for PHP provides fast and reliable&lt;br /&gt;access to SQL Server data using PHP. The driver leverages both Microsoft and&lt;br /&gt;PHP technologies (such as Windows Authentication, ODBC connection pooling, and PHP&lt;br /&gt;streams) to enable the development of rich PHP Web applications.&lt;/p&gt;&lt;p class="Text"&gt;&lt;a name="_slEnd"&gt;&lt;!----&gt;&lt;/a&gt;&lt;/p&gt;&lt;p class="MsoNormal"&gt;&lt;b&gt;&lt;span style="LINE-HEIGHT: 115%;font-family:'Verdana','sans-serif';color:black;"  &gt;For more information:&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;&lt;p class="MsoNormal"&gt;&lt;span style="LINE-HEIGHT: 115%;font-family:'Verdana','sans-serif';color:black;"  &gt;SQL Server Web site: &lt;/span&gt;&lt;a id="ctl00_rs1_mainContentContainer_ctl72" onclick="javascript:Track('ctl00_rs1_mainContentContainer_ctl00ctl00_rs1_mainContentContainer_ctl72',this);" href="http://www.microsoft.com/sqlserver/"&gt;&lt;span style="LINE-HEIGHT: 115%;font-family:'Verdana','sans-serif';" &gt;http://www.microsoft.com/sqlserver/&lt;/span&gt;&lt;/a&gt;&lt;span style="LINE-HEIGHT: 115%;font-family:'Verdana','sans-serif';color:black;"  &gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="LINE-HEIGHT: 115%;font-family:'Verdana','sans-serif';color:black;"  &gt;SQL Server TechCenter: &lt;/span&gt;&lt;a id="ctl00_rs1_mainContentContainer_ctl73" onclick="javascript:Track('ctl00_rs1_mainContentContainer_ctl00ctl00_rs1_mainContentContainer_ctl73',this);" href="http://technet.microsoft.com/en-us/sqlserver/"&gt;&lt;span style="LINE-HEIGHT: 115%;font-family:'Verdana','sans-serif';" &gt;http://technet.microsoft.com/en-us/sqlserver/&lt;/span&gt;&lt;/a&gt;&lt;span style="LINE-HEIGHT: 115%;font-family:'Verdana','sans-serif';color:black;"  &gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="LINE-HEIGHT: 115%;font-family:'Verdana','sans-serif';color:black;"  &gt;SQL Server DevCenter: &lt;/span&gt;&lt;a id="ctl00_rs1_mainContentContainer_ctl74" onclick="javascript:Track('ctl00_rs1_mainContentContainer_ctl00ctl00_rs1_mainContentContainer_ctl74',this);" href="http://msdn.microsoft.com/en-us/sqlserver/"&gt;&lt;span style="LINE-HEIGHT: 115%;font-family:'Verdana','sans-serif';" &gt;http://msdn.microsoft.com/en-us/sqlserver/&lt;/span&gt;&lt;/a&gt;&lt;span style="LINE-HEIGHT: 115%;font-family:'Verdana','sans-serif';color:black;"  &gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="LINE-HEIGHT: 115%;font-family:'Verdana','sans-serif';color:black;"  &gt;Data Platform DevCenter: &lt;/span&gt;&lt;a id="ctl00_rs1_mainContentContainer_ctl75" onclick="javascript:Track('ctl00_rs1_mainContentContainer_ctl00ctl00_rs1_mainContentContainer_ctl75',this);" href="http://msdn.microsoft.com/en-us/data/"&gt;&lt;span style="LINE-HEIGHT: 115%;font-family:'Verdana','sans-serif';" &gt;http://msdn.microsoft.com/en-us/data/&lt;/span&gt;&lt;/a&gt;&lt;span style="LINE-HEIGHT: 115%;font-family:'Verdana','sans-serif';color:black;"  &gt; &lt;/span&gt;&lt;/p&gt;&lt;p class="MsoNormal"&gt;&lt;span style="LINE-HEIGHT: 115%;font-family:'Verdana','sans-serif';color:black;"  &gt;&lt;/span&gt;&lt;/p&gt;&lt;p class="MsoNormal"&gt;&lt;span style="LINE-HEIGHT: 115%;font-family:'Verdana','sans-serif';color:black;"  &gt;&lt;/span&gt;&lt;/p&gt;&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7516218764458010868-5555256294477773014?l=all-about-database.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://all-about-database.blogspot.com/feeds/5555256294477773014/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7516218764458010868&amp;postID=5555256294477773014' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7516218764458010868/posts/default/5555256294477773014'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7516218764458010868/posts/default/5555256294477773014'/><link rel='alternate' type='text/html' href='http://all-about-database.blogspot.com/2008/09/accessing-sql-server-databases-with-php.html' title='Accessing SQL Server Databases with PHP'/><author><name>firman.arrow</name><uri>http://www.blogger.com/profile/14656110271655711161</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7516218764458010868.post-9203992657244386408</id><published>2008-09-13T01:35:00.000-07:00</published><updated>2008-09-13T01:59:04.146-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='SQL Server 2008'/><category scheme='http://www.blogger.com/atom/ns#' term='more'/><category scheme='http://www.blogger.com/atom/ns#' term='Data Ware House'/><category scheme='http://www.blogger.com/atom/ns#' term='SQL Server'/><title type='text'>Best Practices for Data Warehousing with SQL Server 2008</title><content type='html'>&lt;div class="Section1"&gt;&lt;p class="Text"&gt;&lt;b&gt;Writers:&lt;/b&gt; Mark Whitehorn, Solid Quality Mentors; Keith Burns, Microsoft&lt;br /&gt;&lt;b&gt;&lt;span style="color:windowtext;"&gt;Technical Reviewer:&lt;/span&gt;&lt;/b&gt;&lt;span style="color:windowtext;"&gt; Eric N. Hanson, Microsoft&lt;br /&gt;&lt;/span&gt;&lt;b&gt;Published:&lt;/b&gt; July 2008&lt;br /&gt;&lt;b&gt;Applies to:&lt;/b&gt; SQL Server 2008 &lt;/p&gt;&lt;p class="Text"&gt;&lt;b&gt;Summary:&lt;/b&gt; There is considerable evidence&lt;br /&gt;that successful data warehousing projects often produce a very high return on&lt;br /&gt;investment. Over the years a great deal of information has been collected about&lt;br /&gt;the factors that lead to a successful implementation versus an unsuccessful one.&lt;br /&gt;These are encapsulated here into a set of best practices, which are presented&lt;br /&gt;with particular reference to the features in SQL Server 2008. The&lt;br /&gt;application of best practices to a data warehouse project is one of the best&lt;br /&gt;investments you can make toward the establishment of a successful Business&lt;br /&gt;Intelligence infrastructure.&lt;/p&gt;&lt;h4 style="MARGIN-LEFT: 0in"&gt;&lt;a name="_Toc198032298"&gt;&lt;!----&gt;&lt;/a&gt;&lt;a name="_Toc95395435"&gt;Introduction&lt;/a&gt;&lt;/h4&gt;&lt;p class="Text"&gt;Microsoft SQL Server 2008 represents an excellent choice for the&lt;br /&gt;construction and maintenance of data warehouses in enterprises of all sizes. &lt;/p&gt;&lt;p class="Text"&gt;The term Business Intelligence (BI) describes the process of&lt;br /&gt;extracting information from data. The operational data in most enterprises is&lt;br /&gt;held in transaction-based systems with specific functions (HR, Sales, Finance,&lt;br /&gt;and so on). Frequently the information requested by decision makers within the&lt;br /&gt;enterprise requires data from several of the operational systems. Indeed, the&lt;br /&gt;more general the question, such as “What is our current profit?” the more&lt;br /&gt;operational systems are likely to be involved in providing data. &lt;/p&gt;&lt;p class="Text"&gt;An integral part of any BI system is the data warehouse—a central&lt;br /&gt;repository of data that is regularly refreshed from the source systems. The new&lt;br /&gt;data is transferred at regular intervals (often nightly) by extract, transform,&lt;br /&gt;and load (ETL) processes.&lt;/p&gt;&lt;span class="fullpost"&gt;&lt;p class="Text"&gt;Typically the data in the data warehouse is structured as a star&lt;br /&gt;schema [Kim08] although it may also be structured as normalized relational data&lt;br /&gt;[Inmon05] or as a hybrid between the two. No matter which structure is chosen,&lt;br /&gt;after the new data has been loaded into the data warehouse, many BI systems copy&lt;br /&gt;subsets of the data to function-specific data marts where the data is typically&lt;br /&gt;structured as a multi-dimensional OLAP cube as shown in Figure 1.&lt;/p&gt;&lt;p class="Figure"&gt;&lt;img height="438" alt="WPFigure1.gif" src="http://i.technet.microsoft.com/Cc719165.BestPractDWSQL2008Fig01%28en-us,SQL.100%29.jpg" width="584" /&gt;&lt;/p&gt;&lt;p class="Label"&gt;&lt;b&gt;Figure 1:&lt;/b&gt; Overall plan of a data warehouse&lt;/p&gt;&lt;p class="TableSpacingAfter"&gt;Data warehouses have been built in one form or another for over 20 years.&lt;br /&gt;Early in their history it became apparent that building a successful data&lt;br /&gt;warehouse is not a trivial undertaking. The IDC report from 1996 [IDC96] is a&lt;br /&gt;classic study of the state of data warehousing at the time. Paradoxically, it&lt;br /&gt;was used by both supporters and detractors of data warehousing. &lt;/p&gt;&lt;p class="Text"&gt;The supporters claimed that it proved how effective data warehousing&lt;br /&gt;is, citing that for the 62 projects studied, the mean return on investment (ROI)&lt;br /&gt;over three years was just over 400 percent. Fifteen of those projects (25&lt;br /&gt;percent) showed a ROI of more than 600 percent.&lt;/p&gt;&lt;p class="Text"&gt;The detractors maintained that the report was a searing&lt;br /&gt;indictment of current data warehouse practices because of 45 projects&lt;br /&gt;(with outliers discounted) 34 percent failed to return even the cost of&lt;br /&gt;investment after five years. A warehouse that has shown no return in that&lt;br /&gt;length of time is not a good investment.&lt;/p&gt;&lt;p class="Text"&gt;Both sets of figures are accurate and, taken together, reflect the&lt;br /&gt;overall findings of the paper itself which says &lt;i&gt;“One of the more interesting&lt;br /&gt;stories is found in the range of results. While the 45&lt;/i&gt; &lt;i&gt;organizations&lt;br /&gt;included in the summary analysis reported ROI results between 3% and 1,838%,&lt;br /&gt;the total range varied from as low as – 1,857% to as high as 16,000%!”&lt;/i&gt;&lt;/p&gt;&lt;p class="Text"&gt;Worryingly, the trend towards failure for data warehouse projects&lt;br /&gt;continues today: some data warehouses show a huge ROI, others clearly fail. In&lt;br /&gt;a report some nine years later (2005), Gartner predicted that &lt;i&gt;“More than 50 percent&lt;br /&gt;of data warehouse projects will have limited acceptance or will be failures through&lt;br /&gt;2007”&lt;/i&gt; (&lt;a id="ctl00_rs1_mainContentContainer_ctl01" onclick="javascript:Track('ctl00_rs1_mainContentContainer_ctl00ctl00_rs1_mainContentContainer_ctl01',this);" href="http://www.gartner.com/it/page.jsp?id=492112"&gt;Gartner press&lt;br /&gt;release&lt;/a&gt; [Gart07]).&lt;/p&gt;&lt;p class="Text"&gt;Does this mean that we have learned nothing in the intervening&lt;br /&gt;time? No, we have learned a great deal about how to create successful data&lt;br /&gt;warehouses and a set of best practices has evolved. The problem seems to be&lt;br /&gt;that not everyone in the field is aware of those practices. &lt;/p&gt;&lt;p class="Text"&gt;In this paper we cover some of the most important data&lt;br /&gt;warehousing features in SQL Server 2008 and outline best practices for&lt;br /&gt;using them effectively. In addition, we cover some of the more general best&lt;br /&gt;practices for creating a successful data warehouse project. Following best&lt;br /&gt;practices alone cannot, of course, guarantee that your data warehouse project&lt;br /&gt;will succeed; but it will improve its chances immeasurably. And it is&lt;br /&gt;undeniably true that applying best practices is the most cost-effective&lt;br /&gt;investment you can make in a data warehouse.&lt;b&gt; &lt;/b&gt;&lt;/p&gt;&lt;p class="Text"&gt;A companion paper [Han08] discusses how to scale up your data&lt;br /&gt;warehouse with SQL Server 2008. This paper focuses on planning, designing,&lt;br /&gt;modeling, and functional development of your data warehouse infrastructure. See&lt;br /&gt;the companion paper for more detail on performance and scale issues associated&lt;br /&gt;with data warehouse configuration, querying, and management.&lt;/p&gt;&lt;h4 style="MARGIN-LEFT: 0in"&gt;&lt;a name="_Toc198032299"&gt;Benefits of Using Microsoft&lt;br /&gt;Products for&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Data Warehousing and Business Intelligence&lt;/a&gt;&lt;/h4&gt;&lt;p class="Text"&gt;BI systems are, of necessity, complex. The source data is held in&lt;br /&gt;an array of disparate operational applications and databases. A BI system must turn&lt;br /&gt;these nonhomogeneous sets into a cohesive, accurate, and timely set of useful&lt;br /&gt;information.&lt;/p&gt;&lt;p class="Text"&gt;Several different architectures can be successfully employed for&lt;br /&gt;data warehouses; however, most involve:&lt;/p&gt;&lt;p class="BulletedList1"&gt;&lt;span style="font-family:Symbol;"&gt;·&lt;span style="FONT: 7pt 'Times New Roman'; font-size-adjust: none; font-stretch: normal"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;Extracting data from source systems, transforming it, and then loading&lt;br /&gt;it into a data warehouse &lt;/p&gt;&lt;p class="BulletedList1"&gt;&lt;span style="font-family:Symbol;"&gt;·&lt;span style="FONT: 7pt 'Times New Roman'; font-size-adjust: none; font-stretch: normal"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;Structuring the data in the warehouse as either third normal form&lt;br /&gt;tables or in a star/snowflake schema that is not normalized&lt;/p&gt;&lt;p class="BulletedList1"&gt;&lt;span style="font-family:Symbol;"&gt;·&lt;span style="FONT: 7pt 'Times New Roman'; font-size-adjust: none; font-stretch: normal"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;Moving the data into data marts, where it is often managed by a&lt;br /&gt;multidimensional engine&lt;/p&gt;&lt;p class="BulletedList1"&gt;&lt;span style="font-family:Symbol;"&gt;·&lt;span style="FONT: 7pt 'Times New Roman'; font-size-adjust: none; font-stretch: normal"&gt;&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;Reporting in its broadest sense, which takes place from data in&lt;br /&gt;the warehouse and/or the data marts: reporting can take the form of everything&lt;br /&gt;from printed output and Microsoft Office Excel&lt;span class="Trademark"&gt;&lt;span style="font-size:8;"&gt;®&lt;/span&gt;&lt;/span&gt; spreadsheets through rapid&lt;br /&gt;multidimensional analysis to data mining. &lt;/p&gt;&lt;p class="Text"&gt;SQL Server 2008 provides all the tools necessary to perform these&lt;br /&gt;tasks [MMD07].&lt;/p&gt;&lt;p class="BulletedList1"&gt;&lt;span style="font-family:Symbol;"&gt;·&lt;span style="FONT: 7pt 'Times New Roman'; font-size-adjust: none; font-stretch: normal"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;SQL Server Integration Services (SSIS) allows the creation and&lt;br /&gt;maintenance of ETL routines.&lt;/p&gt;&lt;p class="BulletedList1"&gt;&lt;span style="font-family:Symbol;"&gt;·&lt;span style="FONT: 7pt 'Times New Roman'; font-size-adjust: none; font-stretch: normal"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;If you use SQL Server as a data source, the Change Data&lt;br /&gt;Capture feature simplifies the extraction process enormously.&lt;/p&gt;&lt;p class="BulletedList1"&gt;&lt;span style="font-family:Symbol;"&gt;·&lt;span style="FONT: 7pt 'Times New Roman'; font-size-adjust: none; font-stretch: normal"&gt;&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;The SQL Server database engine holds and manages the tables that&lt;br /&gt;make up your data warehouse.&lt;/p&gt;&lt;p class="BulletedList1"&gt;&lt;span style="font-family:Symbol;"&gt;·&lt;span style="FONT: 7pt 'Times New Roman'; font-size-adjust: none; font-stretch: normal"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;SQL Server Analysis Services (SSAS) manages an enhanced&lt;br /&gt;multidimensional form of the data, optimized for fast reporting and ease of&lt;br /&gt;understanding.&lt;/p&gt;&lt;p class="BulletedList1"&gt;&lt;span style="font-family:Symbol;"&gt;·&lt;span style="FONT: 7pt 'Times New Roman'; font-size-adjust: none; font-stretch: normal"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;SQL Server Reporting Services (SSRS) has a wide range of&lt;br /&gt;reporting abilities, including excellent integration with Excel and Microsoft&lt;br /&gt;Office Word. PerformancePoint Server&lt;span class="Trademark"&gt;&lt;span style="font-size:8;"&gt;™&lt;/span&gt;&lt;/span&gt; makes it easy to visualize&lt;br /&gt;multidimensional data.&lt;/p&gt;&lt;p class="Text"&gt;Moreover, Microsoft Office SharePoint Server&lt;span class="Trademark"&gt;&lt;span style="font-size:8;"&gt;®&lt;/span&gt;&lt;/span&gt; (MOSS) 2007&lt;br /&gt;and Microsoft Office 2007 provide an integrated, easy-to-use, end-user&lt;br /&gt;environment, enabling you to distribute analysis and reports derived from your&lt;br /&gt;data warehouse data throughout your organization. SharePoint can be used to&lt;br /&gt;build BI portal and dashboard solutions. For example, you can build a score&lt;br /&gt;card application on top of SharePoint that enables employees to get a custom&lt;br /&gt;display of the metrics and Key Performance Indicators (KPIs) depending on their&lt;br /&gt;job role.&lt;/p&gt;&lt;p class="Text"&gt;As you would expect from a complete end-to-end solution, the&lt;br /&gt;level of integration between the components is extremely high. Microsoft Visual&lt;br /&gt;Studio&lt;span class="Trademark"&gt;&lt;span style="font-size:8;"&gt;®&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;provides a consistent UI from which to drive both SQL Server and Analysis&lt;br /&gt;Services and, of course, it can be used to develop BI applications in a wide&lt;br /&gt;range of languages (Visual C#&lt;span class="Trademark"&gt;&lt;span style="font-size:8;"&gt;®&lt;/span&gt;&lt;/span&gt;, Visual C++&lt;span class="Trademark"&gt;&lt;span style="font-size:8;"&gt;®&lt;/span&gt;&lt;/span&gt;, Visual Basic.Net&lt;span class="Trademark"&gt;&lt;span style="font-size:8;"&gt;®&lt;/span&gt;&lt;/span&gt;, and so on).&lt;/p&gt;&lt;p class="Text"&gt;SQL Server is legendary for its low total cost of ownership (TCO)&lt;br /&gt;in BI solutions. One reason is that the tools you need come in the box at no&lt;br /&gt;extra cost—other vendors charge (and significantly) for ETL tools,&lt;br /&gt;multidimensional engines, and so on. Another factor is the Microsoft renowned&lt;br /&gt;focus on ease of use which, when combined with the integrated development&lt;br /&gt;environment that Visual Studio brings, significantly reduces training times and&lt;br /&gt;development costs. &lt;/p&gt;&lt;h5 style="MARGIN-LEFT: 0in"&gt;&lt;a name="_Toc198032300"&gt;Best Practices: Creating Value&lt;br /&gt;for Your Business&lt;/a&gt;&lt;/h5&gt;&lt;p class="Text"&gt;The main focus of this paper is on technical best practices. However&lt;br /&gt;experience has shown that many data warehouse and BI projects fail not for&lt;br /&gt;technical reasons, but because of the three Ps—personalities, power struggles,&lt;br /&gt;and politics. &lt;/p&gt;&lt;h6 style="MARGIN-LEFT: 0in"&gt;&lt;a name="_Toc198032301"&gt;Find a sponsor&lt;/a&gt;&lt;/h6&gt;&lt;p class="Text"&gt;Your sponsor should be someone at the highest level within the&lt;br /&gt;organization, someone with the will and the authority to give the project the&lt;br /&gt;strong political backing it will need. Why? &lt;/p&gt;&lt;p class="Text"&gt;Some business people have learned that the control of information&lt;br /&gt;means power. They may see the warehouse as an active threat to their power&lt;br /&gt;because it makes information freely available. For political reasons those&lt;br /&gt;people may profess full support for the BI system, but in practice be&lt;br /&gt;effectively obstructive. &lt;/p&gt;&lt;p class="Text"&gt;The BI team may not have the necessary influence to overcome such&lt;br /&gt;obstacles or the inertia that may come from the higher management echelons:&lt;br /&gt;that is the job of the sponsor. &lt;/p&gt;&lt;h6 style="MARGIN-LEFT: 0in"&gt;&lt;a name="_Toc198032302"&gt;Get the architecture right&lt;br /&gt;at the start&lt;/a&gt;&lt;/h6&gt;&lt;p class="Text"&gt;The overall architecture of the entire BI system must be&lt;br /&gt;carefully planned in the early stages. An example of part of this process is&lt;br /&gt;deciding whether to align the structure with the design principles of Ralph Kimball&lt;br /&gt;or Bill Inmon (see &lt;a href="http://www.blogger.com/post-create.g?blogID=7516218764458010868#_Toc198032372"&gt;References&lt;/a&gt;). &lt;/p&gt;&lt;h6 style="MARGIN-LEFT: 0in"&gt;&lt;a name="_Toc198032303"&gt;Develop a Proof of Concept&lt;/a&gt;&lt;/h6&gt;&lt;p class="Text"&gt;Deciding upon a Proof of Concept project is an excellent way to&lt;br /&gt;gain support and influence people. This might involve the creation of an OLAP&lt;br /&gt;cube and the use of visualization software for one (or several) business units.&lt;br /&gt;The project can be used to show business people what they can expect from the&lt;br /&gt;new system and also to train the BI team. Choose a project with a small and&lt;br /&gt;well-defined scope that is relatively easy to achieve. Performing a Proof of&lt;br /&gt;Concept requires an investment of time, effort, and money but, by limiting the&lt;br /&gt;scope, you limit the expenditure and ensure a rapid return on the investment. &lt;/p&gt;&lt;h6 style="MARGIN-LEFT: 0in"&gt;&lt;a name="_Toc198032304"&gt;Select Proof of Concept&lt;br /&gt;projects on the basis of rapid ROI&lt;/a&gt;&lt;/h6&gt;&lt;p class="Text"&gt;When choosing a Proof of Concept project, it can be a useful&lt;br /&gt;exercise to talk to ten or so business units about their needs. Score their&lt;br /&gt;requirements for difficulty and for ROI. Experience suggests that there is&lt;br /&gt;often little correlation between the cost of developing a cube and the ROI it&lt;br /&gt;can generate, so this exercise should identify several low-effort, high-return&lt;br /&gt;projects, each of which should be excellent candidates for Proof of Concept projects.&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;&lt;h6 style="MARGIN-LEFT: 0in"&gt;&lt;a name="_Toc198032305"&gt;Incrementally deliver high&lt;br /&gt;value projects&lt;/a&gt;&lt;/h6&gt;&lt;p class="Text"&gt;By building the most profitable solutions first, a good ROI can&lt;br /&gt;be ensured for the entire data warehouse project after the initial stages. If&lt;br /&gt;the first increment costs, say $250,000 and the ROI is $1 million per&lt;br /&gt;annum, after the first three months of operation the initial outlay will have&lt;br /&gt;been recouped. &lt;/p&gt;&lt;p class="Text"&gt;The ease of use and high level of integration of between&lt;br /&gt;Microsoft BI components are valuable assets that help you deliver results in a&lt;br /&gt;timely fashion; this helps you to rapidly build support from your business&lt;br /&gt;users and sponsors. The value remains apparent as you build on the Proof of&lt;br /&gt;Concept project and start to deliver benefits across the organization. &lt;/p&gt;&lt;h4 style="MARGIN-LEFT: 0in"&gt;&lt;a name="_Designing_your_DW/BI"&gt;&lt;!----&gt;&lt;/a&gt;&lt;b&gt;&lt;/b&gt;&lt;a name="_Toc198032306"&gt;Designing Your Data Warehouse/BI solution&lt;/a&gt;&lt;/h4&gt;&lt;p class="Text"&gt;In this section two areas of best practices are discussed. The first&lt;br /&gt;are general design guidelines that should be considered at the start of the&lt;br /&gt;project. These are followed by guidance on specifying hardware. &lt;/p&gt;&lt;h5 style="MARGIN-LEFT: 0in"&gt;&lt;a name="_Toc198032307"&gt;Best Practices: Initial&lt;br /&gt;Design&lt;/a&gt;&lt;/h5&gt;&lt;h6 style="MARGIN-LEFT: 0in"&gt;&lt;a name="_Toc198032308"&gt;Keep the design in line&lt;br /&gt;with the analytical requirements of the users&lt;/a&gt;&lt;/h6&gt;&lt;p class="Text"&gt;As soon as we start to discuss good data warehouse design we must&lt;br /&gt;introduce both Bill Inmon [Inmon05] and Ralph Kimball [Kim08]. Both contributed&lt;br /&gt;hugely to our understanding of data warehousing and both write about many&lt;br /&gt;aspects of warehouse design. Their views are often characterized in the following&lt;br /&gt;ways:&lt;/p&gt;&lt;p class="BulletedList1"&gt;&lt;span style="font-family:Symbol;"&gt;·&lt;span style="FONT: 7pt 'Times New Roman'; font-size-adjust: none; font-stretch: normal"&gt;&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;Inmon favors a normalized data warehouse.&lt;/p&gt;&lt;p class="BulletedList1"&gt;&lt;span style="font-family:Symbol;"&gt;·&lt;span style="FONT: 7pt 'Times New Roman'; font-size-adjust: none; font-stretch: normal"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;Kimball favors a dimensional data warehouse.&lt;/p&gt;&lt;p class="Text"&gt;Warehouses with these structures are often referred to as &lt;i&gt;Inmon&lt;br /&gt;warehouses&lt;/i&gt; or &lt;i&gt;Kimball warehouses&lt;/i&gt;. Microsoft has no bias either way&lt;br /&gt;and SQL Server is perfectly capable of supporting either design. We&lt;br /&gt;observe that the majority of our customers favor a dimensional warehouse. In&lt;br /&gt;response, we introduced star schema optimization to speed up queries against&lt;br /&gt;fact and dimension tables.&lt;/p&gt;&lt;p class="Text"&gt;The computer industry has understood how to design transactional&lt;br /&gt;systems since the 1970s. Start with the user’s requirements, which we can&lt;br /&gt;describe as the &lt;i&gt;User model&lt;/i&gt;. Formalize those into a &lt;i&gt;Logical model&lt;/i&gt; that&lt;br /&gt;users can agree to and approve. Add the technical detail and transform that&lt;br /&gt;into a &lt;i&gt;Physical model&lt;/i&gt;. After that, it is just a matter of implementation…&lt;/p&gt;&lt;p class="Text"&gt;Designing a data warehouse should follow the same principles and&lt;br /&gt;focus the design process on the requirements of the users. The only difference&lt;br /&gt;is that, for a data warehouse, those requirements are not operational but analytical.&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;&lt;p class="Text"&gt;In a large enterprise, users tend to split into many groups, each&lt;br /&gt;with different analytical requirements. Users in each group often articulate&lt;br /&gt;the analysis they need (the User model of analysis) in terms of graphs, grids&lt;br /&gt;of data (worksheets), and printed reports. These are visually very different&lt;br /&gt;but all essentially present numerical measures filtered and grouped by the&lt;br /&gt;members of one or more dimensions. &lt;/p&gt;&lt;p class="Text"&gt;So we can capture the analytical requirements of a group simply by&lt;br /&gt;formalizing the measures and dimensions that they use. As shown in Figure 2&lt;br /&gt;these can be captured together with the relevant hierarchical information in a sun&lt;br /&gt;model, which is the Logical model of the analytical requirements. &lt;/p&gt;&lt;p class="Figure"&gt;&lt;img height="446" alt="WPFigure2.gif" src="http://i.technet.microsoft.com/Cc719165.BestPractDWSQL2008Fig02%28en-us,SQL.100%29.jpg" width="595" border="0" /&gt;&lt;/p&gt;&lt;p class="Label"&gt;&lt;b&gt;Figure 2:&lt;/b&gt; A sun model used to capture the analytical&lt;br /&gt;requirements of users in terms of measures, dimensions, and hierarchical&lt;br /&gt;structure. This is the Logical model derived from the User model.&lt;/p&gt;&lt;p class="Text"&gt;Once these analytical requirements have been successfully&lt;br /&gt;captured, we can add the technical detail. This transforms the Logical model&lt;br /&gt;into a Physical one, the &lt;i&gt;star schema&lt;/i&gt;, illustrated in Figure 3.&lt;/p&gt;&lt;p class="Figure"&gt;&lt;img id="Picture 1" height="631" src="http://i.technet.microsoft.com/Cc719165.BestPractDWSQL2008Fig03%28en-us,SQL.100%29.jpg" width="594" border="0" /&gt;&lt;/p&gt;&lt;p class="Label"&gt;&lt;b&gt;Figure 3:&lt;/b&gt; A representation of a star schema, which is an example&lt;br /&gt;of a Physical model of analytical requirements&lt;/p&gt;&lt;p class="Text"&gt;The star schemas may ultimately be implemented as a set of&lt;br /&gt;dimensional and fact tables in the data warehouse and/or as cubes (ROLAP, HOLAP,&lt;br /&gt;or MOLAP), which may be housed in multiple data marts.&lt;/p&gt;&lt;p class="Text"&gt;To design the ETL system to deliver the data for this structure,&lt;br /&gt;we must understand the nature of the data in the source systems. &lt;/p&gt;&lt;p class="Text"&gt;To deliver the analytics that users outlined in their&lt;br /&gt;requirements, we must find the appropriate data in the source systems and&lt;br /&gt;transform it appropriately. In a perfect world, the source systems come with excellent&lt;br /&gt;documentation, which includes meaningful table and column names. In addition,&lt;br /&gt;the source applications are perfectly designed and only allow data that falls&lt;br /&gt;within the appropriate domain to be entered. Back on planet Earth, source&lt;br /&gt;systems rarely meet these exacting standards. &lt;/p&gt;&lt;p class="Text"&gt;You may know that users need to analyze sales by the gender of&lt;br /&gt;the customer. Unfortunately, the source system is completely undocumented but&lt;br /&gt;you find a column named Gen in a table called CUS_WeX4. Further investigation&lt;br /&gt;shows that the column may store data relating to customer gender. Suppose that&lt;br /&gt;you can then determine that it contains &lt;span style="font-family:'Arial','sans-serif';"&gt;682370&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;rows and that the distribution of the data is:&lt;/p&gt;&lt;p class="TableSpacing"&gt;&lt;/p&gt;&lt;table class="MsoNormalTable" style="BORDER-RIGHT: medium none; BORDER-TOP: medium none; MARGIN-LEFT: 4.8pt; BORDER-LEFT: medium none; WIDTH: 96pt; BORDER-BOTTOM: medium none; BORDER-COLLAPSE: collapse" width="128" border="1"&gt;&lt;tbody&gt;&lt;tr style="HEIGHT: 15.6pt"&gt;&lt;td style="BORDER-RIGHT: windowtext 1pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: windowtext 1pt solid; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0in; BORDER-LEFT: windowtext 1pt solid; WIDTH: 48pt; PADDING-TOP: 0in; BORDER-BOTTOM: windowtext 1pt solid; HEIGHT: 15.6pt" valign="bottom" width="64"&gt;&lt;p class="Text"&gt;M&lt;/p&gt;&lt;/td&gt;&lt;td style="BORDER-RIGHT: windowtext 1pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: windowtext 1pt solid; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0in; BORDER-LEFT: medium none; WIDTH: 48pt; PADDING-TOP: 0in; BORDER-BOTTOM: windowtext 1pt solid; HEIGHT: 15.6pt" valign="bottom" width="64"&gt;&lt;p class="Text"&gt;&lt;span style="font-family:'Arial','sans-serif';"&gt;234543&lt;/span&gt;&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr style="HEIGHT: 15.6pt"&gt;&lt;td style="BORDER-RIGHT: 1pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0in; BORDER-LEFT: 1pt solid; WIDTH: 48pt; PADDING-TOP: 0in; BORDER-BOTTOM: 1pt solid; HEIGHT: 15.6pt" valign="bottom" width="64"&gt;&lt;p class="Text"&gt;F&lt;/p&gt;&lt;/td&gt;&lt;td style="BORDER-RIGHT: 1pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0in; BORDER-LEFT: medium none; WIDTH: 48pt; PADDING-TOP: 0in; BORDER-BOTTOM: 1pt solid; HEIGHT: 15.6pt" valign="bottom" width="64"&gt;&lt;p class="Text"&gt;&lt;span style="font-family:'Arial','sans-serif';"&gt;342322&lt;/span&gt;&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr style="HEIGHT: 15.6pt"&gt;&lt;td style="BORDER-RIGHT: 1pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0in; BORDER-LEFT: 1pt solid; WIDTH: 48pt; PADDING-TOP: 0in; BORDER-BOTTOM: 1pt solid; HEIGHT: 15.6pt" valign="bottom" width="64"&gt;&lt;p class="Text"&gt;Mail&lt;/p&gt;&lt;/td&gt;&lt;td style="BORDER-RIGHT: 1pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0in; BORDER-LEFT: medium none; WIDTH: 48pt; PADDING-TOP: 0in; BORDER-BOTTOM: 1pt solid; HEIGHT: 15.6pt" valign="bottom" width="64"&gt;&lt;p class="Text"&gt;&lt;span style="font-family:'Arial','sans-serif';"&gt;5&lt;/span&gt;&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr style="HEIGHT: 15.6pt"&gt;&lt;td style="BORDER-RIGHT: 1pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0in; BORDER-LEFT: 1pt solid; WIDTH: 48pt; PADDING-TOP: 0in; BORDER-BOTTOM: 1pt solid; HEIGHT: 15.6pt" valign="bottom" width="64"&gt;&lt;p class="Text"&gt;Femal&lt;/p&gt;&lt;/td&gt;&lt;td style="BORDER-RIGHT: 1pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0in; BORDER-LEFT: medium none; WIDTH: 48pt; PADDING-TOP: 0in; BORDER-BOTTOM: 1pt solid; HEIGHT: 15.6pt" valign="bottom" width="64"&gt;&lt;p class="Text"&gt;&lt;span style="font-family:'Arial','sans-serif';"&gt;9&lt;/span&gt;&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr style="HEIGHT: 15.6pt"&gt;&lt;td style="BORDER-RIGHT: 1pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0in; BORDER-LEFT: 1pt solid; WIDTH: 48pt; PADDING-TOP: 0in; BORDER-BOTTOM: 1pt solid; HEIGHT: 15.6pt" valign="bottom" width="64"&gt;&lt;p class="Text"&gt;Female&lt;/p&gt;&lt;/td&gt;&lt;td style="BORDER-RIGHT: 1pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0in; BORDER-LEFT: medium none; WIDTH: 48pt; PADDING-TOP: 0in; BORDER-BOTTOM: 1pt solid; HEIGHT: 15.6pt" valign="bottom" width="64"&gt;&lt;p class="Text"&gt;&lt;span style="font-family:'Arial','sans-serif';"&gt;4345&lt;/span&gt;&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr style="HEIGHT: 15.6pt"&gt;&lt;td style="BORDER-RIGHT: 1pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0in; BORDER-LEFT: 1pt solid; WIDTH: 48pt; PADDING-TOP: 0in; BORDER-BOTTOM: 1pt solid; HEIGHT: 15.6pt" valign="bottom" width="64"&gt;&lt;p class="Text"&gt;Yes&lt;/p&gt;&lt;/td&gt;&lt;td style="BORDER-RIGHT: 1pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0in; BORDER-LEFT: medium none; WIDTH: 48pt; PADDING-TOP: 0in; BORDER-BOTTOM: 1pt solid; HEIGHT: 15.6pt" valign="bottom" width="64"&gt;&lt;p class="Text"&gt;&lt;span style="font-family:'Arial','sans-serif';"&gt;43456&lt;/span&gt;&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr style="HEIGHT: 15.6pt"&gt;&lt;td style="BORDER-RIGHT: 1pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0in; BORDER-LEFT: 1pt solid; WIDTH: 48pt; PADDING-TOP: 0in; BORDER-BOTTOM: 1pt solid; HEIGHT: 15.6pt" valign="bottom" width="64"&gt;&lt;p class="Text"&gt;No&lt;/p&gt;&lt;/td&gt;&lt;td style="BORDER-RIGHT: 1pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0in; BORDER-LEFT: medium none; WIDTH: 48pt; PADDING-TOP: 0in; BORDER-BOTTOM: 1pt solid; HEIGHT: 15.6pt" valign="bottom" width="64"&gt;&lt;p class="Text"&gt;&lt;span style="font-family:'Arial','sans-serif';"&gt;54232&lt;/span&gt;&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr style="HEIGHT: 15.6pt"&gt;&lt;td style="BORDER-RIGHT: 1pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0in; BORDER-LEFT: 1pt solid; WIDTH: 48pt; PADDING-TOP: 0in; BORDER-BOTTOM: 1pt solid; HEIGHT: 15.6pt" valign="bottom" width="64"&gt;&lt;p class="Text"&gt;Male&lt;/p&gt;&lt;/td&gt;&lt;td style="BORDER-RIGHT: 1pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0in; BORDER-LEFT: medium none; WIDTH: 48pt; PADDING-TOP: 0in; BORDER-BOTTOM: 1pt solid; HEIGHT: 15.6pt" valign="bottom" width="64"&gt;&lt;p class="Text"&gt;&lt;span style="font-family:'Arial','sans-serif';"&gt;3454&lt;/span&gt;&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr style="HEIGHT: 15.6pt"&gt;&lt;td style="BORDER-RIGHT: 1pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0in; BORDER-LEFT: 1pt solid; WIDTH: 48pt; PADDING-TOP: 0in; BORDER-BOTTOM: 1pt solid; HEIGHT: 15.6pt" valign="bottom" width="64"&gt;&lt;p class="Text"&gt;Girl&lt;/p&gt;&lt;/td&gt;&lt;td style="BORDER-RIGHT: 1pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0in; BORDER-LEFT: medium none; WIDTH: 48pt; PADDING-TOP: 0in; BORDER-BOTTOM: 1pt solid; HEIGHT: 15.6pt" valign="bottom" width="64"&gt;&lt;p class="Text"&gt;&lt;span style="font-family:'Arial','sans-serif';"&gt;4&lt;/span&gt;&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;p class="TableSpacing"&gt;&lt;/p&gt;&lt;p class="Text"&gt;You now have more evidence that this is the correct column and&lt;br /&gt;you also have an idea of the challenges that lie ahead in designing the ETL&lt;br /&gt;process. &lt;/p&gt;&lt;p class="Text"&gt;In other words, knowledge of the distribution of the data is&lt;br /&gt;essential in many cases, not only to identify the appropriate columns but also&lt;br /&gt;to begin to understand (and ultimately design) the transformation process.&lt;/p&gt;&lt;h6 style="MARGIN-LEFT: 0in"&gt;&lt;a name="_Toc198032309"&gt;Use data profiling to&lt;br /&gt;examine the distribution of the data in the source systems&lt;/a&gt;&lt;/h6&gt;&lt;p class="Text"&gt;In the past, gaining an understanding of the distribution of data&lt;br /&gt;in the source systems meant running multiple queries (often GROUP BYs) against the&lt;br /&gt;source system tables to determine the domain of values in each column. That&lt;br /&gt;information could be compared to the information supplied by the domain experts&lt;br /&gt;and the defined transformations. &lt;/p&gt;&lt;p class="Text"&gt;Integration Services has a new Data Profiling task that makes the&lt;br /&gt;first part of this job much easier. You can use the information you gather by using&lt;br /&gt;the Data Profiler to define appropriate data transformation rules to ensure that&lt;br /&gt;your data warehouse contains “clean” data after ETL, which leads to more&lt;br /&gt;accurate and trusted analytical results. The Data Profiler is a data flow task&lt;br /&gt;in which you can define the profile information you need.&lt;/p&gt;&lt;p class="Text"&gt;Eight data profiles are available; five of these analyze&lt;br /&gt;individual columns:&lt;/p&gt;&lt;p class="BulletedList1"&gt;&lt;span style="font-family:Symbol;"&gt;·&lt;span style="FONT: 7pt 'Times New Roman'; font-size-adjust: none; font-stretch: normal"&gt;&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;Column Null Ratio&lt;/p&gt;&lt;p class="BulletedList1"&gt;&lt;span style="font-family:Symbol;"&gt;·&lt;span style="FONT: 7pt 'Times New Roman'; font-size-adjust: none; font-stretch: normal"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;Column Value Distribution&lt;/p&gt;&lt;p class="BulletedList1"&gt;&lt;span style="font-family:Symbol;"&gt;·&lt;span style="FONT: 7pt 'Times New Roman'; font-size-adjust: none; font-stretch: normal"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;Column Length Distribution&lt;/p&gt;&lt;p class="BulletedList1"&gt;&lt;span style="font-family:Symbol;"&gt;·&lt;span style="FONT: 7pt 'Times New Roman'; font-size-adjust: none; font-stretch: normal"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;Column Statistics&lt;/p&gt;&lt;p class="BulletedList1"&gt;&lt;span style="font-family:Symbol;"&gt;·&lt;span style="FONT: 7pt 'Times New Roman'; font-size-adjust: none; font-stretch: normal"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;Column Pattern&lt;/p&gt;&lt;p class="Text"&gt;Three analyze either multiple columns or relationships between&lt;br /&gt;tables and columns:&lt;/p&gt;&lt;p class="BulletedList1"&gt;&lt;span style="font-family:Symbol;"&gt;·&lt;span style="FONT: 7pt 'Times New Roman'; font-size-adjust: none; font-stretch: normal"&gt;&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;Candidate Key&lt;/p&gt;&lt;p class="BulletedList1"&gt;&lt;span style="font-family:Symbol;"&gt;·&lt;span style="FONT: 7pt 'Times New Roman'; font-size-adjust: none; font-stretch: normal"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;Functional Dependency&lt;/p&gt;&lt;p class="BulletedList1"&gt;&lt;span style="font-family:Symbol;"&gt;·&lt;span style="FONT: 7pt 'Times New Roman'; font-size-adjust: none; font-stretch: normal"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;Value Inclusion&lt;/p&gt;&lt;p class="Text"&gt;Multiple data profiles for several columns or column combinations&lt;br /&gt;can be computed with one Data Profiling task and the output can be directed to&lt;br /&gt;an XML file or package variable. The former is the best option for ETL design&lt;br /&gt;work. A Data Profile Viewer, shown in Figure 4, is provided as a&lt;br /&gt;standalone executable that can be used to examine the XML file and hence the&lt;br /&gt;profile of the data. &lt;/p&gt;&lt;p class="Figure"&gt;&lt;img id="Picture 2" height="445" src="http://i.technet.microsoft.com/Cc719165.BestPractDWSQL2008Fig04%28en-us,SQL.100%29.jpg" width="595" border="0" /&gt;&lt;/p&gt;&lt;p class="Label"&gt;&lt;b&gt;Figure 4:&lt;/b&gt; The Data Profile Viewer&lt;/p&gt;&lt;h6 style="MARGIN-LEFT: 0in"&gt;&lt;a name="_Toc198032310"&gt;Design from the start to&lt;br /&gt;partition large tables, particularly large fact tables&lt;/a&gt;&lt;/h6&gt;&lt;p class="Text"&gt;No matter how good the indexing and how fast the hardware, large&lt;br /&gt;tables can lead to time-consuming system management operations, such as index&lt;br /&gt;creation and removal of old data. This can be particularly apparent for large&lt;br /&gt;fact tables. So, partition the tables if they are large. In general, if a table&lt;br /&gt;is larger than 50 GB, you should partition it (see &lt;a href="http://www.blogger.com/post-create.g?blogID=7516218764458010868#_Relational_Data_Warehouse"&gt;Relational Data Warehouse Setup, Query, and&lt;br /&gt;Management&lt;/a&gt; later in this white paper).&lt;/p&gt;&lt;h6 style="MARGIN-LEFT: 0in"&gt;&lt;a name="_Toc198032311"&gt;Plan to age out old data&lt;br /&gt;right from the start&lt;/a&gt;&lt;/h6&gt;&lt;p class="Text"&gt;One of the main functions of a data warehouse is to track the&lt;br /&gt;business history of the enterprise—something that the source systems generally&lt;br /&gt;do particularly badly. Tracking history means that the data warehouse will acquire&lt;br /&gt;vast quantities of data over time. As data ages, it is accessed less frequently&lt;br /&gt;and also differently (typically as aggregations rather than the detail). It eventually&lt;br /&gt;becomes necessary to treat older data differently, perhaps by using slower,&lt;br /&gt;cheaper storage, perhaps storing only the aggregations, removing it altogether,&lt;br /&gt;or another plan. It is vital to plan for this right from the start. Partitioning&lt;br /&gt;makes this much easier (see &lt;a href="http://www.blogger.com/post-create.g?blogID=7516218764458010868#_Relational_Data_Warehouse"&gt;Relational Data&lt;br /&gt;Warehouse Setup, Query, and Management&lt;/a&gt; later in this white paper).&lt;/p&gt;&lt;h5 style="MARGIN-LEFT: 0in"&gt;&lt;a name="_Toc198032312"&gt;Best Practices: Specifying Hardware&lt;/a&gt;&lt;/h5&gt;&lt;h6 style="MARGIN-LEFT: 0in"&gt;&lt;a name="_Toc198032313"&gt;Design for maintenance&lt;br /&gt;operation performance, not just query performance&lt;/a&gt;&lt;/h6&gt;&lt;p class="Text"&gt;Specifying hardware is never easy but it is common (and good)&lt;br /&gt;practice when making hardware decisions to focus most of the attention on query&lt;br /&gt;performance for the simple reason that query performance is vital. However, it&lt;br /&gt;is also important not to overlook other considerations. &lt;/p&gt;&lt;p class="Text"&gt;Think about query performance. A very simplistic view is that it&lt;br /&gt;scales in a linear fashion with data size. Generations of DBAs have learned the&lt;br /&gt;hard way that it often does not scale in this way—double the size of the data&lt;br /&gt;and the query may take ten times as long to run—so they may plan hardware&lt;br /&gt;requirements accordingly. But this is a somewhat simplistic approach. &lt;/p&gt;&lt;p class="Text"&gt;For example, consider a query that is run against a 1‑terabyte&lt;br /&gt;table. Its performance is limited by many factors—indexing, memory, number of&lt;br /&gt;rows scanned, number of rows returned and so on. Imagine that the query uses&lt;br /&gt;indexes efficiently, scans some fixed number of rows, and returns only a few&lt;br /&gt;rows. If we run that same query against 2 terabytes of data in the same&lt;br /&gt;table and assume the indexing has been applied efficiently and that the number&lt;br /&gt;of scanned and returned rows is not significantly different, the response time is&lt;br /&gt;about the same. Certainly it will be nothing like double the time. In other&lt;br /&gt;words, query performance can sometimes scale in a nonlinear way to our&lt;br /&gt;advantage. However other factors, such as backup time, do scale linearly in the&lt;br /&gt;expected way. Backing up twice the data takes twice the resources (such as CPU&lt;br /&gt;time and I/O bandwidth), which may well affect the hardware specifications&lt;br /&gt;needed to meet your requirements. &lt;/p&gt;&lt;p class="Text"&gt;In other words, when we design large BI systems we must be&lt;br /&gt;careful to consider all the relevant factors. This does not mean that query&lt;br /&gt;performance is unimportant; it is still the most important consideration. But&lt;br /&gt;it is also a mistake to focus solely on that issue. Other factors, such as&lt;br /&gt;maintenance of the BI system (both the relational and multidimensional&lt;br /&gt;components) must be considered carefully as well.&lt;/p&gt;&lt;h6 style="MARGIN-LEFT: 0in"&gt;&lt;a name="_Toc198032314"&gt;Specify enough main memory&lt;br /&gt;so most queries never do I/O&lt;/a&gt;&lt;/h6&gt;&lt;p class="Text"&gt;According to Microsoft SQL Server Customer Advisory Team&lt;br /&gt;engineers, in most data warehouses the data is very unevenly accessed. By far&lt;br /&gt;the majority of queries access some proportion of the same 20% of the total; in&lt;br /&gt;other words, about 80% of the data is rarely accessed. This means that disk I/O&lt;br /&gt;for the warehouse overall reduces dramatically if there is enough main memory&lt;br /&gt;to hold about 20% of the total data.&lt;/p&gt;&lt;h4 style="MARGIN-LEFT: 0in"&gt;&lt;a name="_Toc198032315"&gt;ETL&lt;/a&gt;&lt;/h4&gt;&lt;p class="Text"&gt;The field of ETL (extract, transform, and load) is the most&lt;br /&gt;complex area of data warehousing [Hath07]. The data itself is often complex and&lt;br /&gt;requires a whole range of techniques and processes to be applied before it is&lt;br /&gt;loaded into the warehouse. These include merging, time/date stamping,&lt;br /&gt;de-duplication and survivorship, data type conversion, normalization and/or&lt;br /&gt;denormalization, surrogate key insertion, and general cleansing.&lt;/p&gt;&lt;h5 style="MARGIN-LEFT: 0in"&gt;&lt;a name="_Toc198032316"&gt;Best Practices: Simplify&lt;br /&gt;the ETL Process and Improve Performance&lt;/a&gt;&lt;/h5&gt;&lt;h6 style="MARGIN-LEFT: 0in"&gt;&lt;a name="_Toc198032317"&gt;&lt;!----&gt;&lt;/a&gt;&lt;a name="_Toc197686031"&gt;Use&lt;br /&gt;SSIS to simplify ETL programming&lt;/a&gt;&lt;/h6&gt;&lt;p class="Text"&gt;SSIS was designed from the ground up to simplify the process of developing&lt;br /&gt;your&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;ETL code. Its automated tools and predefined transformations and connectors&lt;br /&gt;vastly reduce the number of lines of ETL code you have to write compared to&lt;br /&gt;programming in a traditional high-level language or scripting language. It is a&lt;br /&gt;best practice to use SSIS rather than these other methods if reducing the&lt;br /&gt;amount of code for ETL and simplifying ETL job maintenance is important to you.&lt;br /&gt;&lt;/p&gt;&lt;p class="Text"&gt;SSIS provides a comprehensive set of features for building data&lt;br /&gt;warehouses, including:&lt;/p&gt;&lt;p class="BulletedList1"&gt;&lt;span style="font-family:Symbol;font-size:12;"&gt;·&lt;span style="FONT: 7pt 'Times New Roman'; font-size-adjust: none; font-stretch: normal"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;A scalable pipeline architecture that provides a multithreaded 64‑bit&lt;br /&gt;platform to transform growing data volumes in narrower batch windows,&lt;/p&gt;&lt;p class="BulletedList1"&gt;&lt;span style="font-family:Symbol;font-size:12;"&gt;·&lt;span style="FONT: 7pt 'Times New Roman'; font-size-adjust: none; font-stretch: normal"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;Connectivity to non-SQL Server RDBMs, mainframes, ERP systems and&lt;br /&gt;other heterogeneous data sources.&lt;/p&gt;&lt;p class="BulletedList1"&gt;&lt;span style="font-family:Symbol;font-size:12;"&gt;·&lt;span style="FONT: 7pt 'Times New Roman'; font-size-adjust: none; font-stretch: normal"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;A large number of complex transformations to consolidate data&lt;br /&gt;from numerous systems.&lt;/p&gt;&lt;p class="BulletedList1"&gt;&lt;span style="font-family:Symbol;font-size:12;"&gt;·&lt;span style="FONT: 7pt 'Times New Roman'; font-size-adjust: none; font-stretch: normal"&gt;&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;Advanced data cleansing transformations to reduce data&lt;br /&gt;duplication and dirty data.&lt;/p&gt;&lt;p class="BulletedList1"&gt;&lt;span style="font-family:Symbol;font-size:12;"&gt;·&lt;span style="FONT: 7pt 'Times New Roman'; font-size-adjust: none; font-stretch: normal"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;Data warehouse awareness by providing out-of-the-box capability&lt;br /&gt;to manage slowly changing dimensions.&lt;/p&gt;&lt;p class="BulletedList1"&gt;&lt;span style="font-family:Symbol;"&gt;·&lt;span style="FONT: 7pt 'Times New Roman'; font-size-adjust: none; font-stretch: normal"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;Seamless integration with the SQL Server BI platform to directly&lt;br /&gt;build Analysis Services cubes and load into Analysis Services partitions.&lt;/p&gt;&lt;p class="BulletedList1"&gt;&lt;span style="font-family:Symbol;"&gt;·&lt;span style="FONT: 7pt 'Times New Roman'; font-size-adjust: none; font-stretch: normal"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;Extensibility with the power of .NET by incorporating custom&lt;br /&gt;scripts and pluggable components directly into the data integration flow.&lt;/p&gt;&lt;h6 style="MARGIN-LEFT: 0in"&gt;&lt;a name="_Toc198032318"&gt;Simplify the transformation&lt;br /&gt;process by using Data Profiling tasks&lt;/a&gt;&lt;/h6&gt;&lt;p class="Text"&gt;The new Data Profiling task in Integration Services can be used&lt;br /&gt;to initially understand the nature of the source data for design purposes (see &lt;a href="http://www.blogger.com/post-create.g?blogID=7516218764458010868#_Designing_your_DW/BI"&gt;Designing Your Data Warehouse/BI Solution&lt;/a&gt;). However,&lt;br /&gt;the profiles it produces can also be used to apply business rules to data as&lt;br /&gt;part of the transformation process. Suppose, for example, the business rule&lt;br /&gt;says that the data from a particular source is acceptable only if the number of&lt;br /&gt;nulls does not exceed 1%. The profiles produced by a Data Profiling task&lt;br /&gt;can be used to apply this rule.&lt;/p&gt;&lt;p class="Text"&gt;Note that Data Profiling tasks profile SQL Server tables;&lt;br /&gt;data in other locations must be loaded into staging tables before it can be&lt;br /&gt;profiled.&lt;/p&gt;&lt;h6 style="MARGIN-LEFT: 0in"&gt;&lt;a name="_Toc198032319"&gt;Simplify using MERGE and&lt;br /&gt;INSERT INTO&lt;/a&gt;&lt;/h6&gt;&lt;p class="Text"&gt;Whether you are extracting from the source systems into an&lt;br /&gt;Operational Data Store (ODS) or from the ODS into the fact or dimension tables,&lt;br /&gt;you must manage the movement of the changes (the deltas) that have occurred. During&lt;br /&gt;this phase you often need multiple Data Manipulation Language (DML) queries in&lt;br /&gt;order to perform one logical movement of the deltas into the relevant table.&lt;br /&gt;This is particularly true when you have to deal with slowly changing dimensions&lt;br /&gt;(and who doesn’t?).&lt;/p&gt;&lt;p class="Text"&gt;SQL Server 2008 allows you to combine these multiple queries into&lt;br /&gt;one MERGE statement. &lt;/p&gt;&lt;p class="MsoHeading7" style="MARGIN-LEFT: 0in"&gt;&lt;span style="font-size:12;"&gt;MERGE&lt;/span&gt;&lt;/p&gt;&lt;p class="Text"&gt;The MERGE statement performs insert, update, or delete operations&lt;br /&gt;on a target table based on the results of a join with a source table.&lt;/p&gt;&lt;p class="Text"&gt;The MERGE statement provides three types of WHEN clauses: &lt;/p&gt;&lt;p class="BulletedList1"&gt;&lt;span style="font-family:Symbol;"&gt;·&lt;span style="FONT: 7pt 'Times New Roman'; font-size-adjust: none; font-stretch: normal"&gt;&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;WHEN MATCHED enables you to UPDATE or DELETE the given row in the&lt;br /&gt;target table when the source and target rows match some criteria or criterion.&lt;/p&gt;&lt;p class="BulletedList1"&gt;&lt;span style="font-family:Symbol;"&gt;·&lt;span style="FONT: 7pt 'Times New Roman'; font-size-adjust: none; font-stretch: normal"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;WHEN NOT MATCHED [BY TARGET] enables you to INSERT a row into the&lt;br /&gt;target when it exists in the source but not in the target.&lt;/p&gt;&lt;p class="BulletedList1"&gt;&lt;span style="font-family:Symbol;"&gt;·&lt;span style="FONT: 7pt 'Times New Roman'; font-size-adjust: none; font-stretch: normal"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;WHEN NOT MATCHED BY SOURCE enables you to UPDATE or DELETE the&lt;br /&gt;given row in the target table when it exists in the target but not in the&lt;br /&gt;source.&lt;/p&gt;&lt;p class="Text"&gt;You can specify a search condition with each of the WHEN clauses&lt;br /&gt;to choose which type of DML operation should be performed on the row. &lt;/p&gt;&lt;p class="Text"&gt;The OUTPUT clause for the MERGE statement includes a new virtual&lt;br /&gt;column called &lt;b&gt;$action&lt;/b&gt; that you can use to identify the DML action that&lt;br /&gt;was performed on each row. &lt;/p&gt;&lt;p class="Text"&gt;To illustrate the use of the MERGE statement, imagine that you&lt;br /&gt;have a table of Employees:&lt;/p&gt;&lt;p class="TableSpacing"&gt;&lt;/p&gt;&lt;table class="MsoNormalTable" style="BORDER-RIGHT: medium none; BORDER-TOP: medium none; BORDER-LEFT: medium none; BORDER-BOTTOM: medium none; BORDER-COLLAPSE: collapse" border="1"&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td style="BORDER-RIGHT: windowtext 1pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: windowtext 1pt solid; PADDING-LEFT: 5.4pt; BACKGROUND: rgb(217,217,217) 0% 50%; PADDING-BOTTOM: 0in; BORDER-LEFT: windowtext 1pt solid; WIDTH: 59.4pt; PADDING-TOP: 0in; BORDER-BOTTOM: windowtext 1pt solid; moz-background-clip: -moz-initial; moz-background-origin: -moz-initial; moz-background-inline-policy: -moz-initial" valign="top" width="79"&gt;&lt;p class="Label"&gt;EmpID&lt;/p&gt;&lt;/td&gt;&lt;td style="BORDER-RIGHT: windowtext 1pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: windowtext 1pt solid; PADDING-LEFT: 5.4pt; BACKGROUND: rgb(217,217,217) 0% 50%; PADDING-BOTTOM: 0in; BORDER-LEFT: medium none; WIDTH: 1in; PADDING-TOP: 0in; BORDER-BOTTOM: windowtext 1pt solid; moz-background-clip: -moz-initial; moz-background-origin: -moz-initial; moz-background-inline-policy: -moz-initial" valign="top" width="96"&gt;&lt;p class="Label"&gt;Name&lt;/p&gt;&lt;/td&gt;&lt;td style="BORDER-RIGHT: windowtext 1pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: windowtext 1pt solid; PADDING-LEFT: 5.4pt; BACKGROUND: rgb(217,217,217) 0% 50%; PADDING-BOTTOM: 0in; BORDER-LEFT: medium none; WIDTH: 81pt; PADDING-TOP: 0in; BORDER-BOTTOM: windowtext 1pt solid; moz-background-clip: -moz-initial; moz-background-origin: -moz-initial; moz-background-inline-policy: -moz-initial" valign="top" width="108"&gt;&lt;p class="Label"&gt;Title&lt;/p&gt;&lt;/td&gt;&lt;td style="BORDER-RIGHT: windowtext 1pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: windowtext 1pt solid; PADDING-LEFT: 5.4pt; BACKGROUND: rgb(217,217,217) 0% 50%; PADDING-BOTTOM: 0in; BORDER-LEFT: medium none; WIDTH: 81pt; PADDING-TOP: 0in; BORDER-BOTTOM: windowtext 1pt solid; moz-background-clip: -moz-initial; moz-background-origin: -moz-initial; moz-background-inline-policy: -moz-initial" valign="top" width="108"&gt;&lt;p class="Label"&gt;IsCurrent&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td style="BORDER-RIGHT: 1pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0in; BORDER-LEFT: 1pt solid; WIDTH: 59.4pt; PADDING-TOP: 0in; BORDER-BOTTOM: 1pt solid" valign="top" width="79"&gt;&lt;p class="Text"&gt;1&lt;/p&gt;&lt;/td&gt;&lt;td style="BORDER-RIGHT: 1pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0in; BORDER-LEFT: medium none; WIDTH: 1in; PADDING-TOP: 0in; BORDER-BOTTOM: 1pt solid" valign="top" width="96"&gt;&lt;p class="Text"&gt;Jones&lt;/p&gt;&lt;/td&gt;&lt;td style="BORDER-RIGHT: 1pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0in; BORDER-LEFT: medium none; WIDTH: 81pt; PADDING-TOP: 0in; BORDER-BOTTOM: 1pt solid" valign="top" width="108"&gt;&lt;p class="Text"&gt;Ms&lt;/p&gt;&lt;/td&gt;&lt;td style="BORDER-RIGHT: 1pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0in; BORDER-LEFT: medium none; WIDTH: 81pt; PADDING-TOP: 0in; BORDER-BOTTOM: 1pt solid" valign="top" width="108"&gt;&lt;p class="Text"&gt;Yes&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td style="BORDER-RIGHT: 1pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0in; BORDER-LEFT: 1pt solid; WIDTH: 59.4pt; PADDING-TOP: 0in; BORDER-BOTTOM: 1pt solid" valign="top" width="79"&gt;&lt;p class="Text"&gt;2&lt;/p&gt;&lt;/td&gt;&lt;td style="BORDER-RIGHT: 1pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0in; BORDER-LEFT: medium none; WIDTH: 1in; PADDING-TOP: 0in; BORDER-BOTTOM: 1pt solid" valign="top" width="96"&gt;&lt;p class="Text"&gt;Smith&lt;/p&gt;&lt;/td&gt;&lt;td style="BORDER-RIGHT: 1pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0in; BORDER-LEFT: medium none; WIDTH: 81pt; PADDING-TOP: 0in; BORDER-BOTTOM: 1pt solid" valign="top" width="108"&gt;&lt;p class="Text"&gt;Prof&lt;/p&gt;&lt;/td&gt;&lt;td style="BORDER-RIGHT: 1pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0in; BORDER-LEFT: medium none; WIDTH: 81pt; PADDING-TOP: 0in; BORDER-BOTTOM: 1pt solid" valign="top" width="108"&gt;&lt;p class="Text"&gt;Yes&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td style="BORDER-RIGHT: 1pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0in; BORDER-LEFT: 1pt solid; WIDTH: 59.4pt; PADDING-TOP: 0in; BORDER-BOTTOM: 1pt solid" valign="top" width="79"&gt;&lt;p class="Text"&gt;3&lt;/p&gt;&lt;/td&gt;&lt;td style="BORDER-RIGHT: 1pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0in; BORDER-LEFT: medium none; WIDTH: 1in; PADDING-TOP: 0in; BORDER-BOTTOM: 1pt solid" valign="top" width="96"&gt;&lt;p class="Text"&gt;Brown&lt;/p&gt;&lt;/td&gt;&lt;td style="BORDER-RIGHT: 1pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0in; BORDER-LEFT: medium none; WIDTH: 81pt; PADDING-TOP: 0in; BORDER-BOTTOM: 1pt solid" valign="top" width="108"&gt;&lt;p class="Text"&gt;Mr&lt;/p&gt;&lt;/td&gt;&lt;td style="BORDER-RIGHT: 1pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0in; BORDER-LEFT: medium none; WIDTH: 81pt; PADDING-TOP: 0in; BORDER-BOTTOM: 1pt solid" valign="top" width="108"&gt;&lt;p class="Text"&gt;Yes&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td style="BORDER-RIGHT: 1pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0in; BORDER-LEFT: 1pt solid; WIDTH: 59.4pt; PADDING-TOP: 0in; BORDER-BOTTOM: 1pt solid" valign="top" width="79"&gt;&lt;p class="Text"&gt;4&lt;/p&gt;&lt;/td&gt;&lt;td style="BORDER-RIGHT: 1pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0in; BORDER-LEFT: medium none; WIDTH: 1in; PADDING-TOP: 0in; BORDER-BOTTOM: 1pt solid" valign="top" width="96"&gt;&lt;p class="Text"&gt;Wilson&lt;/p&gt;&lt;/td&gt;&lt;td style="BORDER-RIGHT: 1pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0in; BORDER-LEFT: medium none; WIDTH: 81pt; PADDING-TOP: 0in; BORDER-BOTTOM: 1pt solid" valign="top" width="108"&gt;&lt;p class="Text"&gt;Dr&lt;/p&gt;&lt;/td&gt;&lt;td style="BORDER-RIGHT: 1pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0in; BORDER-LEFT: medium none; WIDTH: 81pt; PADDING-TOP: 0in; BORDER-BOTTOM: 1pt solid" valign="top" width="108"&gt;&lt;p class="Text"&gt;Yes&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td style="BORDER-RIGHT: 1pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0in; BORDER-LEFT: 1pt solid; WIDTH: 59.4pt; PADDING-TOP: 0in; BORDER-BOTTOM: 1pt solid" valign="top" width="79"&gt;&lt;p class="Text"&gt;5&lt;/p&gt;&lt;/td&gt;&lt;td style="BORDER-RIGHT: 1pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0in; BORDER-LEFT: medium none; WIDTH: 1in; PADDING-TOP: 0in; BORDER-BOTTOM: 1pt solid" valign="top" width="96"&gt;&lt;p class="Text"&gt;Carlton&lt;/p&gt;&lt;/td&gt;&lt;td style="BORDER-RIGHT: 1pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0in; BORDER-LEFT: medium none; WIDTH: 81pt; PADDING-TOP: 0in; BORDER-BOTTOM: 1pt solid" valign="top" width="108"&gt;&lt;p class="Text"&gt;Dr&lt;/p&gt;&lt;/td&gt;&lt;td style="BORDER-RIGHT: 1pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0in; BORDER-LEFT: medium none; WIDTH: 81pt; PADDING-TOP: 0in; BORDER-BOTTOM: 1pt solid" valign="top" width="108"&gt;&lt;p class="Text"&gt;Yes&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;p class="TableSpacing"&gt;&lt;/p&gt;&lt;p class="Text"&gt;and a table of changes:&lt;/p&gt;&lt;p class="TableSpacingAfter"&gt;&lt;/p&gt;&lt;table class="MsoNormalTable" style="BORDER-RIGHT: medium none; BORDER-TOP: medium none; BORDER-LEFT: medium none; BORDER-BOTTOM: medium none; BORDER-COLLAPSE: collapse" border="1"&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td style="BORDER-RIGHT: windowtext 1pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: windowtext 1pt solid; PADDING-LEFT: 5.4pt; BACKGROUND: rgb(217,217,217) 0% 50%; PADDING-BOTTOM: 0in; BORDER-LEFT: windowtext 1pt solid; WIDTH: 59.4pt; PADDING-TOP: 0in; BORDER-BOTTOM: windowtext 1pt solid; moz-background-clip: -moz-initial; moz-background-origin: -moz-initial; moz-background-inline-policy: -moz-initial" valign="top" width="79"&gt;&lt;p class="Label"&gt;EmpID&lt;/p&gt;&lt;/td&gt;&lt;td style="BORDER-RIGHT: windowtext 1pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: windowtext 1pt solid; PADDING-LEFT: 5.4pt; BACKGROUND: rgb(217,217,217) 0% 50%; PADDING-BOTTOM: 0in; BORDER-LEFT: medium none; WIDTH: 1in; PADDING-TOP: 0in; BORDER-BOTTOM: windowtext 1pt solid; moz-background-clip: -moz-initial; moz-background-origin: -moz-initial; moz-background-inline-policy: -moz-initial" valign="top" width="96"&gt;&lt;p class="Label"&gt;Name&lt;/p&gt;&lt;/td&gt;&lt;td style="BORDER-RIGHT: windowtext 1pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: windowtext 1pt solid; PADDING-LEFT: 5.4pt; BACKGROUND: rgb(217,217,217) 0% 50%; PADDING-BOTTOM: 0in; BORDER-LEFT: medium none; WIDTH: 1in; PADDING-TOP: 0in; BORDER-BOTTOM: windowtext 1pt solid; moz-background-clip: -moz-initial; moz-background-origin: -moz-initial; moz-background-inline-policy: -moz-initial" valign="top" width="96"&gt;&lt;p class="Label"&gt;Title&lt;/p&gt;&lt;/td&gt;&lt;td style="BORDER-RIGHT: windowtext 1pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: windowtext 1pt solid; PADDING-LEFT: 5.4pt; BACKGROUND: rgb(217,217,217) 0% 50%; PADDING-BOTTOM: 0in; BORDER-LEFT: medium none; WIDTH: 1.25in; PADDING-TOP: 0in; BORDER-BOTTOM: windowtext 1pt solid; moz-background-clip: -moz-initial; moz-background-origin: -moz-initial; moz-background-inline-policy: -moz-initial" valign="top" width="120"&gt;&lt;p class="Label"&gt;IsCurrent&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td style="BORDER-RIGHT: 1pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0in; BORDER-LEFT: 1pt solid; WIDTH: 59.4pt; PADDING-TOP: 0in; BORDER-BOTTOM: 1pt solid" valign="top" width="79"&gt;&lt;p class="Text"&gt;1&lt;/p&gt;&lt;/td&gt;&lt;td style="BORDER-RIGHT: 1pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0in; BORDER-LEFT: medium none; WIDTH: 1in; PADDING-TOP: 0in; BORDER-BOTTOM: 1pt solid" valign="top" width="96"&gt;&lt;p class="Text"&gt;Jones&lt;/p&gt;&lt;/td&gt;&lt;td style="BORDER-RIGHT: 1pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0in; BORDER-LEFT: medium none; WIDTH: 1in; PADDING-TOP: 0in; BORDER-BOTTOM: 1pt solid" valign="top" width="96"&gt;&lt;p class="Text"&gt;Prof&lt;/p&gt;&lt;/td&gt;&lt;td style="BORDER-RIGHT: 1pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0in; BORDER-LEFT: medium none; WIDTH: 1.25in; PADDING-TOP: 0in; BORDER-BOTTOM: 1pt solid" valign="top" width="120"&gt;&lt;p class="Text"&gt;Yes&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td style="BORDER-RIGHT: 1pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0in; BORDER-LEFT: 1pt solid; WIDTH: 59.4pt; PADDING-TOP: 0in; BORDER-BOTTOM: 1pt solid" valign="top" width="79"&gt;&lt;p class="Text"&gt;6&lt;/p&gt;&lt;/td&gt;&lt;td style="BORDER-RIGHT: 1pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0in; BORDER-LEFT: medium none; WIDTH: 1in; PADDING-TOP: 0in; BORDER-BOTTOM: 1pt solid" valign="top" width="96"&gt;&lt;p class="Text"&gt;Heron&lt;/p&gt;&lt;/td&gt;&lt;td style="BORDER-RIGHT: 1pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0in; BORDER-LEFT: medium none; WIDTH: 1in; PADDING-TOP: 0in; BORDER-BOTTOM: 1pt solid" valign="top" width="96"&gt;&lt;p class="Text"&gt;Mr&lt;/p&gt;&lt;/td&gt;&lt;td style="BORDER-RIGHT: 1pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0in; BORDER-LEFT: medium none; WIDTH: 1.25in; PADDING-TOP: 0in; BORDER-BOTTOM: 1pt solid" valign="top" width="120"&gt;&lt;p class="Text"&gt;Yes&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;p class="TableSpacing"&gt;&lt;/p&gt;&lt;p class="MsoHeading7" style="MARGIN-LEFT: 0in"&gt;&lt;span style="font-size:12;"&gt;Type&lt;br /&gt;1 slowly changing dimension&lt;/span&gt;&lt;/p&gt;&lt;p class="Text"&gt;Suppose first that the Employee table is a Type 1 slowly&lt;br /&gt;changing dimension, meaning that changes to the Title field are simply allowed&lt;br /&gt;to overwrite the existing value and no history is kept of the change nor of the&lt;br /&gt;previous value. Further assume that new rows in the source are to be inserted&lt;br /&gt;into the Employees table as well. You can manage this simple case with a MERGE&lt;br /&gt;statement:&lt;/p&gt;&lt;p class="Code"&gt;MERGE Employee as TRG&lt;/p&gt;&lt;p class="Code"&gt;USING EmployeeDelta AS SRC&lt;/p&gt;&lt;p class="Code"&gt;ON (SRC.EmpID = TRG.EmpID)&lt;/p&gt;&lt;p class="Code"&gt;WHEN NOT MATCHED THEN&lt;/p&gt;&lt;p class="Code"&gt;INSERT VALUES (SRC.EmpID, SRC.Name, SRC.Title, 'Yes')&lt;/p&gt;&lt;p class="Code"&gt;WHEN MATCHED THEN&lt;/p&gt;&lt;p class="Code"&gt;UPDATE SET TRG.Title = SRC.Title&lt;/p&gt;&lt;p class="Text"&gt;If we add an OUTPUT clause like this:&lt;/p&gt;&lt;p class="Code"&gt;OUTPUT $action, SRC.EmpID, SRC.Name, SRC.Title;&lt;/p&gt;&lt;p class="Text"&gt;we get the following result rowset in addition to the effect on&lt;br /&gt;the Employee table:&lt;/p&gt;&lt;p class="TableSpacing"&gt;&lt;/p&gt;&lt;table class="MsoNormalTable" style="BORDER-RIGHT: medium none; BORDER-TOP: medium none; BORDER-LEFT: medium none; BORDER-BOTTOM: medium none; BORDER-COLLAPSE: collapse" border="1"&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td style="BORDER-RIGHT: windowtext 1pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: windowtext 1pt solid; PADDING-LEFT: 5.4pt; BACKGROUND: rgb(217,217,217) 0% 50%; PADDING-BOTTOM: 0in; BORDER-LEFT: windowtext 1pt solid; WIDTH: 88.55pt; PADDING-TOP: 0in; BORDER-BOTTOM: windowtext 1pt solid; moz-background-clip: -moz-initial; moz-background-origin: -moz-initial; moz-background-inline-policy: -moz-initial" valign="top" width="118"&gt;&lt;p class="Label"&gt;$action&lt;/p&gt;&lt;/td&gt;&lt;td style="BORDER-RIGHT: windowtext 1pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: windowtext 1pt solid; PADDING-LEFT: 5.4pt; BACKGROUND: rgb(217,217,217) 0% 50%; PADDING-BOTTOM: 0in; BORDER-LEFT: medium none; WIDTH: 69.85pt; PADDING-TOP: 0in; BORDER-BOTTOM: windowtext 1pt solid; moz-background-clip: -moz-initial; moz-background-origin: -moz-initial; moz-background-inline-policy: -moz-initial" valign="top" width="93"&gt;&lt;p class="Label"&gt;EmpID&lt;/p&gt;&lt;/td&gt;&lt;td style="BORDER-RIGHT: windowtext 1pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: windowtext 1pt solid; PADDING-LEFT: 5.4pt; BACKGROUND: rgb(217,217,217) 0% 50%; PADDING-BOTTOM: 0in; BORDER-LEFT: medium none; WIDTH: 81pt; PADDING-TOP: 0in; BORDER-BOTTOM: windowtext 1pt solid; moz-background-clip: -moz-initial; moz-background-origin: -moz-initial; moz-background-inline-policy: -moz-initial" valign="top" width="108"&gt;&lt;p class="Label"&gt;Name&lt;/p&gt;&lt;/td&gt;&lt;td style="BORDER-RIGHT: windowtext 1pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: windowtext 1pt solid; PADDING-LEFT: 5.4pt; BACKGROUND: rgb(217,217,217) 0% 50%; PADDING-BOTTOM: 0in; BORDER-LEFT: medium none; WIDTH: 81pt; PADDING-TOP: 0in; BORDER-BOTTOM: windowtext 1pt solid; moz-background-clip: -moz-initial; moz-background-origin: -moz-initial; moz-background-inline-policy: -moz-initial" valign="top" width="108"&gt;&lt;p class="Label"&gt;Title&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td style="BORDER-RIGHT: 1pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0in; BORDER-LEFT: 1pt solid; WIDTH: 88.55pt; PADDING-TOP: 0in; BORDER-BOTTOM: 1pt solid" valign="top" width="118"&gt;&lt;p class="Text"&gt;INSERT&lt;/p&gt;&lt;/td&gt;&lt;td style="BORDER-RIGHT: 1pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0in; BORDER-LEFT: medium none; WIDTH: 69.85pt; PADDING-TOP: 0in; BORDER-BOTTOM: 1pt solid" valign="top" width="93"&gt;&lt;p class="Text"&gt;6&lt;/p&gt;&lt;/td&gt;&lt;td style="BORDER-RIGHT: 1pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0in; BORDER-LEFT: medium none; WIDTH: 81pt; PADDING-TOP: 0in; BORDER-BOTTOM: 1pt solid" valign="top" width="108"&gt;&lt;p class="Text"&gt;Heron&lt;/p&gt;&lt;/td&gt;&lt;td style="BORDER-RIGHT: 1pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0in; BORDER-LEFT: medium none; WIDTH: 81pt; PADDING-TOP: 0in; BORDER-BOTTOM: 1pt solid" valign="top" width="108"&gt;&lt;p class="Text"&gt;Mr&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td style="BORDER-RIGHT: 1pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0in; BORDER-LEFT: 1pt solid; WIDTH: 88.55pt; PADDING-TOP: 0in; BORDER-BOTTOM: 1pt solid" valign="top" width="118"&gt;&lt;p class="Text"&gt;UPDATE&lt;/p&gt;&lt;/td&gt;&lt;td style="BORDER-RIGHT: 1pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0in; BORDER-LEFT: medium none; WIDTH: 69.85pt; PADDING-TOP: 0in; BORDER-BOTTOM: 1pt solid" valign="top" width="93"&gt;&lt;p class="Text"&gt;1&lt;/p&gt;&lt;/td&gt;&lt;td style="BORDER-RIGHT: 1pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0in; BORDER-LEFT: medium none; WIDTH: 81pt; PADDING-TOP: 0in; BORDER-BOTTOM: 1pt solid" valign="top" width="108"&gt;&lt;p class="Text"&gt;Jones&lt;/p&gt;&lt;/td&gt;&lt;td style="BORDER-RIGHT: 1pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0in; BORDER-LEFT: medium none; WIDTH: 81pt; PADDING-TOP: 0in; BORDER-BOTTOM: 1pt solid" valign="top" width="108"&gt;&lt;p class="Text"&gt;Prof&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;p class="TableSpacing"&gt;&lt;/p&gt;&lt;p class="Text"&gt;This can be very helpful with debugging but it also turns out to&lt;br /&gt;be very useful in dealing with Type 2 slowly changing dimensions.&lt;/p&gt;&lt;p class="MsoHeading7" style="MARGIN-LEFT: 0in"&gt;&lt;span style="font-size:12;"&gt;Type&lt;br /&gt;2 slowly changing dimensions&lt;/span&gt;&lt;/p&gt;&lt;p class="Text"&gt;Recall that in a Type 2 slowly changing dimension a new&lt;br /&gt;record is added into the Employee dimension table irrespective of whether an&lt;br /&gt;employee record already exists. For example, we want to preserve the existing&lt;br /&gt;row for Jones but set the value of IsCurrent in that row to ‘No’. Then we want&lt;br /&gt;to insert both of the rows from the delta table (the source) into the target.&lt;/p&gt;&lt;p class="MsoNormal"&gt;&lt;span style="font-family:'Courier New';color:navy;"&gt;&lt;/span&gt;&lt;/p&gt;&lt;p class="Code"&gt;MERGE Employee as TRG&lt;/p&gt;&lt;p class="Code"&gt;USING EmployeeDelta AS SRC&lt;/p&gt;&lt;p class="Code"&gt;ON (SRC.EmpID = TRG.EmpID AND TRG.IsCurrent = 'Yes')&lt;/p&gt;&lt;p class="Code"&gt;WHEN NOT MATCHED THEN&lt;/p&gt;&lt;p class="Code"&gt;INSERT VALUES (SRC.EmpID, SRC.Name, SRC.Title, 'Yes')&lt;/p&gt;&lt;p class="Code"&gt;WHEN MATCHED THEN&lt;/p&gt;&lt;p class="Code"&gt;UPDATE SET TRG.IsCurrent = 'No'&lt;/p&gt;&lt;p class="Code"&gt;OUTPUT $action, SRC.EmpID, SRC.Name, SRC.Title;&lt;/p&gt;&lt;p class="Text"&gt;This statement sets the value of IsCurrent to ‘No’ in the&lt;br /&gt;existing row for Jones and inserts the row for Heron from the delta table into the&lt;br /&gt;Employee table. However, it does not insert the new row for Jones. This does&lt;br /&gt;not present a problem because we can address that with the new INSERT&lt;br /&gt;functionality, described next. In addition, we have the output, which in this&lt;br /&gt;case is:&lt;/p&gt;&lt;p class="TableSpacing"&gt;&lt;/p&gt;&lt;table class="MsoNormalTable" style="BORDER-RIGHT: medium none; BORDER-TOP: medium none; BORDER-LEFT: medium none; BORDER-BOTTOM: medium none; BORDER-COLLAPSE: collapse" border="1"&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td style="BORDER-RIGHT: windowtext 1pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: windowtext 1pt solid; PADDING-LEFT: 5.4pt; BACKGROUND: rgb(217,217,217) 0% 50%; PADDING-BOTTOM: 0in; BORDER-LEFT: windowtext 1pt solid; WIDTH: 1.2in; PADDING-TOP: 0in; BORDER-BOTTOM: windowtext 1pt solid; moz-background-clip: -moz-initial; moz-background-origin: -moz-initial; moz-background-inline-policy: -moz-initial" valign="top" width="115"&gt;&lt;p class="Label"&gt;$action&lt;/p&gt;&lt;/td&gt;&lt;td style="BORDER-RIGHT: windowtext 1pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: windowtext 1pt solid; PADDING-LEFT: 5.4pt; BACKGROUND: rgb(217,217,217) 0% 50%; PADDING-BOTTOM: 0in; BORDER-LEFT: medium none; WIDTH: 1in; PADDING-TOP: 0in; BORDER-BOTTOM: windowtext 1pt solid; moz-background-clip: -moz-initial; moz-background-origin: -moz-initial; moz-background-inline-policy: -moz-initial" valign="top" width="96"&gt;&lt;p class="Label"&gt;EmpID&lt;/p&gt;&lt;/td&gt;&lt;td style="BORDER-RIGHT: windowtext 1pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: windowtext 1pt solid; PADDING-LEFT: 5.4pt; BACKGROUND: rgb(217,217,217) 0% 50%; PADDING-BOTTOM: 0in; BORDER-LEFT: medium none; WIDTH: 81pt; PADDING-TOP: 0in; BORDER-BOTTOM: windowtext 1pt solid; moz-background-clip: -moz-initial; moz-background-origin: -moz-initial; moz-background-inline-policy: -moz-initial" valign="top" width="108"&gt;&lt;p class="Label"&gt;Name&lt;/p&gt;&lt;/td&gt;&lt;td style="BORDER-RIGHT: windowtext 1pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: windowtext 1pt solid; PADDING-LEFT: 5.4pt; BACKGROUND: rgb(217,217,217) 0% 50%; PADDING-BOTTOM: 0in; BORDER-LEFT: medium none; WIDTH: 81pt; PADDING-TOP: 0in; BORDER-BOTTOM: windowtext 1pt solid; moz-background-clip: -moz-initial; moz-background-origin: -moz-initial; moz-background-inline-policy: -moz-initial" valign="top" width="108"&gt;&lt;p class="Label"&gt;Title&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td style="BORDER-RIGHT: 1pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0in; BORDER-LEFT: 1pt solid; WIDTH: 1.2in; PADDING-TOP: 0in; BORDER-BOTTOM: 1pt solid" valign="top" width="115"&gt;&lt;p class="Text"&gt;INSERT&lt;/p&gt;&lt;/td&gt;&lt;td style="BORDER-RIGHT: 1pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0in; BORDER-LEFT: medium none; WIDTH: 1in; PADDING-TOP: 0in; BORDER-BOTTOM: 1pt solid" valign="top" width="96"&gt;&lt;p class="Text"&gt;6&lt;/p&gt;&lt;/td&gt;&lt;td style="BORDER-RIGHT: 1pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0in; BORDER-LEFT: medium none; WIDTH: 81pt; PADDING-TOP: 0in; BORDER-BOTTOM: 1pt solid" valign="top" width="108"&gt;&lt;p class="Text"&gt;Heron&lt;/p&gt;&lt;/td&gt;&lt;td style="BORDER-RIGHT: 1pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0in; BORDER-LEFT: medium none; WIDTH: 81pt; PADDING-TOP: 0in; BORDER-BOTTOM: 1pt solid" valign="top" width="108"&gt;&lt;p class="Text"&gt;Mr&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td style="BORDER-RIGHT: 1pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0in; BORDER-LEFT: 1pt solid; WIDTH: 1.2in; PADDING-TOP: 0in; BORDER-BOTTOM: 1pt solid" valign="top" width="115"&gt;&lt;p class="Text"&gt;UPDATE&lt;/p&gt;&lt;/td&gt;&lt;td style="BORDER-RIGHT: 1pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0in; BORDER-LEFT: medium none; WIDTH: 1in; PADDING-TOP: 0in; BORDER-BOTTOM: 1pt solid" valign="top" width="96"&gt;&lt;p class="Text"&gt;1&lt;/p&gt;&lt;/td&gt;&lt;td style="BORDER-RIGHT: 1pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0in; BORDER-LEFT: medium none; WIDTH: 81pt; PADDING-TOP: 0in; BORDER-BOTTOM: 1pt solid" valign="top" width="108"&gt;&lt;p class="Text"&gt;Jones&lt;/p&gt;&lt;/td&gt;&lt;td style="BORDER-RIGHT: 1pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0in; BORDER-LEFT: medium none; WIDTH: 81pt; PADDING-TOP: 0in; BORDER-BOTTOM: 1pt solid" valign="top" width="108"&gt;&lt;p class="Text"&gt;Prof&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;p class="TableSpacing"&gt;&lt;/p&gt;&lt;p class="MsoHeading7" style="MARGIN-LEFT: 0in"&gt;&lt;span style="font-size:12;"&gt;New&lt;br /&gt;functionality for INSERT&lt;/span&gt;&lt;/p&gt;&lt;p class="Text"&gt;We can combine the capacity to output the data with a new ability&lt;br /&gt;in SQL Server 2008 to have INSERT statements consume the results of DML&lt;br /&gt;statements. This ability to consume output can, of course, be used very effectively&lt;br /&gt;(and simply) as follows: &lt;/p&gt;&lt;p class="Code"&gt;INSERT INTO Employee (EmpID, name, Title)&lt;/p&gt;&lt;p class="Code"&gt;SELECT EmpID, name, Title from EmployeeDelta&lt;/p&gt;&lt;p class="Text"&gt;If we combine this new capability with the output above, we have the&lt;br /&gt;synergistic ability to extract the row that was updated (Jones) &lt;b&gt;and&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;insert it into the Employees table to achieve the desired effect in the context&lt;br /&gt;of Type 2 slowly changing dimensions:&lt;/p&gt;&lt;p class="Code"&gt;INSERT INTO Employee( EMPID, Name, Title, IsCurrent) &lt;/p&gt;&lt;p class="Code"&gt;SELECT EMPID, Name, Title, 'Yes'&lt;/p&gt;&lt;p class="Code"&gt;FROM&lt;/p&gt;&lt;p class="Code"&gt;( &lt;/p&gt;&lt;p class="Code"&gt;MERGE Employee as TRG&lt;/p&gt;&lt;p class="Code"&gt;USING EmployeeDelta AS SRC&lt;/p&gt;&lt;p class="Code"&gt;ON (SRC.EmpID = TRG.EmpID AND TRG.IsCurrent = 'Yes')&lt;/p&gt;&lt;p class="Code"&gt;WHEN TARGET NOT MATCHED THEN&lt;/p&gt;&lt;p class="Code"&gt;INSERT VALUES (SRC.EmpID, SRC.Name, SRC.Title, 'Yes')&lt;/p&gt;&lt;p class="Code"&gt;WHEN MATCHED THEN&lt;/p&gt;&lt;p class="Code"&gt;UPDATE SET TRG.IsCurrent = 'No'&lt;/p&gt;&lt;p class="Code"&gt;OUTPUT $action, SRC.EmpID, SRC.Name, SRC.Title&lt;/p&gt;&lt;p class="Code"&gt;) &lt;/p&gt;&lt;p class="Code"&gt;As Changes (action, EmpID, Name, Title)&lt;/p&gt;&lt;p class="Code"&gt;WHERE action ='UPDATE';&lt;/p&gt;&lt;p class="Text"&gt;MERGE in SQL Server 2008 was implemented to comply with the SQL-2006&lt;br /&gt;standard. The main reason for the introduction of MERGE into the SQL standard&lt;br /&gt;and into SQL Server 2008 is its usefulness in managing slowly changing&lt;br /&gt;dimensions but it is worth remembering that both MERGE and INSERT with output&lt;br /&gt;have many other applications both within data warehousing specifically and in databases&lt;br /&gt;in general. &lt;/p&gt;&lt;h6 style="MARGIN-LEFT: 0in"&gt;&lt;a name="_Toc198032320"&gt;Terminate all SQL&lt;br /&gt;statements with a semi-colon in SQL Server 2008&lt;/a&gt;&lt;/h6&gt;&lt;p class="Text"&gt;Prior to SQL Server 2005, Transact-SQL was relatively&lt;br /&gt;relaxed about semi-colons; now some statements (including MERGE) require this&lt;br /&gt;terminator. If you terminate all SQL statements with a semi-colon, you avoid&lt;br /&gt;problems when the use of a semi-colon is obligatory. &lt;/p&gt;&lt;h6 style="MARGIN-LEFT: 0in"&gt;&lt;a name="_Toc198032321"&gt;If you cannot tolerate&lt;br /&gt;downtime, consider using “ping pong” partitions&lt;/a&gt;&lt;/h6&gt;&lt;p class="Text"&gt;Imagine that you have a fact table that is partitioned by month. It&lt;br /&gt;is currently August. You need to load today’s data into the August partition,&lt;br /&gt;but your users cannot tolerate the performance hit and potential locking&lt;br /&gt;conflicts that will be incurred as you load it. Copy the August partition to&lt;br /&gt;another table (a working table), load the data into that table, index it as&lt;br /&gt;needed, and then simply switch the partitions between the two tables.&lt;/p&gt;&lt;h6 style="MARGIN-LEFT: 0in"&gt;&lt;a name="_Toc198032322"&gt;Use minimal logging to load&lt;br /&gt;data precisely where you want it as fast as possible&lt;/a&gt;&lt;/h6&gt;&lt;p class="Text"&gt;Writing data to a database typically involves two separate write-to-disk&lt;br /&gt;processes, once writing to the database and once to the log (so transactions&lt;br /&gt;can be rolled back or re-done). When inserting data into an existing table it&lt;br /&gt;is, in fact, possible to write only once in some cases by using the minimally&lt;br /&gt;logged INSERT feature. &lt;/p&gt;&lt;p class="Text"&gt;Minimal logging enables transactions to be rolled back but does&lt;br /&gt;not support point-in-time recovery. It is also only available with the bulk&lt;br /&gt;logged and simple recovery models. In SQL Server 2008, minimal logging can&lt;br /&gt;be used with INSERT INTO…SELECT FROM Transact-SQL statements when inserting a&lt;br /&gt;large number of rows into an existing table if they are inserted into an empty&lt;br /&gt;table with a clustered index and no nonclustered indexes, or a heap, empty or&lt;br /&gt;not, with no indexes. (For full details and any late-breaking changes, see SQL&lt;br /&gt;Server 2008 Books Online.)&lt;/p&gt;&lt;p class="Text"&gt;This extends the support for minimal logging, which in SQL Server 2005&lt;br /&gt;included bulk import, SELECT INTO, index creation, and rebuild operations.&lt;/p&gt;&lt;p class="Text"&gt;One huge benefit of minimal logging is that it speeds up the&lt;br /&gt;loading of empty partitions or tables that are on specific filegroups. In SQL&lt;br /&gt;Server 2005, you could achieve effectively the same effect by using a&lt;br /&gt;work-around that involved changing the default filegroup and performing a&lt;br /&gt;SELECT INTO to get minimal logging. Then the default filegroup would be returned&lt;br /&gt;to its initial state. Now you can just create a table on the filegroup(s) you&lt;br /&gt;want it to be in, define its partitioning scheme and then load it with INSERT&lt;br /&gt;INTO tbl WITH(NOLOCK) SELECT FROM and you acheive minimal logging. &lt;/p&gt;&lt;p class="Text"&gt;Minimal logging makes it much easier to put the data just where&lt;br /&gt;you want it and write it to disk only once. As an added bonus, load performance&lt;br /&gt;is increased and the amount of log space required is reduced.&lt;/p&gt;&lt;h6 style="MARGIN-LEFT: 0in"&gt;&lt;a name="_Toc198032323"&gt;Simplify data extraction by&lt;br /&gt;using Change Data Capture in the SQL Server source systems&lt;/a&gt;&lt;/h6&gt;&lt;p class="Text"&gt;SQL Server 2008 has a new data tracking feature that is of particular&lt;br /&gt;benefit in data warehousing. The Change Data Capture process tracks changes to&lt;br /&gt;user tables and collects them into a relational format. A typical use would be&lt;br /&gt;to track changes in an operational database for later inclusion in the&lt;br /&gt;warehouse. &lt;/p&gt;&lt;p class="Text"&gt;The capture process collects change data from the database’s&lt;br /&gt;transaction log and inserts it into a change table. Metadata about each&lt;br /&gt;transaction is also inserted into a metadata table so that changes can be&lt;br /&gt;ordered with regard to time. This enables the identification of, for instance,&lt;br /&gt;the type of change made to each row, and which column or columns changed in an&lt;br /&gt;updated row. It is also possible to request all rows that changed between two&lt;br /&gt;time/dates. Change Data Capture is a big step towards improved extraction performance,&lt;br /&gt;and makes programming the change capture portion of your ETL jobs much easier.&lt;/p&gt;&lt;h6 style="MARGIN-LEFT: 0in"&gt;&lt;a name="_Toc198032324"&gt;Simplify and speed up ETL&lt;br /&gt;with improved Lookup&lt;/a&gt;&lt;/h6&gt;&lt;p class="Text"&gt;The performance of the Lookup component has greatly improved&lt;br /&gt;performance in SQL Server 2008 Integration Services and is much easier to&lt;br /&gt;program.&lt;/p&gt;&lt;p class="Text"&gt;A Lookup verifies whether each row in a stream of rows has a&lt;br /&gt;matching row in a set of reference data. This is often used within the ETL&lt;br /&gt;process to check, for example, the ProductID column in a fact table (acting as&lt;br /&gt;the data source) against a dimension table holding a complete set of products&lt;br /&gt;(the reference set). &lt;/p&gt;&lt;p class="Text"&gt;&lt;span style="color:windowtext;"&gt;In SQL Server 2008, the Lookup&lt;br /&gt;transformation supports two connection types when connecting to the reference&lt;br /&gt;dataset: the Cache connection manager and the OLE DB connection manager. The&lt;br /&gt;reference dataset can be a cache file, an existing table or view, a new table, or&lt;br /&gt;the result of an SQL query.&lt;/span&gt;&lt;/p&gt;&lt;p class="Text"&gt;Reference data is usually cached for efficiency and now a&lt;br /&gt;dataflow can be used to populate the cache. Many potential sources can be used&lt;br /&gt;as reference data: Excel, XML, text, Web services—anything within reach of an&lt;br /&gt;ADO.Net provider. In SQL Server 2005, the cache could only be populated by&lt;br /&gt;an SQL query and a Lookup could only take data from specific OLE /DB&lt;br /&gt;connections. The new Cache Transform component populates a cache defined by the&lt;br /&gt;Cache connection manager. &lt;/p&gt;&lt;p class="Text"&gt;The cache no longer needs to be reloaded each time it is used:&lt;br /&gt;this removes the speed penalty incurred by reloading from a relational source. If&lt;br /&gt;a reference dataset is used by two pipelines in a single package, the cache can&lt;br /&gt;be saved to permanent file storage as well as to virtual memory so it is&lt;br /&gt;available to multiple Lookups within one package. Furthermore the cache file&lt;br /&gt;format is optimized for speed and its size is unrestricted. &lt;/p&gt;&lt;p class="Text"&gt;The miss-cache feature is also new. When running directly against&lt;br /&gt;the dataset, a Lookup component can add to the cache any key values from the&lt;br /&gt;source where there is no matching value in the reference dataset. So if Lookup&lt;br /&gt;has once determined that the reference set does not contain, for example, the&lt;br /&gt;value 885, it does not waste time inspecting the reference set for that value&lt;br /&gt;if it appears again in the source data. Under certain conditions this feature&lt;br /&gt;can produce a performance improvement of 40%.&lt;/p&gt;&lt;p class="Text"&gt;Finally there is now a ‘Lookup no match output’ to which ‘miss-cache’&lt;br /&gt;rows can be directed instead of going to the error output. &lt;/p&gt;&lt;h4 style="MARGIN-LEFT: 0in"&gt;&lt;a name="_Toc198032325"&gt;&lt;!----&gt;&lt;/a&gt;&lt;a name="_Relational_Data_Warehouse"&gt;&lt;!----&gt;&lt;/a&gt;Relational Data Warehouse Setup, Query,&lt;br /&gt;and Management&lt;/h4&gt;&lt;p class="Text"&gt;The relational database is the heart of any BI system. Best&lt;br /&gt;practices here affect not only the performance of the entire system, but also&lt;br /&gt;its flexibility and value to the enterprise. For a more in-depth discussion of&lt;br /&gt;how to get the best performance from your SQL Server 2008 data warehouse&lt;br /&gt;for large-scale data warehouses, see the companion paper [Han08].&lt;/p&gt;&lt;h5 style="MARGIN-LEFT: 0in"&gt;&lt;a name="_Toc198032326"&gt;Best Practices: General&lt;/a&gt;&lt;/h5&gt;&lt;h6 style="MARGIN-LEFT: 0in"&gt;&lt;a name="_Toc198032327"&gt;Use the resource governor&lt;br /&gt;to reserve resources for important work such as data loading, and to prevent&lt;br /&gt;runaway queries&lt;/a&gt;&lt;/h6&gt;&lt;p class="Text"&gt;The workload on a data warehouse can be thought of as a series of&lt;br /&gt;requests that compete for the available resources. In SQL Server 2005,&lt;br /&gt;there was a single pool of resources and requests competed equally for those. The&lt;br /&gt;resource governor in SQL Server 2008 allows the available resources to be&lt;br /&gt;allocated into multiple (up to 20) pools. Requests are classified so that&lt;br /&gt;they fall into specific groups and those groups are allocated to the resource pools—many&lt;br /&gt;requests to each resource pool. Processes that must complete rapidly (such as&lt;br /&gt;the data load) can be allocated high resources when they run. In addition,&lt;br /&gt;important reports can be allocated enough resources to ensure that they&lt;br /&gt;complete rapidly [Bar08]. &lt;/p&gt;&lt;p class="Text"&gt;Many users find unpredictability of performance highly&lt;br /&gt;frustrating. If this occurs, it is beneficial to use the resource governor to&lt;br /&gt;allocate resources in a way that ensures more predictable performance. Over&lt;br /&gt;time, as experience with the resource governor grows, we expect this to evolve&lt;br /&gt;into a best practice.&lt;/p&gt;&lt;p class="Text"&gt;Suppose, as is often the case, that the data warehouse is used&lt;br /&gt;for both reporting and ETL processes and is configured for zero or minimal&lt;br /&gt;downtime during loads. In some enterprises (despite what the BI team might&lt;br /&gt;prefer), the generation of reports on time is seen as more important than the&lt;br /&gt;completion of the daily load. In this case the reporting workload group would&lt;br /&gt;be allocated a high priority. &lt;/p&gt;&lt;p class="Text"&gt;Or, by contrast, the load processes may be given high priority in&lt;br /&gt;order to guarantee the minimum downtime that the business requires. &lt;/p&gt;&lt;p class="Text"&gt;Finally, it is worth noting that the resource governor also enables&lt;br /&gt;you to monitor the resource consumption of each group, which means that resource&lt;br /&gt;usage can be better understood, which in turn allows better management.&lt;/p&gt;&lt;h6 style="MARGIN-LEFT: 0in"&gt;&lt;a name="_Toc198032328"&gt;Carefully plan when to&lt;br /&gt;rebuild statistics and indexes&lt;/a&gt;&lt;/h6&gt;&lt;p class="Text"&gt;The query optimizer uses information from the database statistics&lt;br /&gt;(number of rows, data distribution, and so on) in order to help determine the&lt;br /&gt;optimal query plan. If the statistics are inaccurate, a less optimal plan may&lt;br /&gt;be chosen, which degrades query performance. &lt;/p&gt;&lt;p class="Text"&gt;If you can afford the time during your ETL process, rebuild&lt;br /&gt;statistics after every load of the data warehouse. This ensures that the statistics&lt;br /&gt;are always accurate. However, rebuilding statistics takes time and resources. In&lt;br /&gt;addition, the time between rebuilds is less important than the change to the&lt;br /&gt;distribution of the data that has occurred. &lt;/p&gt;&lt;p class="Text"&gt;When the data warehouse is new and evolving, and also relatively&lt;br /&gt;small, it makes sense to update statistics frequently, possibly after every&lt;br /&gt;load. As the warehouse matures, you can sometimes reduce the frequency of&lt;br /&gt;rebuilding without degrading query performance significantly. If it is&lt;br /&gt;important to reduce the cost of updating statistics, you can determine the&lt;br /&gt;appropriate statistic refresh frequency by monitoring query performance as you&lt;br /&gt;reduce the frequency of refreshes. Be aware that if the bulk of your queries&lt;br /&gt;target only the most recently loaded data, you will not have statistics for&lt;br /&gt;that data unless you update statistics after each load. This is a fairly common&lt;br /&gt;situation, which is why we recommend updating statistics after each load of the&lt;br /&gt;data warehouse by default.&lt;/p&gt;&lt;h5 style="MARGIN-LEFT: 0in"&gt;&lt;a name="_Toc198032329"&gt;Best Practices: Date/time&lt;/a&gt;&lt;/h5&gt;&lt;h6 style="MARGIN-LEFT: 0in"&gt;&lt;a name="_Toc198032330"&gt;Use the correct time/date&lt;br /&gt;data type&lt;/a&gt;&lt;/h6&gt;&lt;p class="Text"&gt;SQL Server 2008 has six date and time data types:&lt;/p&gt;&lt;p class="BulletedList1"&gt;&lt;span style="font-family:Symbol;"&gt;·&lt;span style="FONT: 7pt 'Times New Roman'; font-size-adjust: none; font-stretch: normal"&gt;&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;b&gt;date&lt;/b&gt;&lt;/p&gt;&lt;p class="BulletedList1"&gt;&lt;span style="font-family:Symbol;"&gt;·&lt;span style="FONT: 7pt 'Times New Roman'; font-size-adjust: none; font-stretch: normal"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;b&gt;datetime2&lt;/b&gt;&lt;/p&gt;&lt;p class="BulletedList1"&gt;&lt;span style="font-family:Symbol;"&gt;·&lt;span style="FONT: 7pt 'Times New Roman'; font-size-adjust: none; font-stretch: normal"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;b&gt;datetime&lt;/b&gt;&lt;/p&gt;&lt;p class="BulletedList1"&gt;&lt;span style="font-family:Symbol;"&gt;·&lt;span style="FONT: 7pt 'Times New Roman'; font-size-adjust: none; font-stretch: normal"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;b&gt;datetimeoffset&lt;/b&gt;&lt;/p&gt;&lt;p class="BulletedList1"&gt;&lt;span style="font-family:Symbol;"&gt;·&lt;span style="FONT: 7pt 'Times New Roman'; font-size-adjust: none; font-stretch: normal"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;b&gt;smalldatetime&lt;/b&gt;&lt;/p&gt;&lt;p class="BulletedList1"&gt;&lt;span style="font-family:Symbol;"&gt;·&lt;span style="FONT: 7pt 'Times New Roman'; font-size-adjust: none; font-stretch: normal"&gt;&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;b&gt;time&lt;/b&gt;&lt;/p&gt;&lt;p class="Text"&gt;These store temporal information with varying degrees of&lt;br /&gt;precision. Choosing the right one enables you to maintain accurate time and date&lt;br /&gt;information in a way that suits your application best, saves storage space, and&lt;br /&gt;improves query performance. For example, many older SQL Server&lt;br /&gt;applications use &lt;b&gt;datetime&lt;/b&gt; for dates, but leave the time portion blank.&lt;br /&gt;This takes more space than necessary. Now, you can use the new &lt;b&gt;date&lt;/b&gt; type&lt;br /&gt;for these columns, which takes only three bytes compared to eight bytes&lt;br /&gt;for &lt;b&gt;datetime&lt;/b&gt;. &lt;/p&gt;&lt;h6 style="MARGIN-LEFT: 0in"&gt;&lt;a name="_Toc198032331"&gt;Consider using &lt;b&gt;datetime2&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;in some database ports&lt;/a&gt;&lt;/h6&gt;&lt;p class="Text"&gt;&lt;b&gt;datetime2&lt;/b&gt; can be considered to be an extension of the&lt;br /&gt;existing &lt;b&gt;datetime&lt;/b&gt; type—it combines a date with a time based on the&lt;br /&gt;24-hour clock. However, &lt;b&gt;datetime2&lt;/b&gt; has a larger date range, a larger&lt;br /&gt;default fractional precision, and an optional user-specified precision. The&lt;br /&gt;precision is such that it can store fractions of a second to seven digits—in other&lt;br /&gt;words to within one ten millionth of a second. This new feature can influence&lt;br /&gt;the porting of some data warehouse applications.&lt;/p&gt;&lt;p class="Text"&gt;For example, the DB2 TimeStamp data type has an accuracy of one&lt;br /&gt;millionth of a second. In a data warehouse application written on DB2, if the&lt;br /&gt;application logic is built to ensure that new records are created at least one&lt;br /&gt;microsecond apart (which is not a particularly onerous limitation), time/date can&lt;br /&gt;be used as a unique ID. We can debate whether it is a best practice to design a&lt;br /&gt;database application in this way but the fact is that it is sometimes done with&lt;br /&gt;DB2. &lt;/p&gt;&lt;p class="Text"&gt;Before the advent of &lt;b&gt;datatime2&lt;/b&gt;, if such an application were&lt;br /&gt;ported to SQL Server, the application logic would have to be rewritten&lt;br /&gt;because &lt;b&gt;datetime&lt;/b&gt; provides an accuracy of only a thousandth of a second. Because&lt;br /&gt;&lt;br /&gt;&lt;b&gt;datetime2&lt;/b&gt; is ten times more precise than DB2’s TimeStamp, the&lt;br /&gt;application can now be ported with no change to the logic. &lt;/p&gt;&lt;h5 style="MARGIN-LEFT: 0in"&gt;&lt;a name="_Toc198032332"&gt;Best Practices: Compression&lt;br /&gt;and Encryption&lt;/a&gt;&lt;/h5&gt;&lt;h6 style="MARGIN-LEFT: 0in"&gt;&lt;a name="_Toc198032333"&gt;&lt;!----&gt;&lt;/a&gt;&lt;a name="_Best_practice_–"&gt;&lt;!----&gt;&lt;/a&gt;Use PAGE compression to reduce data volume and&lt;br /&gt;speed up queries&lt;/h6&gt;&lt;p class="Text"&gt;Full-blown data compression capability has been added to SQL&lt;br /&gt;Server 2008; the improvements come in two types—row and page.&lt;/p&gt;&lt;p class="Text"&gt;Row compression stores all fields in variable width format. If&lt;br /&gt;the data is compressible, row compression reduces the number of bytes required&lt;br /&gt;to store it.&lt;/p&gt;&lt;p class="Text"&gt;Page compression does the same but the compression takes place&lt;br /&gt;between the rows within each page. A page-level dictionary is used to store&lt;br /&gt;common values, which are then referenced from the rows themselves rather than&lt;br /&gt;stored redundantly. In addition, common prefixes of column values are stored&lt;br /&gt;only once on the page. As an illustration of how prefix compression can help, consider&lt;br /&gt;product codes where the prefixes are often similar.&lt;/p&gt;&lt;p class="TableSpacing"&gt;&lt;/p&gt;&lt;table class="MsoNormalTable" style="BORDER-RIGHT: medium none; BORDER-TOP: medium none; MARGIN-LEFT: 4.8pt; BORDER-LEFT: medium none; WIDTH: 177.8pt; BORDER-BOTTOM: medium none; BORDER-COLLAPSE: collapse" width="237" border="1"&gt;&lt;tbody&gt;&lt;tr style="HEIGHT: 13.2pt"&gt;&lt;td style="BORDER-RIGHT: windowtext 1pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: windowtext 1pt solid; PADDING-LEFT: 5.4pt; BACKGROUND: rgb(217,217,217) 0% 50%; PADDING-BOTTOM: 0in; BORDER-LEFT: windowtext 1pt solid; WIDTH: 121.1pt; PADDING-TOP: 0in; BORDER-BOTTOM: windowtext 1pt solid; HEIGHT: 13.2pt; moz-background-clip: -moz-initial; moz-background-origin: -moz-initial; moz-background-inline-policy: -moz-initial" valign="bottom" width="161"&gt;&lt;p class="Label"&gt;Code&lt;/p&gt;&lt;/td&gt;&lt;td style="BORDER-RIGHT: windowtext 1pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: windowtext 1pt solid; PADDING-LEFT: 5.4pt; BACKGROUND: rgb(217,217,217) 0% 50%; PADDING-BOTTOM: 0in; BORDER-LEFT: medium none; WIDTH: 56.7pt; PADDING-TOP: 0in; BORDER-BOTTOM: windowtext 1pt solid; HEIGHT: 13.2pt; moz-background-clip: -moz-initial; moz-background-origin: -moz-initial; moz-background-inline-policy: -moz-initial" valign="bottom" width="76"&gt;&lt;p class="Label"&gt;Quantity&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr style="HEIGHT: 13.2pt"&gt;&lt;td style="BORDER-RIGHT: 1pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0in; BORDER-LEFT: 1pt solid; WIDTH: 121.1pt; PADDING-TOP: 0in; BORDER-BOTTOM: 1pt solid; HEIGHT: 13.2pt" valign="bottom" width="161"&gt;&lt;p class="Text"&gt;A-F234-10-1234&lt;/p&gt;&lt;/td&gt;&lt;td style="BORDER-RIGHT: 1pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0in; BORDER-LEFT: medium none; WIDTH: 56.7pt; PADDING-TOP: 0in; BORDER-BOTTOM: 1pt solid; HEIGHT: 13.2pt" valign="bottom" width="76"&gt;&lt;p class="Text"&gt;1834&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr style="HEIGHT: 13.2pt"&gt;&lt;td style="BORDER-RIGHT: 1pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0in; BORDER-LEFT: 1pt solid; WIDTH: 121.1pt; PADDING-TOP: 0in; BORDER-BOTTOM: 1pt solid; HEIGHT: 13.2pt" valign="bottom" width="161"&gt;&lt;p class="Text"&gt;A-F234-10-1235&lt;/p&gt;&lt;/td&gt;&lt;td style="BORDER-RIGHT: 1pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0in; BORDER-LEFT: medium none; WIDTH: 56.7pt; PADDING-TOP: 0in; BORDER-BOTTOM: 1pt solid; HEIGHT: 13.2pt" valign="bottom" width="76"&gt;&lt;p class="Text"&gt;1435&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr style="HEIGHT: 13.2pt"&gt;&lt;td style="BORDER-RIGHT: 1pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0in; BORDER-LEFT: 1pt solid; WIDTH: 121.1pt; PADDING-TOP: 0in; BORDER-BOTTOM: 1pt solid; HEIGHT: 13.2pt" valign="bottom" width="161"&gt;&lt;p class="Text"&gt;A-F234-10-1236&lt;/p&gt;&lt;/td&gt;&lt;td style="BORDER-RIGHT: 1pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0in; BORDER-LEFT: medium none; WIDTH: 56.7pt; PADDING-TOP: 0in; BORDER-BOTTOM: 1pt solid; HEIGHT: 13.2pt" valign="bottom" width="76"&gt;&lt;p class="Text"&gt;1237&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr style="HEIGHT: 13.2pt"&gt;&lt;td style="BORDER-RIGHT: 1pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0in; BORDER-LEFT: 1pt solid; WIDTH: 121.1pt; PADDING-TOP: 0in; BORDER-BOTTOM: 1pt solid; HEIGHT: 13.2pt" valign="bottom" width="161"&gt;&lt;p class="Text"&gt;A-F234-10-1237&lt;/p&gt;&lt;/td&gt;&lt;td style="BORDER-RIGHT: 1pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0in; BORDER-LEFT: medium none; WIDTH: 56.7pt; PADDING-TOP: 0in; BORDER-BOTTOM: 1pt solid; HEIGHT: 13.2pt" valign="bottom" width="76"&gt;&lt;p class="Text"&gt;1546&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr style="HEIGHT: 13.2pt"&gt;&lt;td style="BORDER-RIGHT: 1pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0in; BORDER-LEFT: 1pt solid; WIDTH: 121.1pt; PADDING-TOP: 0in; BORDER-BOTTOM: 1pt solid; HEIGHT: 13.2pt" valign="bottom" width="161"&gt;&lt;p class="Text"&gt;A-F234-10-1238&lt;/p&gt;&lt;/td&gt;&lt;td style="BORDER-RIGHT: 1pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0in; BORDER-LEFT: medium none; WIDTH: 56.7pt; PADDING-TOP: 0in; BORDER-BOTTOM: 1pt solid; HEIGHT: 13.2pt" valign="bottom" width="76"&gt;&lt;p class="Text"&gt;1545&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr style="HEIGHT: 13.2pt"&gt;&lt;td style="BORDER-RIGHT: 1pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0in; BORDER-LEFT: 1pt solid; WIDTH: 121.1pt; PADDING-TOP: 0in; BORDER-BOTTOM: 1pt solid; HEIGHT: 13.2pt" valign="bottom" width="161"&gt;&lt;p class="Text"&gt;A-F234-10-1239&lt;/p&gt;&lt;/td&gt;&lt;td style="BORDER-RIGHT: 1pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0in; BORDER-LEFT: medium none; WIDTH: 56.7pt; PADDING-TOP: 0in; BORDER-BOTTOM: 1pt solid; HEIGHT: 13.2pt" valign="bottom" width="76"&gt;&lt;p class="Text"&gt;1543&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr style="HEIGHT: 13.2pt"&gt;&lt;td style="BORDER-RIGHT: 1pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0in; BORDER-LEFT: 1pt solid; WIDTH: 121.1pt; PADDING-TOP: 0in; BORDER-BOTTOM: 1pt solid; HEIGHT: 13.2pt" valign="bottom" width="161"&gt;&lt;p class="Text"&gt;A-F234-10-1240&lt;/p&gt;&lt;/td&gt;&lt;td style="BORDER-RIGHT: 1pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0in; BORDER-LEFT: medium none; WIDTH: 56.7pt; PADDING-TOP: 0in; BORDER-BOTTOM: 1pt solid; HEIGHT: 13.2pt" valign="bottom" width="76"&gt;&lt;p class="Text"&gt;1756&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr style="HEIGHT: 13.2pt"&gt;&lt;td style="BORDER-RIGHT: 1pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0in; BORDER-LEFT: 1pt solid; WIDTH: 121.1pt; PADDING-TOP: 0in; BORDER-BOTTOM: 1pt solid; HEIGHT: 13.2pt" valign="bottom" width="161"&gt;&lt;p class="Text"&gt;A-F234-10-1241&lt;/p&gt;&lt;/td&gt;&lt;td style="BORDER-RIGHT: 1pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0in; BORDER-LEFT: medium none; WIDTH: 56.7pt; PADDING-TOP: 0in; BORDER-BOTTOM: 1pt solid; HEIGHT: 13.2pt" valign="bottom" width="76"&gt;&lt;p class="Text"&gt;1435&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr style="HEIGHT: 13.2pt"&gt;&lt;td style="BORDER-RIGHT: 1pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0in; BORDER-LEFT: 1pt solid; WIDTH: 121.1pt; PADDING-TOP: 0in; BORDER-BOTTOM: 1pt solid; HEIGHT: 13.2pt" valign="bottom" width="161"&gt;&lt;p class="Text"&gt;A-F234-10-1242&lt;/p&gt;&lt;/td&gt;&lt;td style="BORDER-RIGHT: 1pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0in; BORDER-LEFT: medium none; WIDTH: 56.7pt; PADDING-TOP: 0in; BORDER-BOTTOM: 1pt solid; HEIGHT: 13.2pt" valign="bottom" width="76"&gt;&lt;p class="Text"&gt;1544&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;p class="TableSpacing"&gt;&lt;/p&gt;&lt;p class="Text"&gt;This can be compressed to:&lt;/p&gt;&lt;p class="TableSpacing"&gt;&lt;/p&gt;&lt;table class="MsoNormalTable" style="BORDER-RIGHT: medium none; BORDER-TOP: medium none; MARGIN-LEFT: 4.8pt; BORDER-LEFT: medium none; WIDTH: 142pt; BORDER-BOTTOM: medium none; BORDER-COLLAPSE: collapse" width="189" border="1"&gt;&lt;tbody&gt;&lt;tr style="HEIGHT: 13.2pt"&gt;&lt;td style="BORDER-RIGHT: windowtext 1pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: windowtext 1pt solid; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0in; BORDER-LEFT: windowtext 1pt solid; WIDTH: 94pt; PADDING-TOP: 0in; BORDER-BOTTOM: windowtext 1pt solid; HEIGHT: 13.2pt" valign="bottom" width="125"&gt;&lt;p class="Text"&gt;A-F234-10-12&lt;/p&gt;&lt;/td&gt;&lt;td style="BORDER-RIGHT: windowtext 1pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: windowtext 1pt solid; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0in; BORDER-LEFT: medium none; WIDTH: 48pt; PADDING-TOP: 0in; BORDER-BOTTOM: windowtext 1pt solid; HEIGHT: 13.2pt" valign="bottom" width="64"&gt;&lt;p class="Text"&gt;1000&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;p class="TableSpacing"&gt;&lt;/p&gt;&lt;table class="MsoNormalTable" style="BORDER-RIGHT: medium none; BORDER-TOP: medium none; MARGIN-LEFT: 4.8pt; BORDER-LEFT: medium none; WIDTH: 142.05pt; BORDER-BOTTOM: medium none; BORDER-COLLAPSE: collapse" width="189" border="1"&gt;&lt;tbody&gt;&lt;tr style="HEIGHT: 13.2pt"&gt;&lt;td style="BORDER-RIGHT: windowtext 1pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: windowtext 1pt solid; PADDING-LEFT: 5.4pt; BACKGROUND: rgb(217,217,217) 0% 50%; PADDING-BOTTOM: 0in; BORDER-LEFT: windowtext 1pt solid; WIDTH: 94pt; PADDING-TOP: 0in; BORDER-BOTTOM: windowtext 1pt solid; HEIGHT: 13.2pt; moz-background-clip: -moz-initial; moz-background-origin: -moz-initial; moz-background-inline-policy: -moz-initial" valign="bottom" width="125"&gt;&lt;p class="Label"&gt;Code&lt;/p&gt;&lt;/td&gt;&lt;td style="BORDER-RIGHT: windowtext 1pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: windowtext 1pt solid; PADDING-LEFT: 5.4pt; BACKGROUND: rgb(217,217,217) 0% 50%; PADDING-BOTTOM: 0in; BORDER-LEFT: medium none; WIDTH: 48.05pt; PADDING-TOP: 0in; BORDER-BOTTOM: windowtext 1pt solid; HEIGHT: 13.2pt; moz-background-clip: -moz-initial; moz-background-origin: -moz-initial; moz-background-inline-policy: -moz-initial" valign="bottom" width="64"&gt;&lt;p class="Label"&gt;Quantity&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr style="HEIGHT: 13.2pt"&gt;&lt;td style="BORDER-RIGHT: 1pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0in; BORDER-LEFT: 1pt solid; WIDTH: 94pt; PADDING-TOP: 0in; BORDER-BOTTOM: 1pt solid; HEIGHT: 13.2pt" valign="bottom" width="125"&gt;&lt;p class="Text"&gt;34&lt;/p&gt;&lt;/td&gt;&lt;td style="BORDER-RIGHT: 1pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0in; BORDER-LEFT: medium none; WIDTH: 48.05pt; PADDING-TOP: 0in; BORDER-BOTTOM: 1pt solid; HEIGHT: 13.2pt" valign="bottom" width="64"&gt;&lt;p class="Text"&gt;834&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr style="HEIGHT: 13.2pt"&gt;&lt;td style="BORDER-RIGHT: 1pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0in; BORDER-LEFT: 1pt solid; WIDTH: 94pt; PADDING-TOP: 0in; BORDER-BOTTOM: 1pt solid; HEIGHT: 13.2pt" valign="bottom" width="125"&gt;&lt;p class="Text"&gt;35&lt;/p&gt;&lt;/td&gt;&lt;td style="BORDER-RIGHT: 1pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0in; BORDER-LEFT: medium none; WIDTH: 48.05pt; PADDING-TOP: 0in; BORDER-BOTTOM: 1pt solid; HEIGHT: 13.2pt" valign="bottom" width="64"&gt;&lt;p class="Text"&gt;435&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr style="HEIGHT: 13.2pt"&gt;&lt;td style="BORDER-RIGHT: 1pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0in; BORDER-LEFT: 1pt solid; WIDTH: 94pt; PADDING-TOP: 0in; BORDER-BOTTOM: 1pt solid; HEIGHT: 13.2pt" valign="bottom" width="125"&gt;&lt;p class="Text"&gt;36&lt;/p&gt;&lt;/td&gt;&lt;td style="BORDER-RIGHT: 1pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0in; BORDER-LEFT: medium none; WIDTH: 48.05pt; PADDING-TOP: 0in; BORDER-BOTTOM: 1pt solid; HEIGHT: 13.2pt" valign="bottom" width="64"&gt;&lt;p class="Text"&gt;237&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr style="HEIGHT: 13.2pt"&gt;&lt;td style="BORDER-RIGHT: 1pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0in; BORDER-LEFT: 1pt solid; WIDTH: 94pt; PADDING-TOP: 0in; BORDER-BOTTOM: 1pt solid; HEIGHT: 13.2pt" valign="bottom" width="125"&gt;&lt;p class="Text"&gt;37&lt;/p&gt;&lt;/td&gt;&lt;td style="BORDER-RIGHT: 1pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0in; BORDER-LEFT: medium none; WIDTH: 48.05pt; PADDING-TOP: 0in; BORDER-BOTTOM: 1pt solid; HEIGHT: 13.2pt" valign="bottom" width="64"&gt;&lt;p class="Text"&gt;546&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr style="HEIGHT: 13.2pt"&gt;&lt;td style="BORDER-RIGHT: 1pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0in; BORDER-LEFT: 1pt solid; WIDTH: 94pt; PADDING-TOP: 0in; BORDER-BOTTOM: 1pt solid; HEIGHT: 13.2pt" valign="bottom" width="125"&gt;&lt;p class="Text"&gt;38&lt;/p&gt;&lt;/td&gt;&lt;td style="BORDER-RIGHT: 1pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0in; BORDER-LEFT: medium none; WIDTH: 48.05pt; PADDING-TOP: 0in; BORDER-BOTTOM: 1pt solid; HEIGHT: 13.2pt" valign="bottom" width="64"&gt;&lt;p class="Text"&gt;545&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr style="HEIGHT: 13.2pt"&gt;&lt;td style="BORDER-RIGHT: 1pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0in; BORDER-LEFT: 1pt solid; WIDTH: 94pt; PADDING-TOP: 0in; BORDER-BOTTOM: 1pt solid; HEIGHT: 13.2pt" valign="bottom" width="125"&gt;&lt;p class="Text"&gt;39&lt;/p&gt;&lt;/td&gt;&lt;td style="BORDER-RIGHT: 1pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0in; BORDER-LEFT: medium none; WIDTH: 48.05pt; PADDING-TOP: 0in; BORDER-BOTTOM: 1pt solid; HEIGHT: 13.2pt" valign="bottom" width="64"&gt;&lt;p class="Text"&gt;543&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr style="HEIGHT: 13.2pt"&gt;&lt;td style="BORDER-RIGHT: 1pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0in; BORDER-LEFT: 1pt solid; WIDTH: 94pt; PADDING-TOP: 0in; BORDER-BOTTOM: 1pt solid; HEIGHT: 13.2pt" valign="bottom" width="125"&gt;&lt;p class="Text"&gt;40&lt;/p&gt;&lt;/td&gt;&lt;td style="BORDER-RIGHT: 1pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0in; BORDER-LEFT: medium none; WIDTH: 48.05pt; PADDING-TOP: 0in; BORDER-BOTTOM: 1pt solid; HEIGHT: 13.2pt" valign="bottom" width="64"&gt;&lt;p class="Text"&gt;756&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr style="HEIGHT: 13.2pt"&gt;&lt;td style="BORDER-RIGHT: 1pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0in; BORDER-LEFT: 1pt solid; WIDTH: 94pt; PADDING-TOP: 0in; BORDER-BOTTOM: 1pt solid; HEIGHT: 13.2pt" valign="bottom" width="125"&gt;&lt;p class="Text"&gt;41&lt;/p&gt;&lt;/td&gt;&lt;td style="BORDER-RIGHT: 1pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0in; BORDER-LEFT: medium none; WIDTH: 48.05pt; PADDING-TOP: 0in; BORDER-BOTTOM: 1pt solid; HEIGHT: 13.2pt" valign="bottom" width="64"&gt;&lt;p class="Text"&gt;435&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr style="HEIGHT: 13.2pt"&gt;&lt;td style="BORDER-RIGHT: 1pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0in; BORDER-LEFT: 1pt solid; WIDTH: 94pt; PADDING-TOP: 0in; BORDER-BOTTOM: 1pt solid; HEIGHT: 13.2pt" valign="bottom" width="125"&gt;&lt;p class="Text"&gt;42&lt;/p&gt;&lt;/td&gt;&lt;td style="BORDER-RIGHT: 1pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0in; BORDER-LEFT: medium none; WIDTH: 48.05pt; PADDING-TOP: 0in; BORDER-BOTTOM: 1pt solid; HEIGHT: 13.2pt" valign="bottom" width="64"&gt;&lt;p class="Text"&gt;544&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;p class="TableSpacing"&gt;&lt;/p&gt;&lt;p class="Text"&gt;Even a column like Quantity can benefit from compression. For&lt;br /&gt;more details on how row and page compression work in SQL Server 2008, see SQL&lt;br /&gt;Server 2008 Books Online. &lt;/p&gt;&lt;p class="Text"&gt;Both page and row compression can be applied to tables and&lt;br /&gt;indexes.&lt;/p&gt;&lt;p class="Text"&gt;The obvious benefit is, of course, that you need less disk space.&lt;br /&gt;In tests, we have seen compression from two- to seven-fold, with three-fold being&lt;br /&gt;typical. This reduces your disk requirements by about two thirds. &lt;/p&gt;&lt;p class="Text"&gt;A less obvious but potentially more valuable benefit is found in&lt;br /&gt;query speed. The gain here comes from two factors. Disk I/O is substantially&lt;br /&gt;reduced because fewer reads are required to acquire the same amount of data. Secondly&lt;br /&gt;the percentage of the data that can be held in main memory is increased as a&lt;br /&gt;function of the compression factor. &lt;/p&gt;&lt;p class="Text"&gt;The main memory advantage is the performance gains enabled by&lt;br /&gt;compression and surging main memory sizes.&lt;/p&gt;&lt;p class="Text"&gt;Query performance can improve ten times or more if you can get&lt;br /&gt;all the data that you ever query into main memory and keep it there. Your&lt;br /&gt;results depend on the relative speed of your I/O system, memory, and CPUs. A&lt;br /&gt;moderately large data warehouse can fit entirely in main memory on a commodity four-processor&lt;br /&gt;server that can accommodate up to 128 GB of RAM—RAM that is increasingly&lt;br /&gt;affordable. This much RAM can hold &lt;i&gt;all&lt;/i&gt; of a typical 400‑GB data&lt;br /&gt;warehouse, compressed. Larger servers with up to 2 terabytes of memory are&lt;br /&gt;available that can fit an entire 6‑terabyte data warehouse in RAM.&lt;/p&gt;&lt;p class="Text"&gt;There is, of course, CPU cost associated with compression. This&lt;br /&gt;is seen mainly during the load process. When page compression is employed we have&lt;br /&gt;seen CPU utilization increase by a factor of about 2.5. Some specific&lt;br /&gt;figures for page compression, recorded during tests on both a 600‑GB and a&lt;br /&gt;6‑terabyte data warehouse with a workload of over a hundred different&lt;br /&gt;queries, are a 30-40% improvement in query response time with a 10-15% CPU&lt;br /&gt;time penalty.&lt;/p&gt;&lt;p class="Text"&gt;So, in data warehouses that are not currently CPU-bound, you&lt;br /&gt;should see significant improvements in query performance at a small CPU cost. Writes&lt;br /&gt;have more CPU overhead than reads. &lt;/p&gt;&lt;p class="Text"&gt;This description of the characteristics of compression should enable&lt;br /&gt;you to determine your optimal compression strategy for the tables and indexes&lt;br /&gt;in the warehouse. It may not be as simple as applying compression to every&lt;br /&gt;table and index. &lt;/p&gt;&lt;p class="Text"&gt;For example, suppose that you partition your fact table over time,&lt;br /&gt;such as by Quarter 1, Quarter 2, and so on. The current partition is&lt;br /&gt;Quarter 4. Quarter 4 is updated nightly, Quarter 3 far less&lt;br /&gt;frequently, and Quarters 1 and 2 are never updated. However, all are&lt;br /&gt;queried extensively.&lt;/p&gt;&lt;p class="Text"&gt;After testing you might find that the best practice is to apply&lt;br /&gt;both row and page compression to Quarters 1 and 2, row compression Quarter 3,&lt;br /&gt;and neither to Quarter 4. &lt;/p&gt;&lt;p class="Text"&gt;Your mileage will, of course, vary and testing is vital to&lt;br /&gt;establish best practices for your specific requirements. However, most data&lt;br /&gt;warehouses should gain significant benefit from implementing a compression strategy.&lt;br /&gt;Start by using page compression on all fact tables and fact table indexes. If&lt;br /&gt;this causes performance problems for loading or querying, consider falling back&lt;br /&gt;to row compression or no compression on some or all partitions.&lt;/p&gt;&lt;h6 style="MARGIN-LEFT: 0in"&gt;&lt;a name="_Toc198032334"&gt;If you use both compression&lt;br /&gt;and encryption, do so in that order&lt;/a&gt;&lt;/h6&gt;&lt;p class="Text"&gt;SQL Server 2008 allows table data to be encrypted. Best practice&lt;br /&gt;for using this depends on circumstances (see &lt;a href="http://www.blogger.com/post-create.g?blogID=7516218764458010868#_Best_practice_%E2%80%93"&gt;above&lt;/a&gt;)&lt;br /&gt;but be aware that much of the compression described in the previous best&lt;br /&gt;practice depends on finding repeated patterns in the data. Encryption actively&lt;br /&gt;and significantly reduces the patterning in the data. So, if you intend to use&lt;br /&gt;both, it is unwise to first encrypt and then compress.&lt;/p&gt;&lt;h6 style="MARGIN-LEFT: 0in"&gt;&lt;a name="_Toc198032335"&gt;Use backup compression to&lt;br /&gt;reduce storage footprint&lt;/a&gt;&lt;/h6&gt;&lt;p class="Text"&gt;Backup compression is now available and should be used unless you&lt;br /&gt;find good reason not to do so. The benefits are the same as other compression&lt;br /&gt;techniques—there are both speed and volume gains. We anticipate that for most&lt;br /&gt;data warehouses the primary gain of backup compression is in the reduced&lt;br /&gt;storage footprint, and the secondary gain is that backup completes more&lt;br /&gt;rapidly. Moreover, a restore runs faster because the backup is smaller.&lt;/p&gt;&lt;h5 style="MARGIN-LEFT: 0in"&gt;&lt;a name="_Toc198032336"&gt;Best Practices: Partitioning&lt;/a&gt; &lt;/h5&gt;&lt;h6 style="MARGIN-LEFT: 0in"&gt;&lt;a name="_Toc198032337"&gt;Partition large fact tables&lt;/a&gt;&lt;/h6&gt;&lt;p class="Text"&gt;Partitioning a table means splitting it horizontally into smaller&lt;br /&gt;components (called &lt;i&gt;partitions&lt;/i&gt;). Partitioning brings several benefits. Essentially,&lt;br /&gt;partitioning enables the data to be segregated into sets. This alone has huge&lt;br /&gt;advantages in terms of manageability. Partitions can be placed in different&lt;br /&gt;filegroups so that they can be backed up independently. This means that we can&lt;br /&gt;position the data on different spindles for performance reasons. In data&lt;br /&gt;warehouses it also means that we can isolate the rows that are likely to change&lt;br /&gt;and perform updates, deletes, and inserts on those rows alone. &lt;/p&gt;&lt;p class="Text"&gt;Query processing can be improved by partitioning because it&lt;br /&gt;sometimes enables query plans to eliminate entire partitions from&lt;br /&gt;consideration. For example, fact tables are frequently partitioned by date,&lt;br /&gt;such as by month. So when a report is run against the July figures, instead of&lt;br /&gt;accessing 1 billion rows, it may only have to access 20 million. &lt;/p&gt;&lt;p class="Text"&gt;Indexes as well as tables can be (and frequently are)&lt;br /&gt;partitioned, which increases the benefits.&lt;/p&gt;&lt;p class="Text"&gt;Imagine a fact table of 1 billion rows that is not&lt;br /&gt;partitioned. Every load (typically nightly) means insertion, deletion, and&lt;br /&gt;updating across that huge table. This can incur huge index maintenance costs,&lt;br /&gt;to the point where it may not be feasible to do the updates during your ETL&lt;br /&gt;window.&lt;/p&gt;&lt;p class="Text"&gt;If the same table is partitioned by time, generally only the most&lt;br /&gt;recent partition must be touched, which means that the majority of the table&lt;br /&gt;(and indexes) remain untouched. You can drop all the indexes prior to the load&lt;br /&gt;and rebuild them afterwards to avoid index maintenance overhead. This can&lt;br /&gt;greatly improve your load time.&lt;/p&gt;&lt;h6 style="MARGIN-LEFT: 0in"&gt;&lt;a name="_Toc198032338"&gt;Partition-align your&lt;br /&gt;indexed views&lt;/a&gt;&lt;/h6&gt;&lt;p class="Text"&gt;SQL Server 2008 enables you to create indexed views that are&lt;br /&gt;aligned with your partitioned fact tables, and switch partitions of the fact&lt;br /&gt;tables in and out. This works if the indexed views and fact table are&lt;br /&gt;partitioned using the same partition function. Typically, both the fact tables&lt;br /&gt;and the indexed views are partitioned by the surrogate key referencing the Date&lt;br /&gt;dimension table. When you switch in a new partition, or switch out an old one,&lt;br /&gt;you do not have to drop the indexed views first and then re‑create them&lt;br /&gt;afterwards. This can save a huge amount of time during your ETL process. In&lt;br /&gt;fact, it can make it feasible to use indexed views to accelerate your queries&lt;br /&gt;when it was not feasible before because of the impact on your daily load cycle.&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;&lt;h6 style="MARGIN-LEFT: 0in"&gt;&lt;a name="_Toc198032339"&gt;Design your partitioning&lt;br /&gt;scheme for ease of management first and foremost&lt;/a&gt;&lt;/h6&gt;&lt;p class="Text"&gt;In SQL Server 2008 it is not necessary to create partitions to&lt;br /&gt;get parallelism, as it is in some competing products. SQL Server 2008&lt;br /&gt;supports multiple threads per partition for query processing, which&lt;br /&gt;significantly simplifies development and management. When you design your&lt;br /&gt;partitioning strategy, choose your partition width for the convenience of your&lt;br /&gt;ETL and data life cycle management. For example, it is not necessary to&lt;br /&gt;partition by day to get more partitions (which might be necessary in SQL Server 2005&lt;br /&gt;or other DBMS products) if partitioning by week is more convenient for system&lt;br /&gt;management.&lt;/p&gt;&lt;h6 style="MARGIN-LEFT: 0in"&gt;&lt;a name="_Toc198032340"&gt;For best parallel&lt;br /&gt;performance, include an explicit date range predicate on the fact table in&lt;br /&gt;queries, rather than a join with the Date dimension&lt;/a&gt;&lt;/h6&gt;&lt;p class="Text"&gt;SQL Server generally does a good job of processing queries&lt;br /&gt;like the following one, where a date range predicate is specified by using a&lt;br /&gt;join between the fact table and the date dimension:&lt;/p&gt;&lt;p class="TableSpacingAfter"&gt;&lt;/p&gt;&lt;p class="Code" style="MARGIN-BOTTOM: 0pt; LINE-HEIGHT: normal"&gt;&lt;span class="CodeEmbedded"&gt;&lt;span style="POSITION: relative; TOP: 0pt"&gt;select top 10&lt;br /&gt;p.ProductKey, sum(f.SalesAmount)&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;&lt;p class="Code" style="MARGIN-BOTTOM: 0pt; LINE-HEIGHT: normal"&gt;&lt;span class="CodeEmbedded"&gt;&lt;span style="POSITION: relative; TOP: 0pt"&gt;from&lt;br /&gt;FactInternetSales f, DimProduct p, DimDate d&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;&lt;p class="Code" style="MARGIN-BOTTOM: 0pt; LINE-HEIGHT: normal"&gt;&lt;span class="CodeEmbedded"&gt;&lt;span style="POSITION: relative; TOP: 0pt"&gt;where&lt;br /&gt;f.ProductKey=p.ProductKey and p.ProductAlternateKey like N'BK-%'&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;&lt;p class="Code" style="MARGIN-BOTTOM: 0pt; LINE-HEIGHT: normal"&gt;&lt;span class="CodeEmbedded"&gt;&lt;span style="POSITION: relative; TOP: 0pt"&gt;and f.OrderDateKey =&lt;br /&gt;d.DateKey&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;&lt;p class="Code" style="MARGIN-BOTTOM: 0pt; LINE-HEIGHT: normal"&gt;&lt;span class="CodeEmbedded"&gt;&lt;span style="POSITION: relative; TOP: 0pt"&gt;and&lt;br /&gt;d.MonthNumberOfYear = 1&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;&lt;p class="Code" style="MARGIN-BOTTOM: 0pt; LINE-HEIGHT: normal"&gt;&lt;span class="CodeEmbedded"&gt;&lt;span style="POSITION: relative; TOP: 0pt"&gt;and d.CalendarYear =&lt;br /&gt;2008&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;&lt;p class="Code" style="MARGIN-BOTTOM: 0pt; LINE-HEIGHT: normal"&gt;&lt;span class="CodeEmbedded"&gt;&lt;span style="POSITION: relative; TOP: 0pt"&gt;and&lt;br /&gt;d.DayNumberOfMonth between 1 and 7&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;&lt;p class="Code" style="MARGIN-BOTTOM: 0pt; LINE-HEIGHT: normal"&gt;&lt;span class="CodeEmbedded"&gt;&lt;span style="POSITION: relative; TOP: 0pt"&gt;group by&lt;br /&gt;p.ProductKey&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;&lt;p class="Code" style="MARGIN-BOTTOM: 0pt; LINE-HEIGHT: normal"&gt;&lt;span class="CodeEmbedded"&gt;&lt;span style="POSITION: relative; TOP: 0pt"&gt;order by&lt;br /&gt;sum(f.SalesAmount) desc&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;&lt;p class="TableSpacingAfter"&gt;&lt;/p&gt;&lt;p class="Text"&gt;However, a query like this one normally uses a nested loop join&lt;br /&gt;between the date dimension and the fact table, which can limit parallelism and&lt;br /&gt;overall query performance because, at most, one thread is used for each&lt;br /&gt;qualifying date dimension row. Instead, for the best possible performance, put an&lt;br /&gt;explicit date range predicate on the date dimension key column of the fact&lt;br /&gt;table, and make sure the date dimension keys are in ascending order of date.&lt;br /&gt;The following is an example of a query with an explicit date range predicate:&lt;/p&gt;&lt;p class="TableSpacingAfter"&gt;&lt;/p&gt;&lt;p class="Code" style="MARGIN-BOTTOM: 0pt; LINE-HEIGHT: normal"&gt;&lt;span class="CodeEmbedded"&gt;&lt;span style="POSITION: relative; TOP: 0pt"&gt;select top 10&lt;br /&gt;p.ProductKey, d.CalendarYear, d.EnglishMonthName,&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;sum(f.SalesAmount)&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;&lt;p class="Code" style="MARGIN-BOTTOM: 0pt; LINE-HEIGHT: normal"&gt;&lt;span class="CodeEmbedded"&gt;&lt;span style="POSITION: relative; TOP: 0pt"&gt;from&lt;br /&gt;FactInternetSales f, DimProduct p, DimDate d&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;&lt;p class="Code" style="MARGIN-BOTTOM: 0pt; LINE-HEIGHT: normal"&gt;&lt;span class="CodeEmbedded"&gt;&lt;span style="POSITION: relative; TOP: 0pt"&gt;where&lt;br /&gt;f.ProductKey=p.ProductKey and p.ProductAlternateKey like N'BK-%'&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;&lt;p class="Code" style="MARGIN-BOTTOM: 0pt; LINE-HEIGHT: normal"&gt;&lt;span class="CodeEmbedded"&gt;&lt;span style="POSITION: relative; TOP: 0pt"&gt;and OrderDateKey&lt;br /&gt;between 20030101 and 20030107&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;&lt;p class="Code" style="MARGIN-BOTTOM: 0pt; LINE-HEIGHT: normal"&gt;&lt;span class="CodeEmbedded"&gt;&lt;span style="POSITION: relative; TOP: 0pt"&gt;and&lt;br /&gt;f.OrderDateKey=d.DateKey&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;&lt;p class="Code" style="MARGIN-BOTTOM: 0pt; LINE-HEIGHT: normal"&gt;&lt;span class="CodeEmbedded"&gt;&lt;span style="POSITION: relative; TOP: 0pt"&gt;group by&lt;br /&gt;p.ProductKey, d.CalendarYear, d.EnglishMonthName&lt;br /&gt;&lt;br /&gt;order by sum(f.SalesAmount) desc&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;&lt;p class="TableSpacingAfter"&gt;&lt;/p&gt;&lt;p class="Text"&gt;This type of query typically gets a hash join query plan and will&lt;br /&gt;fully parallelize. &lt;/p&gt;&lt;h5 style="MARGIN-LEFT: 0in"&gt;&lt;a name="_Toc198032341"&gt;Best Practice: Manage&lt;br /&gt;Multiple Servers Uniformly&lt;/a&gt;&lt;/h5&gt;&lt;h6 style="MARGIN-LEFT: 0in"&gt;&lt;a name="_Toc198032342"&gt;Use Policy-Based Management&lt;br /&gt;to enforce good practice across multiple servers&lt;/a&gt;&lt;/h6&gt;&lt;p class="Text"&gt;SQL Server 2008 introduces Policy-Based Management, which&lt;br /&gt;makes it possible to declare policies (such as "all log files must be&lt;br /&gt;stored on a disk other than the data disk") in one location and then apply&lt;br /&gt;them to multiple servers. So a (somewhat recursive) best practice is to set up best&lt;br /&gt;practices on one server and apply them to all servers. For example, you might build&lt;br /&gt;three data marts that draw data from a main data warehouse and use Policy-Based&lt;br /&gt;Management to apply the same rules to all the data marts.&lt;/p&gt;&lt;h5 style="MARGIN-LEFT: 0in"&gt;&lt;a name="_Toc198032343"&gt;Additional Resources&lt;/a&gt;&lt;/h5&gt;&lt;p class="Text"&gt;You can find additional tips mostly related to getting the best&lt;br /&gt;scalability and performance from SQL Server 2008 in the companion white&lt;br /&gt;paper [Han08]. These tips cover a range of topics including storage&lt;br /&gt;configuration, query formulation, indexing, aggregates, and more.&lt;/p&gt;&lt;h4 style="MARGIN-LEFT: 0in"&gt;&lt;a name="_Toc198032344"&gt;Analysis&lt;/a&gt;&lt;/h4&gt;&lt;p class="Text"&gt;There are several excellent white papers on Best Practices for&lt;br /&gt;analysis such as &lt;a id="ctl00_rs1_mainContentContainer_ctl02" onclick="javascript:Track('ctl00_rs1_mainContentContainer_ctl00ctl00_rs1_mainContentContainer_ctl02',this);" href="http://www.microsoft.com/technet/prodtechnol/sql/bestpractice/olapdbpssas2005.mspx"&gt;OLAP&lt;br /&gt;Design Best Practices for Analysis Services 2005&lt;/a&gt;, &lt;a id="ctl00_rs1_mainContentContainer_ctl03" onclick="javascript:Track('ctl00_rs1_mainContentContainer_ctl00ctl00_rs1_mainContentContainer_ctl03',this);" href="http://www.microsoft.com/technet/prodtechnol/sql/bestpractice/ssaspbpr.mspx"&gt;Analysis&lt;br /&gt;Services Processing Best Practices &lt;/a&gt;, and &lt;a id="ctl00_rs1_mainContentContainer_ctl04" onclick="javascript:Track('ctl00_rs1_mainContentContainer_ctl00ctl00_rs1_mainContentContainer_ctl04',this);" href="http://www.microsoft.com/technet/prodtechnol/sql/bestpractice/ssasqptb.mspx"&gt;Analysis&lt;br /&gt;Services Query Performance Top 10 Best Practices&lt;/a&gt;. Rather than repeat their&lt;br /&gt;content, in this paper we focus more specifically on best practices for&lt;br /&gt;analysis in SQL Server 2008.&lt;/p&gt;&lt;h5 style="MARGIN-LEFT: 0in"&gt;&lt;a name="_Toc198032345"&gt;Best Practices: Analysis&lt;/a&gt;&lt;/h5&gt;&lt;h6 style="MARGIN-LEFT: 0in"&gt;&lt;a name="_Toc198032346"&gt;Seriously consider the best&lt;br /&gt;practice advice offered by AMO warnings&lt;/a&gt;&lt;/h6&gt;&lt;p class="Text"&gt;Good design is fundamental to robust, high-performance systems,&lt;br /&gt;and the dissemination of best practice guidelines encourages good design. A&lt;br /&gt;whole new way of indicating where following best practice could help is&lt;br /&gt;incorporated into SQL Server 2008 Analysis Services.&lt;/p&gt;&lt;p class="Text"&gt;SQL Server 2008 Analysis Services shows real-time suggestions&lt;br /&gt;and warnings about design and best practices as you work. These are implemented&lt;br /&gt;in Analysis Management Objects (AMO) and displayed in the UI as blue wiggly underlines:&lt;br /&gt;hovering over an underlined object displays the warning. For instance, a cube&lt;br /&gt;name might be underlined and the warning might say:&lt;/p&gt;&lt;p class="Text" style="MARGIN: 3pt 50pt 3pt 0.5in"&gt;&lt;i&gt;The ‘SalesProfit’ and ‘Profit’ measure groups have the&lt;br /&gt;same dimensionality and granularity. Consider unifying them to improve&lt;br /&gt;performance. Avoid cubes with a single dimension. &lt;/i&gt;&lt;/p&gt;&lt;p class="Text"&gt;Over 40 different warnings indicate where best practice is&lt;br /&gt;not being followed. Warnings can be dismissed individually or turned off&lt;br /&gt;globally, but our recommendation is to follow them unless you have an active&lt;br /&gt;reason not to do so. &lt;/p&gt;&lt;h6 style="MARGIN-LEFT: 0in"&gt;&lt;a name="_Toc198032347"&gt;Use MOLAP writeback instead&lt;br /&gt;of ROLAP writeback&lt;/a&gt;&lt;/h6&gt;&lt;p class="Text"&gt;For certain classes of business applications (forecasting,&lt;br /&gt;budgeting, what if, and so on) the ability to write data back to the cube can be&lt;br /&gt;highly advantageous. &lt;/p&gt;&lt;p class="Text"&gt;It has for some time been possible to write back cell values to&lt;br /&gt;the cube, both at the leaf and aggregation levels. A special writeback&lt;br /&gt;partition is used to store the difference (the deltas) between the original and&lt;br /&gt;the new value. This mechanism means that the original value is still present in&lt;br /&gt;the cube; if an MDX query requests the new value, it hits both partitions and&lt;br /&gt;returns the aggregated value of the original and the delta.&lt;/p&gt;&lt;p class="Text"&gt;However, in many cases, despite the business need, performance&lt;br /&gt;considerations have limited the use of writeback. &lt;/p&gt;&lt;p class="Text"&gt;In the previous implementation, the writeback partition had to&lt;br /&gt;use ROLAP storage and this was frequently a cause of poor performance. To&lt;br /&gt;retrieve data it was necessary to query the relational data source and that can&lt;br /&gt;be slow. In SQL Server 2008 Analysis Services, writeback partitions can be&lt;br /&gt;stored as MOLAP, which removes this bottleneck. &lt;/p&gt;&lt;p class="Text"&gt;While it is true that this configuration can slow the writeback&lt;br /&gt;commit operation fractionally (both the writeback table and the MOLAP partition&lt;br /&gt;must be updated), query performance dramatically improves in the majority of&lt;br /&gt;cases. One in-house test of a 2 million cell update showed a five-fold&lt;br /&gt;improvement in performance.&lt;/p&gt;&lt;h6 style="MARGIN-LEFT: 0in"&gt;&lt;a name="_Toc198032348"&gt;Use SQL Server 2008 Analysis&lt;br /&gt;Services backup rather than file copy&lt;/a&gt; &lt;/h6&gt;&lt;p class="Text"&gt;In SQL Server 2008 Analysis Services, the backup storage&lt;br /&gt;subsystem has been rewritten and its performance has improved dramatically as Figure 5&lt;br /&gt;shows. &lt;/p&gt;&lt;p class="Figure"&gt;&lt;img id="Picture 3" height="445" src="http://i.technet.microsoft.com/Cc719165.BestPractDWSQL2008Fig05%28en-us,SQL.100%29.jpg" width="594" border="0" /&gt;&lt;/p&gt;&lt;p class="Label"&gt;&lt;b&gt;Figure 5:&lt;/b&gt; Backup performance in SQL Server 2005 Analysis&lt;br /&gt;Services versus SQL Server 2008 Analysis Services. X = Time Y=Data volume&lt;/p&gt;&lt;p class="Text"&gt;Backup now scales linearly and can handle an Analysis Services&lt;br /&gt;database of more than a terabyte. In addition, the limitations on backup size&lt;br /&gt;and metadata files have been removed. Systems handling large data volumes can&lt;br /&gt;now adopt the backup system and abandon raw file system copying, and enjoy the&lt;br /&gt;benefits of integration with the transactional system and of running backup in&lt;br /&gt;parallel with other operations.&lt;/p&gt;&lt;p class="Text"&gt;Although the file extension is unaltered, the format of the&lt;br /&gt;backup file has changed. It is fully backwardly compatible with the previous&lt;br /&gt;format so it is possible to restore a database backed up in SQL&lt;br /&gt;Server 2005 Analysis Services to SQL Server 2005 Analysis Services. It&lt;br /&gt;is not, however, possible to save files in the old format. &lt;/p&gt;&lt;h6 style="MARGIN-LEFT: 0in"&gt;&lt;a name="_Toc198032349"&gt;Write simpler MDX without&lt;br /&gt;worrying about performance&lt;/a&gt;&lt;/h6&gt;&lt;p class="Text"&gt;A new feature, block computation (also known as &lt;i&gt;subspace&lt;br /&gt;computation&lt;/i&gt;), has been implemented in SQL Server 2008 Analysis&lt;br /&gt;Services and one of the main benefits it brings is improved MDX query&lt;br /&gt;performance. In SQL Server 2005 complex workarounds were possible in some&lt;br /&gt;cases; now the MDX can be written much more simply.&lt;/p&gt;&lt;p class="Text"&gt;Cubes commonly contain sparse data: there are often relatively&lt;br /&gt;few values in a vast sea of nulls. In SQL Server 2005 Analysis Services,&lt;br /&gt;queries that touched a large number of nulls could perform poorly for the&lt;br /&gt;simple reason that even if it was logically pointless to perform a calculation&lt;br /&gt;on a null value, the query would nevertheless process all the null values in&lt;br /&gt;the same way as non-nulls.&lt;/p&gt;&lt;p class="Text"&gt;Block computation addresses this issue and greatly enhances the&lt;br /&gt;performance of these queries. The internal structure of a cube is highly&lt;br /&gt;complex and the description that follows of how block computation works is a&lt;br /&gt;simplified version of the full story. It does, however, give an idea of how the&lt;br /&gt;speed gain is achieved.&lt;/p&gt;&lt;p class="Text"&gt;This MDX calculation calculates a running total for two&lt;br /&gt;consecutive years:&lt;/p&gt;&lt;p class="Code"&gt;CREATE MEMBER CURRENTCUBE.[Measures].RM&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;AS ([Order Date].[Hierarchy].PrevMember,[Measures].[Order Quantity])+&lt;br /&gt;&lt;br /&gt;Measures.[Order Quantity],&lt;br /&gt;&lt;br /&gt;FORMAT_STRING = "Standard",&lt;br /&gt;&lt;br /&gt;VISIBLE = 2 ;&lt;/p&gt;&lt;p class="Text"&gt;These tables show orders of a small subset of products and&lt;br /&gt;illustrate that there are very few values for most products in 2003 and 2004:&lt;/p&gt;&lt;p class="TableSpacing"&gt;&lt;/p&gt;&lt;table class="MsoNormalTable" style="BORDER-RIGHT: medium none; BORDER-TOP: medium none; MARGIN-LEFT: 41.4pt; BORDER-LEFT: medium none; BORDER-BOTTOM: medium none; BORDER-COLLAPSE: collapse" border="1"&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td style="BORDER-RIGHT: 1pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0in; BORDER-LEFT: medium none; WIDTH: 69.3pt; PADDING-TOP: 0in; BORDER-BOTTOM: medium none" valign="top" width="92"&gt;&lt;p class="Label"&gt;&lt;/p&gt;&lt;/td&gt;&lt;td style="BORDER-RIGHT: windowtext 1pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: windowtext 1pt solid; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0in; BORDER-LEFT: medium none; WIDTH: 38.7pt; PADDING-TOP: 0in; BORDER-BOTTOM: windowtext 1pt solid" valign="top" width="52"&gt;&lt;p class="Label"&gt;2003&lt;/p&gt;&lt;/td&gt;&lt;td style="BORDER-RIGHT: 1pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0in; BORDER-LEFT: medium none; WIDTH: 27pt; PADDING-TOP: 0in; BORDER-BOTTOM: medium none" valign="top" width="36"&gt;&lt;p class="Label"&gt;&lt;/p&gt;&lt;/td&gt;&lt;td style="BORDER-RIGHT: windowtext 1pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: windowtext 1pt solid; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0in; BORDER-LEFT: medium none; WIDTH: 0.5in; PADDING-TOP: 0in; BORDER-BOTTOM: windowtext 1pt solid" valign="top" width="48"&gt;&lt;p class="Label"&gt;2004&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td style="BORDER-RIGHT: 1pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0in; BORDER-LEFT: medium none; WIDTH: 69.3pt; PADDING-TOP: 0in; BORDER-BOTTOM: medium none" valign="top" width="92"&gt;&lt;p class="Text"&gt;Product 1&lt;/p&gt;&lt;/td&gt;&lt;td style="BORDER-RIGHT: 1pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0in; BORDER-LEFT: medium none; WIDTH: 38.7pt; PADDING-TOP: 0in; BORDER-BOTTOM: 1pt solid" valign="top" width="52"&gt;&lt;p class="Text"&gt;&lt;/p&gt;&lt;/td&gt;&lt;td style="BORDER-RIGHT: 1pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0in; BORDER-LEFT: medium none; WIDTH: 27pt; PADDING-TOP: 0in; BORDER-BOTTOM: medium none" valign="top" width="36"&gt;&lt;p class="Text"&gt;&lt;/p&gt;&lt;/td&gt;&lt;td style="BORDER-RIGHT: 1pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0in; BORDER-LEFT: medium none; WIDTH: 0.5in; PADDING-TOP: 0in; BORDER-BOTTOM: 1pt solid" valign="top" width="48"&gt;&lt;p class="Text"&gt;&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td style="BORDER-RIGHT: 1pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0in; BORDER-LEFT: medium none; WIDTH: 69.3pt; PADDING-TOP: 0in; BORDER-BOTTOM: medium none" valign="top" width="92"&gt;&lt;p class="Text"&gt;Product 2&lt;/p&gt;&lt;/td&gt;&lt;td style="BORDER-RIGHT: 1pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0in; BORDER-LEFT: medium none; WIDTH: 38.7pt; PADDING-TOP: 0in; BORDER-BOTTOM: 1pt solid" valign="top" width="52"&gt;&lt;p class="Text"&gt;2&lt;/p&gt;&lt;/td&gt;&lt;td style="BORDER-RIGHT: 1pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0in; BORDER-LEFT: medium none; WIDTH: 27pt; PADDING-TOP: 0in; BORDER-BOTTOM: medium none" valign="top" width="36"&gt;&lt;p class="Text"&gt;&lt;/p&gt;&lt;/td&gt;&lt;td style="BORDER-RIGHT: 1pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0in; BORDER-LEFT: medium none; WIDTH: 0.5in; PADDING-TOP: 0in; BORDER-BOTTOM: 1pt solid" valign="top" width="48"&gt;&lt;p class="Text"&gt;&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td style="BORDER-RIGHT: 1pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0in; BORDER-LEFT: medium none; WIDTH: 69.3pt; PADDING-TOP: 0in; BORDER-BOTTOM: medium none" valign="top" width="92"&gt;&lt;p class="Text"&gt;Product 3&lt;/p&gt;&lt;/td&gt;&lt;td style="BORDER-RIGHT: 1pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0in; BORDER-LEFT: medium none; WIDTH: 38.7pt; PADDING-TOP: 0in; BORDER-BOTTOM: 1pt solid" valign="top" width="52"&gt;&lt;p class="Text"&gt;&lt;/p&gt;&lt;/td&gt;&lt;td style="BORDER-RIGHT: 1pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0in; BORDER-LEFT: medium none; WIDTH: 27pt; PADDING-TOP: 0in; BORDER-BOTTOM: medium none" valign="top" width="36"&gt;&lt;p class="Text"&gt;&lt;/p&gt;&lt;/td&gt;&lt;td style="BORDER-RIGHT: 1pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0in; BORDER-LEFT: medium none; WIDTH: 0.5in; PADDING-TOP: 0in; BORDER-BOTTOM: 1pt solid" valign="top" width="48"&gt;&lt;p class="Text"&gt;&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td style="BORDER-RIGHT: 1pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0in; BORDER-LEFT: medium none; WIDTH: 69.3pt; PADDING-TOP: 0in; BORDER-BOTTOM: medium none" valign="top" width="92"&gt;&lt;p class="Text"&gt;Product 4&lt;/p&gt;&lt;/td&gt;&lt;td style="BORDER-RIGHT: 1pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0in; BORDER-LEFT: medium none; WIDTH: 38.7pt; PADDING-TOP: 0in; BORDER-BOTTOM: 1pt solid" valign="top" width="52"&gt;&lt;p class="Text"&gt;&lt;/p&gt;&lt;/td&gt;&lt;td style="BORDER-RIGHT: 1pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0in; BORDER-LEFT: medium none; WIDTH: 27pt; PADDING-TOP: 0in; BORDER-BOTTOM: medium none" valign="top" width="36"&gt;&lt;p class="Text"&gt;&lt;/p&gt;&lt;/td&gt;&lt;td style="BORDER-RIGHT: 1pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0in; BORDER-LEFT: medium none; WIDTH: 0.5in; PADDING-TOP: 0in; BORDER-BOTTOM: 1pt solid" valign="top" width="48"&gt;&lt;p class="Text"&gt;5&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td style="BORDER-RIGHT: 1pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0in; BORDER-LEFT: medium none; WIDTH: 69.3pt; PADDING-TOP: 0in; BORDER-BOTTOM: medium none" valign="top" width="92"&gt;&lt;p class="Text"&gt;Product 5&lt;/p&gt;&lt;/td&gt;&lt;td style="BORDER-RIGHT: 1pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0in; BORDER-LEFT: medium none; WIDTH: 38.7pt; PADDING-TOP: 0in; BORDER-BOTTOM: 1pt solid" valign="top" width="52"&gt;&lt;p class="Text"&gt;&lt;/p&gt;&lt;/td&gt;&lt;td style="BORDER-RIGHT: 1pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0in; BORDER-LEFT: medium none; WIDTH: 27pt; PADDING-TOP: 0in; BORDER-BOTTOM: medium none" valign="top" width="36"&gt;&lt;p class="Text"&gt;&lt;/p&gt;&lt;/td&gt;&lt;td style="BORDER-RIGHT: 1pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0in; BORDER-LEFT: medium none; WIDTH: 0.5in; PADDING-TOP: 0in; BORDER-BOTTOM: 1pt solid" valign="top" width="48"&gt;&lt;p class="Text"&gt;&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td style="BORDER-RIGHT: 1pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0in; BORDER-LEFT: medium none; WIDTH: 69.3pt; PADDING-TOP: 0in; BORDER-BOTTOM: medium none" valign="top" width="92"&gt;&lt;p class="Text"&gt;Product 6&lt;/p&gt;&lt;/td&gt;&lt;td style="BORDER-RIGHT: 1pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0in; BORDER-LEFT: medium none; WIDTH: 38.7pt; PADDING-TOP: 0in; BORDER-BOTTOM: 1pt solid" valign="top" width="52"&gt;&lt;p class="Text"&gt;&lt;/p&gt;&lt;/td&gt;&lt;td style="BORDER-RIGHT: 1pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0in; BORDER-LEFT: medium none; WIDTH: 27pt; PADDING-TOP: 0in; BORDER-BOTTOM: medium none" valign="top" width="36"&gt;&lt;p class="Text"&gt;&lt;/p&gt;&lt;/td&gt;&lt;td style="BORDER-RIGHT: 1pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0in; BORDER-LEFT: medium none; WIDTH: 0.5in; PADDING-TOP: 0in; BORDER-BOTTOM: 1pt solid" valign="top" width="48"&gt;&lt;p class="Text"&gt;&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td style="BORDER-RIGHT: 1pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0in; BORDER-LEFT: medium none; WIDTH: 69.3pt; PADDING-TOP: 0in; BORDER-BOTTOM: medium none" valign="top" width="92"&gt;&lt;p class="Text"&gt;Product 7&lt;/p&gt;&lt;/td&gt;&lt;td style="BORDER-RIGHT: 1pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0in; BORDER-LEFT: medium none; WIDTH: 38.7pt; PADDING-TOP: 0in; BORDER-BOTTOM: 1pt solid" valign="top" width="52"&gt;&lt;p class="Text"&gt;3&lt;/p&gt;&lt;/td&gt;&lt;td style="BORDER-RIGHT: 1pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0in; BORDER-LEFT: medium none; WIDTH: 27pt; PADDING-TOP: 0in; BORDER-BOTTOM: medium none" valign="top" width="36"&gt;&lt;p class="Text"&gt;&lt;/p&gt;&lt;/td&gt;&lt;td style="BORDER-RIGHT: 1pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0in; BORDER-LEFT: medium none; WIDTH: 0.5in; PADDING-TOP: 0in; BORDER-BOTTOM: 1pt solid" valign="top" width="48"&gt;&lt;p class="Text"&gt;1&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td style="BORDER-RIGHT: 1pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0in; BORDER-LEFT: medium none; WIDTH: 69.3pt; PADDING-TOP: 0in; BORDER-BOTTOM: medium none" valign="top" width="92"&gt;&lt;p class="Text"&gt;Product 8&lt;/p&gt;&lt;/td&gt;&lt;td style="BORDER-RIGHT: 1pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0in; BORDER-LEFT: medium none; WIDTH: 38.7pt; PADDING-TOP: 0in; BORDER-BOTTOM: 1pt solid" valign="top" width="52"&gt;&lt;p class="Text"&gt;&lt;/p&gt;&lt;/td&gt;&lt;td style="BORDER-RIGHT: 1pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0in; BORDER-LEFT: medium none; WIDTH: 27pt; PADDING-TOP: 0in; BORDER-BOTTOM: medium none" valign="top" width="36"&gt;&lt;p class="Text"&gt;&lt;/p&gt;&lt;/td&gt;&lt;td style="BORDER-RIGHT: 1pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0in; BORDER-LEFT: medium none; WIDTH: 0.5in; PADDING-TOP: 0in; BORDER-BOTTOM: 1pt solid" valign="top" width="48"&gt;&lt;p class="Text"&gt;&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td style="BORDER-RIGHT: 1pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0in; BORDER-LEFT: medium none; WIDTH: 69.3pt; PADDING-TOP: 0in; BORDER-BOTTOM: medium none" valign="top" width="92"&gt;&lt;p class="Text"&gt;Product 9&lt;/p&gt;&lt;/td&gt;&lt;td style="BORDER-RIGHT: 1pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0in; BORDER-LEFT: medium none; WIDTH: 38.7pt; PADDING-TOP: 0in; BORDER-BOTTOM: 1pt solid" valign="top" width="52"&gt;&lt;p class="Text"&gt;&lt;/p&gt;&lt;/td&gt;&lt;td style="BORDER-RIGHT: 1pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0in; BORDER-LEFT: medium none; WIDTH: 27pt; PADDING-TOP: 0in; BORDER-BOTTOM: medium none" valign="top" width="36"&gt;&lt;p class="Text"&gt;&lt;/p&gt;&lt;/td&gt;&lt;td style="BORDER-RIGHT: 1pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0in; BORDER-LEFT: medium none; WIDTH: 0.5in; PADDING-TOP: 0in; BORDER-BOTTOM: 1pt solid" valign="top" width="48"&gt;&lt;p class="Text"&gt;&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;p class="TableSpacing"&gt;&lt;/p&gt;&lt;p class="Text"&gt;This query:&lt;/p&gt;&lt;p class="Code"&gt;SELECT [Order Date].[Hierarchy].[all].[2004] on columns,&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;[Dim Product].[Product Key].[All].children on rows&lt;br /&gt;&lt;br /&gt;From Foo&lt;br /&gt;&lt;br /&gt;Where Measures.RM&lt;/p&gt;&lt;p class="Text"&gt;returns the calculated measure RM for all products ordered in&lt;br /&gt;2004. &lt;/p&gt;&lt;p class="Text"&gt;In SQL Server 2005 Analysis Services, the result is&lt;br /&gt;generated by taking the value from a cell for 2004 (the current year), finding&lt;br /&gt;the value in the corresponding cell for the previous year and summing the&lt;br /&gt;values. Each cell is handled in this way.&lt;/p&gt;&lt;p class="Text"&gt;This approach contains two processes that are slow to perform. Firstly,&lt;br /&gt;for every cell, the query processor navigates to the previous period to see if&lt;br /&gt;a value is present. In most hierarchical structures we know that if data is&lt;br /&gt;present for one cell in 2003, it will be there for all cells in 2003. The trip&lt;br /&gt;to see if there is data for the previous period only needs to be carried out&lt;br /&gt;once, which is what happens in SQL Server 2008 Analysis Services. This&lt;br /&gt;facet of block computation speeds up queries regardless of the proportion of&lt;br /&gt;null values in the cube.&lt;/p&gt;&lt;p class="Text"&gt;Secondly, the sum of each pair of values is calculated. With a&lt;br /&gt;sparse cube, a high proportion of the calculations result in a null, and time&lt;br /&gt;is wasted making these calculations. If we look at the previous tables, it is&lt;br /&gt;easy to see that only the calculations for products 2, 4, and 7 will&lt;br /&gt;deliver anything that is not null. So in SQL Server 2008 Analysis Services,&lt;br /&gt;before any calculations are performed, null rows are removed from the data for&lt;br /&gt;both years:&lt;/p&gt;&lt;p class="TableSpacing"&gt;&lt;/p&gt;&lt;table class="MsoNormalTable" style="BORDER-RIGHT: medium none; BORDER-TOP: medium none; MARGIN-LEFT: 41.4pt; BORDER-LEFT: medium none; BORDER-BOTTOM: medium none; BORDER-COLLAPSE: collapse" border="1"&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td style="BORDER-RIGHT: 1pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0in; BORDER-LEFT: medium none; WIDTH: 77.4pt; PADDING-TOP: 0in; BORDER-BOTTOM: medium none" valign="top" width="103"&gt;&lt;p class="Label"&gt;&lt;/p&gt;&lt;/td&gt;&lt;td style="BORDER-RIGHT: windowtext 1pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: windowtext 1pt solid; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0in; BORDER-LEFT: medium none; WIDTH: 48.6pt; PADDING-TOP: 0in; BORDER-BOTTOM: windowtext 1pt solid" valign="top" width="65"&gt;&lt;p class="Label"&gt;2003&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td style="BORDER-RIGHT: 1pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0in; BORDER-LEFT: medium none; WIDTH: 77.4pt; PADDING-TOP: 0in; BORDER-BOTTOM: medium none" valign="top" width="103"&gt;&lt;p class="Text"&gt;Product 2&lt;/p&gt;&lt;/td&gt;&lt;td style="BORDER-RIGHT: 1pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0in; BORDER-LEFT: medium none; WIDTH: 48.6pt; PADDING-TOP: 0in; BORDER-BOTTOM: 1pt solid" valign="top" width="65"&gt;&lt;p class="Text"&gt;2&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td style="BORDER-RIGHT: 1pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0in; BORDER-LEFT: medium none; WIDTH: 77.4pt; PADDING-TOP: 0in; BORDER-BOTTOM: medium none" valign="top" width="103"&gt;&lt;p class="Text"&gt;Product 7&lt;/p&gt;&lt;/td&gt;&lt;td style="BORDER-RIGHT: 1pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0in; BORDER-LEFT: medium none; WIDTH: 48.6pt; PADDING-TOP: 0in; BORDER-BOTTOM: 1pt solid" valign="top" width="65"&gt;&lt;p class="Text"&gt;3&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;p class="TableSpacing"&gt;&lt;/p&gt;&lt;p class="TableSpacing"&gt;&lt;/p&gt;&lt;table class="MsoNormalTable" style="BORDER-RIGHT: medium none; BORDER-TOP: medium none; MARGIN-LEFT: 41.4pt; BORDER-LEFT: medium none; BORDER-BOTTOM: medium none; BORDER-COLLAPSE: collapse" border="1"&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td style="BORDER-RIGHT: 1pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0in; BORDER-LEFT: medium none; WIDTH: 77.4pt; PADDING-TOP: 0in; BORDER-BOTTOM: medium none" valign="top" width="103"&gt;&lt;p class="Label"&gt;&lt;/p&gt;&lt;/td&gt;&lt;td style="BORDER-RIGHT: windowtext 1pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: windowtext 1pt solid; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0in; BORDER-LEFT: medium none; WIDTH: 48.6pt; PADDING-TOP: 0in; BORDER-BOTTOM: windowtext 1pt solid" valign="top" width="65"&gt;&lt;p class="Label"&gt;2004&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td style="BORDER-RIGHT: 1pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0in; BORDER-LEFT: medium none; WIDTH: 77.4pt; PADDING-TOP: 0in; BORDER-BOTTOM: medium none" valign="top" width="103"&gt;&lt;p class="Text"&gt;Product 4&lt;/p&gt;&lt;/td&gt;&lt;td style="BORDER-RIGHT: 1pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0in; BORDER-LEFT: medium none; WIDTH: 48.6pt; PADDING-TOP: 0in; BORDER-BOTTOM: 1pt solid" valign="top" width="65"&gt;&lt;p class="Text"&gt;5&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td style="BORDER-RIGHT: 1pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0in; BORDER-LEFT: medium none; WIDTH: 77.4pt; PADDING-TOP: 0in; BORDER-BOTTOM: medium none" valign="top" width="103"&gt;&lt;p class="Text"&gt;Product 7&lt;/p&gt;&lt;/td&gt;&lt;td style="BORDER-RIGHT: 1pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0in; BORDER-LEFT: medium none; WIDTH: 48.6pt; PADDING-TOP: 0in; BORDER-BOTTOM: 1pt solid" valign="top" width="65"&gt;&lt;p class="Text"&gt;1&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;p class="TableSpacing"&gt;&lt;/p&gt;&lt;p class="Text"&gt;The results are compared:&lt;/p&gt;&lt;p class="TableSpacing"&gt;&lt;/p&gt;&lt;table class="MsoNormalTable" style="BORDER-RIGHT: medium none; BORDER-TOP: medium none; MARGIN-LEFT: 41.4pt; BORDER-LEFT: medium none; BORDER-BOTTOM: medium none; BORDER-COLLAPSE: collapse" border="1"&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td style="BORDER-RIGHT: 1pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0in; BORDER-LEFT: medium none; WIDTH: 77.4pt; PADDING-TOP: 0in; BORDER-BOTTOM: medium none" valign="top" width="103"&gt;&lt;p class="Label"&gt;&lt;/p&gt;&lt;/td&gt;&lt;td style="BORDER-RIGHT: windowtext 1pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: windowtext 1pt solid; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0in; BORDER-LEFT: medium none; WIDTH: 48.6pt; PADDING-TOP: 0in; BORDER-BOTTOM: windowtext 1pt solid" valign="top" width="65"&gt;&lt;p class="Label"&gt;2003&lt;/p&gt;&lt;/td&gt;&lt;td style="BORDER-RIGHT: 1pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0in; BORDER-LEFT: medium none; WIDTH: 53.4pt; PADDING-TOP: 0in; BORDER-BOTTOM: medium none" valign="top" width="71"&gt;&lt;p class="Label"&gt;&lt;/p&gt;&lt;/td&gt;&lt;td style="BORDER-RIGHT: windowtext 1pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: windowtext 1pt solid; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0in; BORDER-LEFT: medium none; WIDTH: 46.85pt; PADDING-TOP: 0in; BORDER-BOTTOM: windowtext 1pt solid" valign="top" width="62"&gt;&lt;p class="Label"&gt;2004&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td style="BORDER-RIGHT: 1pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0in; BORDER-LEFT: medium none; WIDTH: 77.4pt; PADDING-TOP: 0in; BORDER-BOTTOM: medium none" valign="top" width="103"&gt;&lt;p class="Text"&gt;Product 2&lt;/p&gt;&lt;/td&gt;&lt;td style="BORDER-RIGHT: 1pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0in; BORDER-LEFT: medium none; WIDTH: 48.6pt; PADDING-TOP: 0in; BORDER-BOTTOM: 1pt solid" valign="top" width="65"&gt;&lt;p class="Text"&gt;2&lt;/p&gt;&lt;/td&gt;&lt;td style="BORDER-RIGHT: 1pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0in; BORDER-LEFT: medium none; WIDTH: 53.4pt; PADDING-TOP: 0in; BORDER-BOTTOM: medium none" valign="top" width="71"&gt;&lt;p class="Text"&gt;&lt;/p&gt;&lt;/td&gt;&lt;td style="BORDER-RIGHT: 1pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0in; BORDER-LEFT: medium none; WIDTH: 46.85pt; PADDING-TOP: 0in; BORDER-BOTTOM: 1pt solid" valign="top" width="62"&gt;&lt;p class="Text"&gt;&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td style="BORDER-RIGHT: 1pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0in; BORDER-LEFT: medium none; WIDTH: 77.4pt; PADDING-TOP: 0in; BORDER-BOTTOM: medium none" valign="top" width="103"&gt;&lt;p class="Text"&gt;Product 4&lt;/p&gt;&lt;/td&gt;&lt;td style="BORDER-RIGHT: 1pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0in; BORDER-LEFT: medium none; WIDTH: 48.6pt; PADDING-TOP: 0in; BORDER-BOTTOM: 1pt solid" valign="top" width="65"&gt;&lt;p class="Text"&gt;&lt;/p&gt;&lt;/td&gt;&lt;td style="BORDER-RIGHT: 1pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0in; BORDER-LEFT: medium none; WIDTH: 53.4pt; PADDING-TOP: 0in; BORDER-BOTTOM: medium none" valign="top" width="71"&gt;&lt;p class="Text"&gt;&lt;/p&gt;&lt;/td&gt;&lt;td style="BORDER-RIGHT: 1pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0in; BORDER-LEFT: medium none; WIDTH: 46.85pt; PADDING-TOP: 0in; BORDER-BOTTOM: 1pt solid" valign="top" width="62"&gt;&lt;p class="Text"&gt;5&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td style="BORDER-RIGHT: 1pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0in; BORDER-LEFT: medium none; WIDTH: 77.4pt; PADDING-TOP: 0in; BORDER-BOTTOM: medium none" valign="top" width="103"&gt;&lt;p class="Text"&gt;Product 7&lt;/p&gt;&lt;/td&gt;&lt;td style="BORDER-RIGHT: 1pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0in; BORDER-LEFT: medium none; WIDTH: 48.6pt; PADDING-TOP: 0in; BORDER-BOTTOM: 1pt solid" valign="top" width="65"&gt;&lt;p class="Text"&gt;3&lt;/p&gt;&lt;/td&gt;&lt;td style="BORDER-RIGHT: 1pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0in; BORDER-LEFT: medium none; WIDTH: 53.4pt; PADDING-TOP: 0in; BORDER-BOTTOM: medium none" valign="top" width="71"&gt;&lt;p class="Text"&gt;&lt;/p&gt;&lt;/td&gt;&lt;td style="BORDER-RIGHT: 1pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: medium none; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0in; BORDER-LEFT: medium none; WIDTH: 46.85pt; PADDING-TOP: 0in; BORDER-BOTTOM: 1pt solid" valign="top" width="62"&gt;&lt;p class="Text"&gt;1&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;p class="TableSpacing"&gt;&lt;/p&gt;&lt;p class="Text"&gt;and the calculations performed only for the rows that will&lt;br /&gt;generate a meaningful result.&lt;/p&gt;&lt;p class="Text"&gt;The speed gain can be impressive: under testing a query that took&lt;br /&gt;two minutes and 16 seconds in SQL Server 2005 Analysis Services took&lt;br /&gt;a mere eight seconds in SQL Server 2008 Analysis Services.&lt;/p&gt;&lt;h6 style="MARGIN-LEFT: 0in"&gt;&lt;a name="_Toc198032350"&gt;Scale out if you need more&lt;br /&gt;hardware capacity and hardware price is important&lt;/a&gt;&lt;/h6&gt;&lt;p class="Text"&gt;To improve performance under load, you can either scale up or&lt;br /&gt;scale out. Scale up is undeniably simple: put the cube on an extra large high-performance&lt;br /&gt;server. This is an excellent solution—it is quick and easy and is the correct&lt;br /&gt;decision in many cases. However, it is also expensive because these servers&lt;br /&gt;cost more per CPU than multiple smaller servers.&lt;/p&gt;&lt;p class="Text"&gt;Previous version of Analysis Services offered a scale-out&lt;br /&gt;solution that used multiple cost-effective servers. The data was replicated&lt;br /&gt;across the servers and a load balancing solution such as Microsoft Network Load&lt;br /&gt;Balancing (NLB) was installed between the clients and the servers. This worked&lt;br /&gt;but incurred the additional costs of set up and ongoing maintenance.&lt;/p&gt;&lt;p class="Text"&gt;SQL Server 2008 Analysis Services has a new scale-out&lt;br /&gt;solution called Scalable Shared Database (SSD). Its workings are very similar&lt;br /&gt;to the SSD feature in the SQL Server 2005 relational database engine. It&lt;br /&gt;comprises three components:&lt;/p&gt;&lt;p class="BulletedList1"&gt;&lt;span style="font-family:Symbol;"&gt;·&lt;span style="FONT: 7pt 'Times New Roman'; font-size-adjust: none; font-stretch: normal"&gt;&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;Read-only database – enables a database to be designated&lt;br /&gt;‘read-only’&lt;/p&gt;&lt;p class="BulletedList1"&gt;&lt;span style="font-family:Symbol;"&gt;·&lt;span style="FONT: 7pt 'Times New Roman'; font-size-adjust: none; font-stretch: normal"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;Database storage location – enables a database to reside outside&lt;br /&gt;the server Data folder&lt;/p&gt;&lt;p class="BulletedList1"&gt;&lt;span style="font-family:Symbol;"&gt;·&lt;span style="FONT: 7pt 'Times New Roman'; font-size-adjust: none; font-stretch: normal"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;Attach/detach database - a database can be attached or detached&lt;br /&gt;from any UNC path&lt;/p&gt;&lt;p class="Text"&gt;Used together, these components make it easier to build a&lt;br /&gt;scale-out solution for read-only Analysis Services cubes. For example, you can&lt;br /&gt;connect four blade servers to a shared, read-only database on a SAN, and direct&lt;br /&gt;SSAS queries to any of the four, thereby improving your total throughput by a&lt;br /&gt;factor of four with inexpensive hardware. The best possible query response time&lt;br /&gt;remains constrained by the capabilities of an individual server, since only one&lt;br /&gt;server runs each query.&lt;/p&gt;&lt;h4 style="MARGIN-LEFT: 0in"&gt;&lt;a name="_Toc198032351"&gt;Reporting&lt;/a&gt;&lt;/h4&gt;&lt;p class="Text"&gt;This section offers best practices for different aspects of&lt;br /&gt;reporting such as improving performance.&lt;/p&gt;&lt;h5 style="MARGIN-LEFT: 0in"&gt;&lt;a name="_Toc198032352"&gt;Best Practices: Data Presentation&lt;/a&gt;&lt;/h5&gt;&lt;h6 style="MARGIN-LEFT: 0in"&gt;&lt;a name="_Toc198032353"&gt;Allow IT and business users&lt;br /&gt;to create both simple and complex reports&lt;/a&gt;&lt;/h6&gt;&lt;p class="Text"&gt;Reports have always fallen between two opinions—should IT people&lt;br /&gt;or business people design them? The problem is that business users understand&lt;br /&gt;the business context in which the data is used (the meaning of the data) while&lt;br /&gt;IT people understand the underlying data structure. &lt;/p&gt;&lt;p class="Text"&gt;To help business users, in SQL Server 2005, Microsoft&lt;br /&gt;introduced the concept of a report model. This was built by IT people for the&lt;br /&gt;business users who then wrote reports against it by using a tool called Report&lt;br /&gt;Builder. Report Builder will ship in the SQL Server 2008 box and will work&lt;br /&gt;as before. &lt;/p&gt;&lt;p class="Text"&gt;An additional tool, Report Builder, aimed squarely at the power&lt;br /&gt;user, has been enhanced in SQL Server 2008. This stand-alone product,&lt;br /&gt;complete with an Office System 12 interface, boasts the full layout&lt;br /&gt;capabilities of Report Designer and is available as a Web download.&lt;/p&gt;&lt;p class="Text"&gt;IT professionals were well served in SQL Server 2005 by a&lt;br /&gt;tool called Report Designer in Visual Studio, which allowed them to create very&lt;br /&gt;detailed reports. An enhanced version of Report Designer ships with SQL Server 2008&lt;br /&gt;and is shown in Figure 6.&lt;/p&gt;&lt;p class="Figure"&gt;&lt;img id="Picture 4" height="446" src="http://i.technet.microsoft.com/Cc719165.BestPractDWSQL2008Fig06%28en-us,SQL.100%29.jpg" width="594" border="0" /&gt;&lt;/p&gt;&lt;p class="Label"&gt;&lt;b&gt;Figure 6:&lt;/b&gt; Report Designer in Visual Studio&lt;/p&gt;&lt;h6 style="MARGIN-LEFT: 0in"&gt;&lt;a name="_Toc198032354"&gt;Present data in the most&lt;br /&gt;accessible way possible&lt;/a&gt;&lt;/h6&gt;&lt;p class="Text"&gt;Traditionally, grid data has been presented to users as tables, matrices,&lt;br /&gt;and lists. Each has its strengths, which is another way of saying that each has&lt;br /&gt;its weaknesses. SQL Server 2008 Reporting Services combines all three into&lt;br /&gt;a new data region called a &lt;i&gt;Tablix&lt;/i&gt;. &lt;/p&gt;&lt;p class="Text"&gt;In Figure 7, the table on the left shows percentage growth&lt;br /&gt;and the matrix on the right shows the actual figures.&lt;/p&gt;&lt;p class="Text"&gt;&lt;/p&gt;&lt;p class="Figure"&gt;&lt;img id="Picture 5" height="219" src="http://i.technet.microsoft.com/Cc719165.BestPractDWSQL2008Fig07%28en-us,SQL.100%29.jpg" width="594" border="0" /&gt;&lt;/p&gt;&lt;p class="Label"&gt;&lt;b&gt;Figure 7:&lt;/b&gt; A table and a matrix&lt;/p&gt;&lt;p class="Text"&gt;The Tablix shown in Figure 8 combines these and adds some totals.&lt;/p&gt;&lt;p class="Figure"&gt;&lt;img id="Picture 6" height="340" src="http://i.technet.microsoft.com/Cc719165.BestPractDWSQL2008Fig08%28en-us,SQL.100%29.jpg" width="594" border="0" /&gt;&lt;/p&gt;&lt;p class="Label"&gt;&lt;b&gt;Figure 8:&lt;/b&gt; A Tablix data region combining table and matrix&lt;br /&gt;features&lt;/p&gt;&lt;p class="Text"&gt;The Tablix data region gives users far more control over layout&lt;br /&gt;than tables, lists, and matrices. It also enables them to add column groups,&lt;br /&gt;specify arbitrary nesting on each axis, optionally omit rows and column headers,&lt;br /&gt;and have multiple parallel row/column members at each level. &lt;/p&gt;&lt;h6 style="MARGIN-LEFT: 0in"&gt;&lt;a name="_Toc198032355"&gt;Present data in reports that&lt;br /&gt;can be understood easily&lt;/a&gt;&lt;/h6&gt;&lt;p class="Text"&gt;The reason for generating reports is to transform data into&lt;br /&gt;information for the business user. Many people find graphics easier to&lt;br /&gt;understand than numbers. SQL Server 2008 Reporting Services introduces&lt;br /&gt;the &lt;i&gt;gauge&lt;/i&gt;, a new form of visual output. A gauge displays a single value&lt;br /&gt;from the data. As Figure 9 shows, gauges are particularly useful for&lt;br /&gt;creating easy-to-read displays that compare several values. &lt;/p&gt;&lt;p class="Figure"&gt;&lt;img id="Picture 7" height="374" src="http://i.technet.microsoft.com/Cc719165.BestPractDWSQL2008Fig09%28en-us,SQL.100%29.jpg" width="594" border="0" /&gt;&lt;/p&gt;&lt;p class="Label"&gt;&lt;b&gt;Figure 9:&lt;/b&gt; Examples of the gauges that can be created in SSRS&lt;/p&gt;&lt;p class="Text"&gt;Charts have also been extended to include polar, range, and&lt;br /&gt;shape. Figure 10 shows only a subset of those available.&lt;/p&gt;&lt;p class="Figure"&gt;&lt;img id="Picture 8" height="413" src="http://i.technet.microsoft.com/Cc719165.BestPractDWSQL2008Fig10%28en-us,SQL.100%29.jpg" width="594" border="0" /&gt;&lt;/p&gt;&lt;p class="Label"&gt;&lt;b&gt;Figure 10:&lt;/b&gt; A large number of charts are available in SQL&lt;br /&gt;Server 2008 Reporting Services&lt;/p&gt;&lt;p class="Text"&gt;Multiple data series can now be displayed on more than one axis,&lt;br /&gt;the user has control over scale breaks on the axis, and there is support for&lt;br /&gt;run-time formulae. &lt;/p&gt;&lt;h6 style="MARGIN-LEFT: 0in"&gt;&lt;a name="_Toc198032356"&gt;Present data to users in&lt;br /&gt;familiar environments&lt;/a&gt;&lt;/h6&gt;&lt;p class="Text"&gt;Reports can now be rendered as Word documents compatible with&lt;br /&gt;Word versions back to and including Word 2000. In addition, the existing&lt;br /&gt;Excel renderer has been enhanced and now supports features such as nested data&lt;br /&gt;regions, merged cells and subreports.&lt;/p&gt;&lt;h5 style="MARGIN-LEFT: 0in"&gt;&lt;a name="_Toc198032357"&gt;Best Practices: Performance&lt;/a&gt;&lt;/h5&gt;&lt;h6 style="MARGIN-LEFT: 0in"&gt;&lt;a name="_Toc198032358"&gt;Structure your query to&lt;br /&gt;return only the level of detail displayed in the report&lt;/a&gt;&lt;/h6&gt;&lt;p class="Text"&gt;Use report-level aggregation only for subtotals in the report,&lt;br /&gt;not for detail rows. Remember that the most commonly used aggregate, Sum, can&lt;br /&gt;be summed in any order and still yield the same result. While other commonly&lt;br /&gt;used aggregates cannot, they can often be decomposed into simpler, reusable&lt;br /&gt;components. &lt;/p&gt;&lt;p class="Text"&gt;For example, if you are trying to show an average at several&lt;br /&gt;grouping levels and also want subtotals, rather than returning detail rows and&lt;br /&gt;aggregating everything in the report, you can decompose the calculation into&lt;br /&gt;sums and counts. Then you can reconstitute the average in the report by using this&lt;br /&gt;kind of division:&lt;/p&gt;&lt;p class="TableSpacingAfter"&gt;&lt;/p&gt;&lt;p class="Code"&gt;Sum(Fields!Sum.Value)/Sum(Fields!Count.Value) &lt;/p&gt;&lt;p class="TableSpacingAfter"&gt;&lt;/p&gt;&lt;p class="Text"&gt;thereby avoiding the need to pass detail rows to the report. Let&lt;br /&gt;the database do the bulk summarization and aggregation, and use SSRS to&lt;br /&gt;assemble and format results for display.&lt;/p&gt;&lt;h6 style="MARGIN-LEFT: 0in"&gt;&lt;a name="_Toc198032359"&gt;Filter by using parameters that&lt;br /&gt;are passed to the query&lt;/a&gt;&lt;/h6&gt;&lt;p class="Text"&gt;As a general rule it is better to filter in the query rather than&lt;br /&gt;in the report (SELECT * FROM xyz WHERE field = @param). However, if you do this&lt;br /&gt;you cannot create a historical snapshot of the report without locking down the&lt;br /&gt;parameter value. If users need to change the parameter value in the snapshot,&lt;br /&gt;the best practice is to return all of the data to the report and filter inside&lt;br /&gt;the report instead. &lt;/p&gt;&lt;h6 style="MARGIN-LEFT: 0in"&gt;&lt;a name="_Toc198032360"&gt;Sort within the query&lt;/a&gt;&lt;/h6&gt;&lt;p class="Text"&gt;SSRS utilizes sort-stable operations internally, so the order of&lt;br /&gt;data returned from the query is not altered within each group instance, thereby&lt;br /&gt;allowing sorting to be performed in the query.&lt;/p&gt;&lt;p class="Text"&gt;However, sorting that is based on aggregate values is far more&lt;br /&gt;convenient (and usually more efficient) to perform in the report itself. &lt;/p&gt;&lt;h6 style="MARGIN-LEFT: 0in"&gt;&lt;a name="_Toc198032361"&gt;Avoid using subreports&lt;br /&gt;inside a grouping&lt;/a&gt; &lt;/h6&gt;&lt;p class="Text"&gt;Each subreport instance is a separate query execution and a&lt;br /&gt;separate processing step for SSRS. For master-detail reports, it is far more&lt;br /&gt;efficient to simply join the master and detail data in your query and then&lt;br /&gt;group by the master key in the report, except in cases where the number of&lt;br /&gt;master records is small (in which case subreport usage is not a performance&lt;br /&gt;issue).&lt;/p&gt;&lt;p class="Text"&gt;There are two cases where subreports may be required:&lt;/p&gt;&lt;p class="BulletedList1"&gt;&lt;span style="font-family:Symbol;"&gt;·&lt;span style="FONT: 7pt 'Times New Roman'; font-size-adjust: none; font-stretch: normal"&gt;&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;When the master and detail data are in different data sources.&lt;br /&gt;Pulling the data into a single data warehouse is recommended in this case. If&lt;br /&gt;that is not possible, SQL Server linked server or open rowset capabilities&lt;br /&gt;should be considered.&lt;/p&gt;&lt;p class="BulletedList1"&gt;&lt;span style="font-family:Symbol;"&gt;·&lt;span style="FONT: 7pt 'Times New Roman'; font-size-adjust: none; font-stretch: normal"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;When there are multiple independent sets of detail records for&lt;br /&gt;each master record. An example might be displaying a detailed list of both&lt;br /&gt;sales and returns for each customer. In this case, drillthrough reports are&lt;br /&gt;recommended instead of inline subreports unless the number of master records is&lt;br /&gt;small.&lt;/p&gt;&lt;h6 style="MARGIN-LEFT: 0in"&gt;&lt;a name="_Toc198032362"&gt;Limit the data in charts to&lt;br /&gt;what a user can see&lt;/a&gt; &lt;/h6&gt;&lt;p class="Text"&gt;While it may be tempting to include a large amount of detail data&lt;br /&gt;in charts to improve accuracy, it is better to pre-group the data in either the&lt;br /&gt;query or in the report, limiting the number of data points. Drawing hundreds of&lt;br /&gt;points in a space that only occupies a few pixels degrades performance and does&lt;br /&gt;nothing to enhance the visual appeal of the chart.&lt;/p&gt;&lt;h6 style="MARGIN-LEFT: 0in"&gt;&lt;a name="_Toc198032363"&gt;Pre-sort and pre-group the&lt;br /&gt;data in your query&lt;/a&gt; &lt;/h6&gt;&lt;p class="Text"&gt;You can normally improve performance by grouping and sorting the&lt;br /&gt;data in the query to match the sort order required by the report.&lt;/p&gt;&lt;h6 style="MARGIN-LEFT: 0in"&gt;&lt;a name="_Toc198032364"&gt;Use drillthrough rather&lt;br /&gt;than drilldown when detail data volumes are large&lt;/a&gt; &lt;/h6&gt;&lt;p class="Text"&gt;While the SQL Server 2008 on-demand processing engine&lt;br /&gt;optimizes away most calculations for items that are not displayed, keep in mind&lt;br /&gt;that all of the data is retrieved for every detail row, even if your initial&lt;br /&gt;drilldown state has everything collapsed up to the highest level of&lt;br /&gt;aggregation. In addition, all grouping, sorting, and filtering must be&lt;br /&gt;performed regardless of visibility or drilldown state. So if the user typically&lt;br /&gt;is only interested in seeing a small percentage of the detail data, an&lt;br /&gt;associated drillthrough report is a better choice.&lt;/p&gt;&lt;h6 style="MARGIN-LEFT: 0in"&gt;&lt;a name="_Toc198032365"&gt;Avoid complex expressions&lt;br /&gt;in the page header and footer&lt;/a&gt; &lt;/h6&gt;&lt;p class="Text"&gt;If the page header or footer contains complex expressions&lt;br /&gt;(anything other than a simple field or parameter reference), SSRS must assume&lt;br /&gt;it may include a reference to the total number of pages. As a result, the&lt;br /&gt;entire report must be paginated before the first page can be rendered. Otherwise,&lt;br /&gt;the first page can be rendered and returned to the user immediately who will&lt;br /&gt;not have to wait for the entire report to be paginated first.&lt;/p&gt;&lt;h6 style="MARGIN-LEFT: 0in"&gt;&lt;a name="_Toc198032366"&gt;Turn off CanGrow on&lt;br /&gt;textboxes and AutoSize on images if possible&lt;/a&gt; &lt;/h6&gt;&lt;p class="Text"&gt;Some renderers are more efficient if the sizes of all objects in&lt;br /&gt;the report are known to be fixed.&lt;/p&gt;&lt;h6 style="MARGIN-LEFT: 0in"&gt;&lt;a name="_Toc198032367"&gt;Do not return columns that&lt;br /&gt;you are not going to use from your query&lt;/a&gt;&lt;/h6&gt;&lt;p class="Text"&gt;Since all of the data in the query must be retrieved (and stored)&lt;br /&gt;by the report server, it is more efficient to return only the columns that will&lt;br /&gt;be used in the report. If the result set cannot be controlled (such as when the&lt;br /&gt;results are returned from a stored procedure), it is sufficient to remove the&lt;br /&gt;field definitions from the dataset. While the extra columns will be retrieved,&lt;br /&gt;this will at least prevent them from being stored.&lt;/p&gt;&lt;h5 style="MARGIN-LEFT: 0in"&gt;&lt;a name="_Toc198032368"&gt;Best Practices: System&lt;br /&gt;Architecture and Performance&lt;/a&gt;&lt;/h5&gt;&lt;h6 style="MARGIN-LEFT: 0in"&gt;&lt;a name="_Toc198032369"&gt;Keep the report server&lt;br /&gt;catalog on the same computer as the report server&lt;/a&gt; &lt;/h6&gt;&lt;p class="Text"&gt;Reporting Services generates and uses a database that is&lt;br /&gt;essentially a catalog. This is used during report processing for a number of&lt;br /&gt;tasks, including managing the data returned from queries. While it is possible&lt;br /&gt;to store this database on a server other than the reporting server, doing so&lt;br /&gt;requires that the report data be pushed across the network unnecessarily, which&lt;br /&gt;slows down report execution. It is a much better practice to hold the catalog&lt;br /&gt;database on the reporting server to avoid this network traffic and the&lt;br /&gt;associated delays. &lt;/p&gt;&lt;h6 style="MARGIN-LEFT: 0in"&gt;&lt;a name="_Toc198032370"&gt;Consider placing Reporting Services&lt;br /&gt;on a different server from your data warehouse&lt;/a&gt; &lt;/h6&gt;&lt;p class="Text"&gt;While pulling the data from the queries across the network does&lt;br /&gt;slow things down, it is beneficial to not have your data warehouse and SSRS&lt;br /&gt;competing for memory and processing time.&lt;/p&gt;&lt;h4 style="MARGIN-LEFT: 0in"&gt;&lt;a name="_Toc198032371"&gt;&lt;span lang="EN-GB"&gt;Conclusion&lt;/span&gt;&lt;/a&gt;&lt;/h4&gt;&lt;p class="Text"&gt;&lt;span lang="EN-GB"&gt;Designing and building an enterprise data&lt;br /&gt;warehouse can be a major effort with a significant cost. Data warehouses are&lt;br /&gt;not built as displays of technical excellence; they are investments made by the&lt;br /&gt;enterprise and intended to yield a high return. As both the IDC and Gartner&lt;br /&gt;papers show, all too often these projects have more of the characteristics of a&lt;br /&gt;gamble than a shrewd investment—a potentially high return but a concomitantly&lt;br /&gt;high risk of failure and loss. &lt;/span&gt;&lt;/p&gt;&lt;p class="Text"&gt;&lt;span lang="EN-GB"&gt;A great deal has been learned about factors that&lt;br /&gt;are associated with successful projects. These have been distilled into a&lt;br /&gt;number of best practices, many of which are described here, encompassing&lt;br /&gt;business processes, design, and technical implementation using SQL Server 2008.&lt;br /&gt;Indeed, many of the new features in SQL Server 2008 were specifically&lt;br /&gt;designed to allow these best practices to be implemented easily. &lt;/span&gt;&lt;/p&gt;&lt;p class="Text"&gt;&lt;span lang="EN-GB"&gt;As a proportion of the total effort required to&lt;br /&gt;create a data warehouse, applying these best practices is inexpensive, and yet&lt;br /&gt;doing so can have a major impact on the success (and therefore the ROI) of the&lt;br /&gt;project as a whole.&lt;/span&gt;&lt;/p&gt;&lt;p class="Text"&gt;&lt;/p&gt;&lt;p class="Text"&gt;&lt;span class="Bold"&gt;For more information:&lt;/span&gt;&lt;/p&gt;&lt;p class="BulletedList2"&gt;&lt;span style="font-family:Symbol;"&gt;·&lt;span style="FONT: 7pt 'Times New Roman'; font-size-adjust: none; font-stretch: normal"&gt;&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;a id="ctl00_rs1_mainContentContainer_ctl05" onclick="javascript:Track('ctl00_rs1_mainContentContainer_ctl00ctl00_rs1_mainContentContainer_ctl05',this);" href="http://www.microsoft.com/sql/default.mspx"&gt;SQL Server&lt;br /&gt;Web site&lt;/a&gt; &lt;/p&gt;&lt;p class="BulletedList2"&gt;&lt;span style="font-family:Symbol;"&gt;·&lt;span style="FONT: 7pt 'Times New Roman'; font-size-adjust: none; font-stretch: normal"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;a id="ctl00_rs1_mainContentContainer_ctl06" onclick="javascript:Track('ctl00_rs1_mainContentContainer_ctl00ctl00_rs1_mainContentContainer_ctl06',this);" href="http://technet.microsoft.com/en-us/sqlserver/default.aspx"&gt;SQL Server&lt;br /&gt;TechCenter&lt;/a&gt; &lt;/p&gt;&lt;p class="BulletedList2"&gt;&lt;span style="font-family:Symbol;"&gt;·&lt;span style="FONT: 7pt 'Times New Roman'; font-size-adjust: none; font-stretch: normal"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;a id="ctl00_rs1_mainContentContainer_ctl07" onclick="javascript:Track('ctl00_rs1_mainContentContainer_ctl00ctl00_rs1_mainContentContainer_ctl07',this);" href="http://msdn2.microsoft.com/en-us/sqlserver/default.aspx"&gt;SQL&lt;br /&gt;Server Developer Center&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;&lt;/p&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7516218764458010868-9203992657244386408?l=all-about-database.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://all-about-database.blogspot.com/feeds/9203992657244386408/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7516218764458010868&amp;postID=9203992657244386408' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7516218764458010868/posts/default/9203992657244386408'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7516218764458010868/posts/default/9203992657244386408'/><link rel='alternate' type='text/html' href='http://all-about-database.blogspot.com/2008/09/best-practices-for-data-warehousing.html' title='Best Practices for Data Warehousing with SQL Server 2008'/><author><name>firman.arrow</name><uri>http://www.blogger.com/profile/14656110271655711161</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7516218764458010868.post-953476660959315324</id><published>2008-09-12T00:30:00.001-07:00</published><updated>2008-09-12T08:29:41.028-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='more'/><category scheme='http://www.blogger.com/atom/ns#' term='Oracle'/><title type='text'>Automating Database Startup and Shutdown on Linux</title><content type='html'>When using RAC or ASM under Oracle 10g Release 2 or above, the Oracle Clusterware automatically starts and stops the Oracle database instances, so the following procedures are not necessary. For all other cases, you can use the methods described below.&lt;br /&gt;&lt;ul&gt;&lt;li&gt;&lt;a href="http://www.blogger.com/post-edit.g?blogID=7516218764458010868&amp;amp;postID=953476660959315324#the_su_command"&gt;The "su" Command&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://www.blogger.com/post-edit.g?blogID=7516218764458010868&amp;amp;postID=953476660959315324#the_rsh_command"&gt;The "rsh" Command&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://www.blogger.com/post-edit.g?blogID=7516218764458010868&amp;amp;postID=953476660959315324#known_issues"&gt;Known Issues&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;h2&gt;The "su" Command&lt;/h2&gt;The following represents the Oracle recommended method for automating database startup and shutdown of Oracle 9i instances.&lt;br /&gt;&lt;br /&gt;&lt;span class="fullpost"&gt;&lt;br /&gt;Once the instance is created, edit the "/etc/oratab" file setting the restart flag for each instance to 'Y'.&lt;br /&gt;&lt;blockquote&gt;&lt;pre&gt;TSH1:/u01/app/oracle/product/9.2.0:Y&lt;/pre&gt;&lt;/blockquote&gt;Next, create a file called "/etc/init.d/dbora" as the root user, containing the following.&lt;br /&gt;&lt;br /&gt;&lt;blockquote&gt;&lt;pre&gt;#!/bin/sh&lt;br /&gt;# chkconfig: 345 99 10&lt;br /&gt;# description: Oracle auto start-stop script.&lt;br /&gt;#&lt;br /&gt;# Set ORA_HOME to be equivalent to the $ORACLE_HOME&lt;br /&gt;# from which you wish to execute dbstart and dbshut;&lt;br /&gt;#&lt;br /&gt;# Set ORA_OWNER to the user id of the owner of the&lt;br /&gt;# Oracle database in ORA_HOME.&lt;br /&gt;&lt;br /&gt;ORA_HOME=/u01/app/oracle/product/9.2.0&lt;br /&gt;ORA_OWNER=oracle&lt;br /&gt;&lt;br /&gt;if [ ! -f $ORA_HOME/bin/dbstart ]&lt;br /&gt;then&lt;br /&gt;    echo "Oracle startup: cannot start"&lt;br /&gt;    exit&lt;br /&gt;fi&lt;br /&gt;&lt;br /&gt;case "$1" in&lt;br /&gt;    'start')&lt;br /&gt;        # Start the Oracle databases:&lt;br /&gt;        # The following command assumes that the oracle login&lt;br /&gt;        # will not prompt the user for any values&lt;br /&gt;        su - $ORA_OWNER -c "$ORA_HOME/bin/lsnrctl start"&lt;br /&gt;        su - $ORA_OWNER -c $ORA_HOME/bin/dbstart&lt;br /&gt;        ;;&lt;br /&gt;    'stop')&lt;br /&gt;        # Stop the Oracle databases:&lt;br /&gt;        # The following command assumes that the oracle login&lt;br /&gt;        # will not prompt the user for any values&lt;br /&gt;        su - $ORA_OWNER -c $ORA_HOME/bin/dbshut&lt;br /&gt;        su - $ORA_OWNER -c "$ORA_HOME/bin/lsnrctl stop"&lt;br /&gt;        ;;&lt;br /&gt;esac&lt;/pre&gt;&lt;/blockquote&gt;Use the &lt;code&gt;chmod&lt;/code&gt; command to set the privileges to 750.&lt;br /&gt;&lt;br /&gt;&lt;blockquote&gt;&lt;pre&gt;chmod 750 /etc/init.d/dbora&lt;/pre&gt;&lt;/blockquote&gt;Associate the dbora service with the appropriate run levels and set it to auto-start using the following command.&lt;br /&gt;&lt;br /&gt;&lt;blockquote&gt;&lt;pre&gt;chkconfig --level 345 dbora on&lt;/pre&gt;&lt;/blockquote&gt;The relevant instances should now startup/shutdown automatically at system startup/shutdown.&lt;br /&gt;&lt;br /&gt;This method can still be used under Oracle 10g and 11g, provided the "ORA_HOME" variable is amended to use the correct path and this is added to the end of the &lt;code&gt;dbstart&lt;/code&gt; and &lt;code&gt;dbshut&lt;/code&gt; lines. The lines to start and stop the listener can be removed under Oracle 10g release 2, as the &lt;code&gt;dbstart&lt;/code&gt; command includes an automatic start of the listener.&lt;br /&gt;&lt;br /&gt;&lt;blockquote&gt;&lt;pre&gt;#!/bin/sh&lt;br /&gt;# chkconfig: 345 99 10&lt;br /&gt;# description: Oracle auto start-stop script.&lt;br /&gt;#&lt;br /&gt;# Set ORA_HOME to be equivalent to the $ORACLE_HOME&lt;br /&gt;# from which you wish to execute dbstart and dbshut;&lt;br /&gt;#&lt;br /&gt;# Set ORA_OWNER to the user id of the owner of the&lt;br /&gt;# Oracle database in ORA_HOME.&lt;br /&gt;&lt;br /&gt;ORA_HOME=/u01/app/oracle/product/10.2.0/db_1&lt;br /&gt;#ORA_HOME=/u01/app/oracle/product/11.1.0/db_1&lt;br /&gt;ORA_OWNER=oracle&lt;br /&gt;&lt;br /&gt;if [ ! -f $ORA_HOME/bin/dbstart ]&lt;br /&gt;then&lt;br /&gt;    echo "Oracle startup: cannot start"&lt;br /&gt;    exit&lt;br /&gt;fi&lt;br /&gt;&lt;br /&gt;case "$1" in&lt;br /&gt;    'start')&lt;br /&gt;        # Start the Oracle databases:&lt;br /&gt;        # The following command assumes that the oracle login&lt;br /&gt;        # will not prompt the user for any values&lt;br /&gt;        su - $ORA_OWNER -c "$ORA_HOME/bin/dbstart $ORA_HOME"&lt;br /&gt;        ;;&lt;br /&gt;    'stop')&lt;br /&gt;        # Stop the Oracle databases:&lt;br /&gt;        # The following command assumes that the oracle login&lt;br /&gt;        # will not prompt the user for any values&lt;br /&gt;        su - $ORA_OWNER -c "$ORA_HOME/bin/dbshut $ORA_HOME"&lt;br /&gt;        ;;&lt;br /&gt;esac&lt;/pre&gt;&lt;/blockquote&gt;&lt;a id="the_rsh_command"&gt;&lt;/a&gt;&lt;h2&gt;The "rsh" Command&lt;/h2&gt;With Oracle 10g, Oracle switched from recommending the "su" command to the "rsh" command. In Oracle 10g release 2, the &lt;code&gt;dbstart&lt;/code&gt; command includes an automatic start of the listener, so there are some differences between the two versions, but the following represents the preferred method for Oracle 10g.&lt;br /&gt;&lt;br /&gt;Once the instance is created, edit the "/etc/oratab" file setting the restart flag for each instance to 'Y'.&lt;br /&gt;&lt;br /&gt;&lt;blockquote&gt;&lt;pre&gt;TSH1:/u01/app/oracle/product/9.2.0:Y&lt;/pre&gt;&lt;/blockquote&gt;Next, create a file called "/etc/init.d/dbora" as the root user, containing the following.&lt;br /&gt;&lt;br /&gt;&lt;blockquote&gt;&lt;pre&gt;#!/bin/sh&lt;br /&gt;# chkconfig: 345 99 10&lt;br /&gt;# description: Oracle auto start-stop script.&lt;br /&gt;#&lt;br /&gt;# Change the value of ORACLE_HOME to specify the correct Oracle home&lt;br /&gt;# directory for your installation.&lt;br /&gt;&lt;br /&gt;ORACLE_HOME=/u01/app/oracle/product/10.2.0/db_1&lt;br /&gt;#&lt;br /&gt;# Change the value of ORACLE to the login name of the&lt;br /&gt;# oracle owner at your site.&lt;br /&gt;#&lt;br /&gt;ORACLE=oracle&lt;br /&gt;&lt;br /&gt;PATH=${PATH}:$ORACLE_HOME/bin&lt;br /&gt;HOST=`hostname`&lt;br /&gt;PLATFORM=`uname`&lt;br /&gt;export ORACLE_HOME PATH&lt;br /&gt;#&lt;br /&gt;if [ ! "$2" = "ORA_DB" ] ; then&lt;br /&gt;   if [ "$PLATFORM" = "HP-UX" ] ; then&lt;br /&gt;      remsh $HOST -l $ORACLE -n "$0 $1 ORA_DB"&lt;br /&gt;      exit&lt;br /&gt;   else&lt;br /&gt;      rsh $HOST -l $ORACLE  $0 $1 ORA_DB&lt;br /&gt;      exit&lt;br /&gt;   fi&lt;br /&gt;fi&lt;br /&gt;#&lt;br /&gt;case $1 in&lt;br /&gt;'start')&lt;br /&gt;        $ORACLE_HOME/bin/dbstart $ORACLE_HOME&lt;br /&gt;        ;;&lt;br /&gt;'stop')&lt;br /&gt;        $ORACLE_HOME/bin/dbshut $ORACLE_HOME&lt;br /&gt;        ;;&lt;br /&gt;*)&lt;br /&gt;        echo "usage: $0 {startstop}"&lt;br /&gt;        exit&lt;br /&gt;        ;;&lt;br /&gt;esac&lt;br /&gt;#&lt;br /&gt;exit&lt;/pre&gt;&lt;/blockquote&gt;Use the &lt;code&gt;chmod&lt;/code&gt; command to set the privileges to 750.&lt;br /&gt;&lt;br /&gt;&lt;blockquote&gt;&lt;pre&gt;chmod 750 /etc/init.d/dbora&lt;/pre&gt;&lt;/blockquote&gt;Associate the dbora service with the appropriate run levels and set it to auto-start using the following command.&lt;br /&gt;&lt;br /&gt;&lt;blockquote&gt;&lt;pre&gt;chkconfig --level 345 dbora on&lt;/pre&gt;&lt;/blockquote&gt;The relevant instances should now startup/shutdown automatically at system startup/shutdown.&lt;br /&gt;&lt;br /&gt;This method relies on the presence of an RSH server, which requires additional packages and configuration.&lt;br /&gt;&lt;br /&gt;&lt;blockquote&gt;&lt;pre&gt;# Install the rhs and rsh-server packages from the OS CD/DVD.&lt;br /&gt;rpm -Uvh --force rsh-*&lt;br /&gt;&lt;br /&gt;# Enable rsh and rlogin.&lt;br /&gt;chkconfig rsh on&lt;br /&gt;chkconfig rlogin on&lt;br /&gt;service xinetd reload&lt;/pre&gt;&lt;/blockquote&gt;This can be quite problematic when attempting to use this method under FC5 and FC6, where rsh is deprecated. As a result, I prefer to&lt;br /&gt;use the "su" command method.&lt;br /&gt;&lt;br /&gt;This method can also be used for 11g databases that are not using ASM or RAC.&lt;br /&gt;&lt;br /&gt;&lt;a id="known_issues"&gt;&lt;/a&gt;&lt;h2&gt;Known Issues&lt;/h2&gt;When using Oracle 10g Release 2, calling &lt;code&gt;dbstart&lt;/code&gt; might result in the following error message:&lt;br /&gt;&lt;blockquote&gt;&lt;p&gt;Failed to auto-start Oracle Net Listener using /ade/vikrkuma_new/oracle/bin/tnslsnr&lt;/p&gt;&lt;/blockquote&gt;This is due to a hard coded path in the &lt;code&gt;dbstart&lt;/code&gt; script. To correct this, edit the "$ORACLE_HOME/bin/dbstart"&lt;br /&gt;script and replace the following line (approximately line 78):&lt;br /&gt;&lt;br /&gt;&lt;blockquote&gt;&lt;pre&gt;ORACLE_HOME_LISTNER=/ade/vikrkuma_new/oracle&lt;/pre&gt;&lt;/blockquote&gt;With this:&lt;br /&gt;&lt;br /&gt;&lt;blockquote&gt;&lt;pre&gt;ORACLE_HOME_LISTNER=$ORACLE_HOME&lt;/pre&gt;&lt;/blockquote&gt;The &lt;code&gt;dbstart&lt;/code&gt; script shold now start the listener as expected.&lt;br /&gt;&lt;br /&gt;For more information see:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;&lt;a href="http://download-uk.oracle.com/docs/cd/B19306_01/server.102/b15658/strt_stp.htm#BABGDGHF"&gt;Automating Shutdown and Startup (10.2)&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://download-uk.oracle.com/docs/html/B10812_01/chapter2.htm#sthref210"&gt;Automating Startup and Shutdown (10.1)&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://download-uk.oracle.com/docs/html/A96167_01/post-inst.htm#sthref548"&gt;Automating Database Startup and Shutdown (9.2)&lt;/a&gt;&lt;/li&gt;&lt;br /&gt;&lt;/ul&gt;Hope this helps. Regards Tim...&lt;br /&gt;&lt;br /&gt;&lt;a href="http://www.blogger.com/post-edit.g?blogID=751621876445
