<?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-3689647052791307812</id><updated>2012-01-30T06:36:42.525+11:00</updated><category term='Excel 2010'/><category term='Manly'/><category term='Product Function'/><category term='SQL Server Integration Services Scalability - Live Demonstration'/><category term='Temp Tables'/><category term='SQL'/><category term='Online Demonstrations'/><category term='MSSQLServerOLAPService File system error.  Invalid Data Directory'/><category term='BI jobs'/><category term='Live Data Mining Demonstration real-time'/><category term='Basket Analysis'/><category term='Performancepoint Planning'/><category term='IO Performance'/><category term='Powerpivot'/><category term='SSAS Performance'/><category term='Analysis Services'/><category term='SQL Server 2008 deploying new database versions very large OLAP databases with slow processing'/><category term='Saving table changes SQL Server 2008'/><category term='configuration'/><category term='web performance'/><category term='OLAP Security Large Numbers of Users No Maintenance'/><category term='SQL Server Performance File Fragmentation'/><category term='PerformancePoint thin client cube browsing'/><category term='performance'/><category term='drllthrough Analysis Services OLAP drill through'/><category term='cube browsers'/><category term='real data Data Mining tutorial SQL Server 2008'/><category term='unicycle'/><category term='Referrals'/><category term='real-time analytics'/><category term='Excel 2007'/><category term='Conditional Formatting Analysis Services Excel ThinSlicer'/><category term='Digital Dashboards Mobile Devices'/><category term='web footprints'/><category term='Analysis Services Cancel long running MDX query'/><category term='eat well'/><category term='MAXDOP'/><category term='browsing history'/><category term='Live SharePoint Excel Services Demonstration Real Data'/><category term='Windows Server'/><category term='Learning'/><category term='Error 44 The following system error occurred:  No mapping between account names and security IDs was done.'/><category term='olap cubes'/><category term='SQL Syntax'/><category term='SSAS'/><category term='SSRS'/><category term='Paddling as anternative to Sydney Traffic Congestion'/><category term='Book Review: Data Mining with SQL Server 2008'/><category term='sharepoint server 2007'/><category term='OLAP Performance'/><category term='cube design'/><category term='heterogeneous MDX SQL queries OpenRowset'/><category term='MDX'/><category term='Database Design'/><category term='Perfmon'/><category term='Reporting Services 2008 overwrites MDX parameter datasets'/><category term='Fact Table OLAP RDBMS Performance'/><category term='Windows Performance Monitor Continuous monitoring real time analysis'/><category term='Internet Explorer 8 market share real time analytics'/><category term='Microsoft discontinuing PerformancePoint Server 2007'/><category term='TechEd SSAS 2008 Peformance download powerpoint'/><category term='Security'/><category term='MDX Analysis Services text in a calculated member'/><category term='SQL Performance'/><category term='SQL Server Performance'/><category term='Web Analytics of a third party blog activity'/><category term='SCD'/><category term='Profiler'/><category term='Bing.com'/><category term='jpg images'/><category term='Data Mining DMX basket analysis SQL Server 2008 Analysis Services'/><category term='SSIS Packages'/><category term='Recursive MDX Performance SQL Server 2008'/><category term='SQL Profiler'/><category term='Altavista'/><category term='DMX Data Mining models predicting response times'/><category term='Web Analytics using Microsoft Analysis Services'/><category term='Metadata'/><category term='SQL Server 2008'/><category term='SSAS Partioning'/><category term='Database name'/><category term='Analysis Services MDX Ranking Customers Top cities'/><category term='Deadlocks'/><category term='PerformancePoint'/><category term='rename'/><category term='Row count fast high performing SQL count(*)'/><category term='Transactions'/><category term='Dynamic Reports'/><category term='Microsoft Source Code'/><category term='Microsoft Analysis Services Distinct Count Performance Alternative'/><category term='DatabaseID'/><category term='data types'/><category term='DMV'/><category term='Search engines'/><category term='SQL Server 2008 R2'/><category term='Web Analytics Response Time'/><category term='Imagination more important than knowledge business intelligence business strategy'/><category term='Web Analytics'/><title type='text'>Richard Lees on BI</title><subtitle type='html'>I am a Microsoft SQL Server Business Intelligence developer, creating solutions using OLAP and Data Mining from Analysis Services.  Pre 1998 (when Microsoft entered the OLAP space) I was a relational database performance specialist for IBM and its DB2 customers.  To learn more about me or see my live BI demonstrations goto http://RichardLees.com.au</subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://richardlees.blogspot.com/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3689647052791307812/posts/default?max-results=100'/><link rel='alternate' type='text/html' href='http://richardlees.blogspot.com/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><link rel='next' type='application/atom+xml' href='http://www.blogger.com/feeds/3689647052791307812/posts/default?start-index=101&amp;max-results=100'/><author><name>Richard Lees</name><uri>http://www.blogger.com/profile/05671716466559973540</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='31' height='32' src='http://2.bp.blogspot.com/-AdeWScnQel8/Thj9ePsjpQI/AAAAAAAAAew/XnIn3xt4a0s/s220/RichardUni.png'/></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>106</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>100</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-3689647052791307812.post-5192417605239653759</id><published>2012-01-29T07:49:00.000+11:00</published><updated>2012-01-29T07:53:32.390+11:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='performance'/><category scheme='http://www.blogger.com/atom/ns#' term='configuration'/><title type='text'>Analysis Services Configuration Options</title><content type='html'>To see the SSAS configuration options (properties), right click on the service from SQL Server Management Studio and select properties.&amp;nbsp; You will only see the Basic options, by default.&amp;nbsp; To see all options, check the "Show Advanced (All) Properties".&lt;a href="http://4.bp.blogspot.com/-PdZfB5Gzc9w/TyReRPuPAUI/AAAAAAAAAjk/IlDL88dWpWc/s1600/Properties.png" imageanchor="1" style="clear: right; float: right; margin-bottom: 1em; margin-left: 1em;"&gt;&lt;img border="0" height="287" src="http://4.bp.blogspot.com/-PdZfB5Gzc9w/TyReRPuPAUI/AAAAAAAAAjk/IlDL88dWpWc/s320/Properties.png" width="320" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Most of these options, you really shouldn't alter, but there are a few that you should know about.&amp;nbsp; &lt;br /&gt;Here are a few SSAS properties that I think you should be aware of, and might want to change.&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;DataDir&lt;/strong&gt;&lt;br /&gt;This is where SSAS will, by default, put the SSAS database files.&amp;nbsp; By default, this will be a sub directory of the "program files", which is not an ideal location.&amp;nbsp; The SSAS databases should be on high performance storage.&amp;nbsp; IO is very often a critical factor in large SSAS cubes.&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;ExternalCommandTimeout&lt;/strong&gt;&lt;br /&gt;As I understand it, this property is the number of seconds that SSAS will wait for the first row to return from an SQL query during processing.&amp;nbsp; The default is 3600 seconds (1 hour) which is insufficient for many environments.&amp;nbsp;&amp;nbsp;&amp;nbsp;I often change this to 36000 (10 hours) in larger environments, or environments with Oracle as an rdbms (just kidding).&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;ForceCommitTimeout&lt;/strong&gt;&lt;br /&gt;This is the amount of time (milliseconds) that SSAS will wait on queries while performing process updates.  A process update cannot commit while a query is active.  The default is 30 seconds, which I think is quite reasonable, but you might have a need to change this value.&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;QueryLogSampling&lt;/strong&gt;&lt;br /&gt;The default value is 10, so that 1 in 10 queries are recorded.&amp;nbsp; In practice, I either need all queries or none.&amp;nbsp; So what I do is change this value to 0 to minimise overhead.&amp;nbsp;&amp;nbsp; When I am running aperformance tuning exercise, I will temporarily change this to 1.&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;LowMemoryLimit&lt;/strong&gt;&lt;br /&gt;If this number is between 1 and 100, it is the percentage of memory that SSAS can use, without concern.&amp;nbsp; By default it is 65, which tends to be an appropriate value.&amp;nbsp; You might wonder why your 100GB server never sees&amp;nbsp;65GB memory for SSAS, even though your cubes are much larger and the IO demands are huge.&amp;nbsp; This is due to SSAS caching logic.&amp;nbsp; Just because you have the memory and the cube is large doesn't mean that SSAS will hold the cube in memory.&amp;nbsp; see my earlier blog &lt;a href="http://richardlees.blogspot.com/2011/12/why-doesnt-ssas-cache-entire-cube.html"&gt;http://richardlees.blogspot.com/2011/12/why-doesnt-ssas-cache-entire-cube.html&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;TotalMemoryLimit&lt;/strong&gt;&lt;br /&gt;You might see the&amp;nbsp;LowMemoryLimit as a very soft limit, which can be exceeded when processing demands would benefit.&amp;nbsp; The TotalMemoryLimit is a harder limit, but it can also be exceeded when necessary.&amp;nbsp;&amp;nbsp; One way that I look at these two options is the LowMemoryLimit is the memory limit during non processing times, and the TotalMemoryLimit is the limit when SSAS processing jobs are active.&amp;nbsp; However, as I mentioned above, the more common concern is that SSAS isn't using the available memory, rather than it is using too much.&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;DrillThroughMaxRows&lt;/strong&gt;&lt;br /&gt;I haven't ever changed this property.&amp;nbsp; Since 2005, SSAS hasn't offered a real drill-through.&amp;nbsp; This is really a drill down option.&amp;nbsp; If you want to support drill through see my blog &lt;a href="http://richardlees.blogspot.com/2009/01/using-drill-through-in-analysis.html"&gt;http://richardlees.blogspot.com/2009/01/using-drill-through-in-analysis.html&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;Port&lt;/strong&gt;&lt;br /&gt;If you want to change the port that SSAS listens on (or you are running multiple instances) this is where you specify the port number.&amp;nbsp; By default this value is 0, which denotes 2383.&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;ServerTimeout&lt;/strong&gt;&lt;br /&gt;This is the limit for SSAS queries in seconds.&amp;nbsp; After this time (default 3600 or 1 hour) mdx queries will timeout.&amp;nbsp; You may have reason to increase or decrease this value.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3689647052791307812-5192417605239653759?l=richardlees.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://richardlees.blogspot.com/feeds/5192417605239653759/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3689647052791307812&amp;postID=5192417605239653759' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3689647052791307812/posts/default/5192417605239653759'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3689647052791307812/posts/default/5192417605239653759'/><link rel='alternate' type='text/html' href='http://richardlees.blogspot.com/2012/01/analysis-services-configuration-options.html' title='Analysis Services Configuration Options'/><author><name>Richard Lees</name><uri>http://www.blogger.com/profile/05671716466559973540</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='31' height='32' src='http://2.bp.blogspot.com/-AdeWScnQel8/Thj9ePsjpQI/AAAAAAAAAew/XnIn3xt4a0s/s220/RichardUni.png'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://4.bp.blogspot.com/-PdZfB5Gzc9w/TyReRPuPAUI/AAAAAAAAAjk/IlDL88dWpWc/s72-c/Properties.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3689647052791307812.post-3926524759386690330</id><published>2011-12-28T09:19:00.001+11:00</published><updated>2011-12-28T09:19:18.628+11:00</updated><title type='text'>Remember What's Important</title><content type='html'>Happy New Year to everybody.&lt;br /&gt;&lt;br /&gt;Perhaps, like me, you enjoy your work in the Microsoft software space.&amp;nbsp; That's great, you are very lucky if you enjoy your work.&amp;nbsp; But remember what's really important in this life.&amp;nbsp; Your family and friends.&lt;br /&gt;&lt;br /&gt;So, don't work too hard.&amp;nbsp; Eat well and keep exercising.&amp;nbsp; It helps if you find a sport that you really enjoy.&amp;nbsp; At the moment, that is unicycling for me.&lt;br /&gt;&lt;br /&gt;See you all in the New Year.&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://2.bp.blogspot.com/-nvIULJco0Dw/TvpDniVynAI/AAAAAAAAAjU/yG5g4VnPcSc/s1600/NorthSteyneUnicycle.jpg" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="484" src="http://2.bp.blogspot.com/-nvIULJco0Dw/TvpDniVynAI/AAAAAAAAAjU/yG5g4VnPcSc/s640/NorthSteyneUnicycle.jpg" width="640" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3689647052791307812-3926524759386690330?l=richardlees.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://richardlees.blogspot.com/feeds/3926524759386690330/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3689647052791307812&amp;postID=3926524759386690330' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3689647052791307812/posts/default/3926524759386690330'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3689647052791307812/posts/default/3926524759386690330'/><link rel='alternate' type='text/html' href='http://richardlees.blogspot.com/2011/12/remember-whats-important.html' title='Remember What&apos;s Important'/><author><name>Richard Lees</name><uri>http://www.blogger.com/profile/05671716466559973540</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='31' height='32' src='http://2.bp.blogspot.com/-AdeWScnQel8/Thj9ePsjpQI/AAAAAAAAAew/XnIn3xt4a0s/s220/RichardUni.png'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://2.bp.blogspot.com/-nvIULJco0Dw/TvpDniVynAI/AAAAAAAAAjU/yG5g4VnPcSc/s72-c/NorthSteyneUnicycle.jpg' height='72' width='72'/><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3689647052791307812.post-6260419350690184175</id><published>2011-12-11T13:39:00.001+11:00</published><updated>2011-12-29T06:17:51.605+11:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='IO Performance'/><category scheme='http://www.blogger.com/atom/ns#' term='Analysis Services'/><category scheme='http://www.blogger.com/atom/ns#' term='olap cubes'/><category scheme='http://www.blogger.com/atom/ns#' term='OLAP Performance'/><title type='text'>My SSAS databases are corrupted</title><content type='html'>Did you happen to have a unplanned shutdown?&amp;nbsp; This is quite likely to be the cause of the failure.&amp;nbsp; This is unlike SQL Server, which has a transaction log and will always recover databases on restart.&lt;br /&gt;As I have mentioned earlier, SQL Server Analysis Services exploits the Windows File Cache to help minimise IO, which might lead you to suspect the Windows cache.&amp;nbsp; However SSAS uses the FlushFileBuffers API to minimise the possibility of having dirty pages in the Windows File Cache.&amp;nbsp; However, this does not protect SSAS from corruption on an unplanned shutdown.&lt;br /&gt;So, if you really want to avoid corrupted SSAS databases on power failure, you should ensure your SSAS servers are supported by UPS.&lt;br /&gt;Most production SSAS servers have UPS.&amp;nbsp; But, if you don't have UPS, like my demonstration site (&lt;a href="http://richardlees.com.au/Sites/Demonstrations"&gt;http://RichardLees.com.au/Sites/Demonstrations&lt;/a&gt;) and, like my site, the cubes are continually updating,&amp;nbsp;when the power fails, SSAS databases have a good chance of being corrupted.&amp;nbsp;&amp;nbsp;The only solution to this, that I know of, is to restore the affected (typically all) SSAS databases.&amp;nbsp; I find the quickest way to do this is to stop SSAS, empty the SSAS data directory, start SSAS and recover all the databases.&amp;nbsp; One thing you will also need to do is add back any userids that had SSAS administrator privileges as they were held in&amp;nbsp;a file on the SSAS data directory.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3689647052791307812-6260419350690184175?l=richardlees.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://richardlees.blogspot.com/feeds/6260419350690184175/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3689647052791307812&amp;postID=6260419350690184175' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3689647052791307812/posts/default/6260419350690184175'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3689647052791307812/posts/default/6260419350690184175'/><link rel='alternate' type='text/html' href='http://richardlees.blogspot.com/2011/12/my-ssas-databases-are-corrupted.html' title='My SSAS databases are corrupted'/><author><name>Richard Lees</name><uri>http://www.blogger.com/profile/05671716466559973540</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='31' height='32' src='http://2.bp.blogspot.com/-AdeWScnQel8/Thj9ePsjpQI/AAAAAAAAAew/XnIn3xt4a0s/s220/RichardUni.png'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3689647052791307812.post-1697054490419265094</id><published>2011-12-06T12:05:00.001+11:00</published><updated>2011-12-09T19:57:59.573+11:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Windows Server'/><category scheme='http://www.blogger.com/atom/ns#' term='SQL Performance'/><title type='text'>Windows Server Performance Tuning</title><content type='html'>I just came across this very good whitepaper on Windows 2008 Server performance tuning.&amp;nbsp; There is will be a lot of information that you already know, but you will probably learn some new things from this document.&amp;nbsp; There is quite a lot of information on storage, networking, processor utilisation and virtualisation.&amp;nbsp; The virtualisation section was most informative for me.&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://1.bp.blogspot.com/-92VSWnoMDoo/TuHMZwViWqI/AAAAAAAAAi0/TxF0gGCISMw/s1600/WindowsServer2008Tuning.png" imageanchor="1" style="clear: right; float: right; margin-bottom: 1em; margin-left: 1em;"&gt;&lt;img border="0" height="380" src="http://1.bp.blogspot.com/-92VSWnoMDoo/TuHMZwViWqI/AAAAAAAAAi0/TxF0gGCISMw/s400/WindowsServer2008Tuning.png" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;The document is in Word format &lt;a href="http://msdn.microsoft.com/en-us/windows/hardware/gg463394"&gt;http://msdn.microsoft.com/en-us/windows/hardware/gg463394&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3689647052791307812-1697054490419265094?l=richardlees.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://richardlees.blogspot.com/feeds/1697054490419265094/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3689647052791307812&amp;postID=1697054490419265094' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3689647052791307812/posts/default/1697054490419265094'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3689647052791307812/posts/default/1697054490419265094'/><link rel='alternate' type='text/html' href='http://richardlees.blogspot.com/2011/12/windows-server-performance-tuning.html' title='Windows Server Performance Tuning'/><author><name>Richard Lees</name><uri>http://www.blogger.com/profile/05671716466559973540</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='31' height='32' src='http://2.bp.blogspot.com/-AdeWScnQel8/Thj9ePsjpQI/AAAAAAAAAew/XnIn3xt4a0s/s220/RichardUni.png'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://1.bp.blogspot.com/-92VSWnoMDoo/TuHMZwViWqI/AAAAAAAAAi0/TxF0gGCISMw/s72-c/WindowsServer2008Tuning.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3689647052791307812.post-8511388716915632295</id><published>2011-12-05T17:09:00.001+11:00</published><updated>2011-12-11T07:12:05.195+11:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='IO Performance'/><category scheme='http://www.blogger.com/atom/ns#' term='Analysis Services'/><category scheme='http://www.blogger.com/atom/ns#' term='OLAP Performance'/><title type='text'>Why doesn't SSAS cache the entire cube?</title><content type='html'>Would you expect a 36GB server dedicated to SSAS to eventually cache an entire 11GB cube in memory?&amp;nbsp; If so, you would be wrong.&amp;nbsp; SSAS does not necessarily keep aggregations in memory, even though memory used is less than&amp;nbsp;Memory\LowMemoryLimit.&lt;br /&gt;&lt;br /&gt;I have seen this effect on many production servers but never quite believed it.&amp;nbsp; There is much more RAM than cube size, yet SSAS is continually losing data from cache and asking for physical IO.&amp;nbsp; Actually, SSAS relies on&amp;nbsp;the Windows File Cache to reduce&amp;nbsp;much of its IO.&amp;nbsp; However, the Windows File Cache does not&amp;nbsp;cache the entire cube either.&lt;br /&gt;&lt;br /&gt;To demonstrate this effect, I have set up a 36GB server, dedicated to SSAS, and set up an 11GB MOLAP cube.&amp;nbsp; Then, over a period of 19 hours, I have client tasks querying the cube in a semi random fashion.&amp;nbsp; The MOLAP cube was not updated over the period. &amp;nbsp;Over the 19 hours, if SSAS or Windows File Cache were effective, there wouldn't be much more than 11GB of read IO on the SSAS data drive over the 19 hours.&amp;nbsp; However, the system reached an equilibrium, where it was requesting over 20MB of read IO/second.&amp;nbsp; If you add this up, the system read&amp;nbsp;1.3 terabytes&amp;nbsp;of data from the 11GB cube (20MB*60*60*19).&amp;nbsp; Obviously, the system was not&amp;nbsp;effectively caching the cube.&amp;nbsp; This is very different from SQL Server, which would quite happily keep an&amp;nbsp;11GB database in memory, effectively eliminating further read IO.&lt;br /&gt;&lt;br /&gt;My server was running Windows 2008, SQL Server 2008 R2 with default configuration settings.&amp;nbsp; The SSAS database was located on the d: drive.&amp;nbsp; Nothing else is located on the d: drive&lt;br /&gt;Here is a Perfmon chart with some of the key counters during my test.&lt;br /&gt;&lt;a href="http://1.bp.blogspot.com/-yqBIyXI-DDQ/TuHfgWiGdqI/AAAAAAAAAjE/gdzy_jxMCd4/s1600/WindowsServer2008Tuning.png" imageanchor="1" style="clear: right; float: right; margin-bottom: 1em; margin-left: 1em;"&gt;&lt;img border="0" height="416" src="http://1.bp.blogspot.com/-yqBIyXI-DDQ/TuHfgWiGdqI/AAAAAAAAAjE/gdzy_jxMCd4/s640/WindowsServer2008Tuning.png" width="640" /&gt;&lt;/a&gt;SSAS (msmdsrv.exe) did not use more than 12GB of memory during the test, which is well below the LowMemoryLimit 65% (which is about 23GB).&lt;br /&gt;&lt;br /&gt;During the 19 hours, SSAS requested, on average, 102MB/second of read IO.&amp;nbsp; The Windows File Cache was able to satisfy about 80% of these read requests, so the physical disk bytes/second was only 20MB/second.&amp;nbsp; &lt;br /&gt;&lt;br /&gt;The Perfmon chart above shows some of the key counters.&amp;nbsp; Notice&lt;br /&gt;&lt;ul&gt;&lt;li&gt;The Process (msmdsrv) Bytes/second y axis scale is in MB, often going off scale at 100MB/sec.  These are not necessarily physical IO, as the Windows cache will satisfy many.&lt;/li&gt;&lt;li&gt;The PhysicalDisk Bytes/second is averaging 20.1MB/second for the 19 hours&lt;/li&gt;&lt;li&gt;The Windows Cache Bytes is sitting between 0.6GB and 1.1 GB.&lt;/li&gt;&lt;li&gt;The red line (Cache Copy Reads/sec) is the IO requested of the cache.&amp;nbsp; These are IO that&amp;nbsp;the Windows cache has managed to satisfy by finding the page in its cache.&lt;/li&gt;&lt;li&gt;See how the Cache Copy Reads/sec is satisfying IO requested of the msmdsrv process, reducing the PhysicalDisk Bytes/sec.&amp;nbsp;&amp;nbsp;Not so easy to see, as the reads are reads/sec, while the PhysicalDisk and Process are in bytes/sec, but you can see it.&lt;/li&gt;&lt;/ul&gt;Now that I understand this effect, I am much more appreciative of the Windows Cache.&amp;nbsp; This is very different from a SQL Server system, where the Windows Cache has little to do as SQL disables the Windows File Cache for its database files.&lt;br /&gt;&lt;br /&gt;However, you can clearly see that SSAS (or the system)&amp;nbsp;is performing much more IO than would be necessary if the entire 11GB cube was cached in memory.&lt;br /&gt;There are a couple of takeaways from this exercise&lt;br /&gt;&lt;ol&gt;&lt;li&gt;Be thankful that we have a Windows Cache, and consider configuring it for SSAS.&lt;/li&gt;&lt;li&gt;Try and put SSAS databases on very fast (ideally solid state disks) storage devices.&amp;nbsp; If you can't avoid the IO, at least make it fast.&lt;/li&gt;&lt;li&gt;Partition large cubes.&amp;nbsp; This helps reduce logical IO requests and to the extent it bunches the popular data together it will probably help&amp;nbsp;the Windows Cache hold the hot data more effectively.&lt;/li&gt;&lt;li&gt;Hopefully, in a future version, either SSAS or the Windows File Cache will cache more of the cube.&lt;/li&gt;&lt;/ol&gt;By the way, this effect is not always noticeable.&amp;nbsp; If your cube is less than 2-3GB, then it is likely to be well cached between SSAS and Windows File Cache.&amp;nbsp; Also, if you cube is much larger than the memory available to SSAS, then you would expect to see continual IO, and it is likely to be quite well optimised.&amp;nbsp; However, when you have a 64 bit server with a cube that is&amp;nbsp;larger than&amp;nbsp;3GB but is comfortably less than the server memory, you might be surprised to see the volume of continual IO.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3689647052791307812-8511388716915632295?l=richardlees.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://richardlees.blogspot.com/feeds/8511388716915632295/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3689647052791307812&amp;postID=8511388716915632295' title='7 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3689647052791307812/posts/default/8511388716915632295'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3689647052791307812/posts/default/8511388716915632295'/><link rel='alternate' type='text/html' href='http://richardlees.blogspot.com/2011/12/why-doesnt-ssas-cache-entire-cube.html' title='Why doesn&apos;t SSAS cache the entire cube?'/><author><name>Richard Lees</name><uri>http://www.blogger.com/profile/05671716466559973540</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='31' height='32' src='http://2.bp.blogspot.com/-AdeWScnQel8/Thj9ePsjpQI/AAAAAAAAAew/XnIn3xt4a0s/s220/RichardUni.png'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://1.bp.blogspot.com/-yqBIyXI-DDQ/TuHfgWiGdqI/AAAAAAAAAjE/gdzy_jxMCd4/s72-c/WindowsServer2008Tuning.png' height='72' width='72'/><thr:total>7</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3689647052791307812.post-667495503181742934</id><published>2011-11-27T20:37:00.001+11:00</published><updated>2011-11-27T21:18:04.749+11:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Deadlocks'/><category scheme='http://www.blogger.com/atom/ns#' term='SQL Performance'/><category scheme='http://www.blogger.com/atom/ns#' term='SQL Profiler'/><title type='text'>Causing a Deadlock in SQL Server</title><content type='html'>SQL Server has a background process that is continually looking for deadlock chains.&amp;nbsp; If it finds a deadlock chain, it will rollback the transaction that has done the least amount of work, which should be the fastest to rollback.&amp;nbsp; That's unless one of the transactions has volunteered as a deadlock victim&amp;nbsp; by setting their deadlock_priority to low.&lt;br /&gt;If you want to cause a deadlock, it is quite easy.&amp;nbsp; Most DBAs will be able to do it, and it can be useful in determining what profiler and logging information is available in the event of an unplanned deadlock.&lt;br /&gt;&lt;br /&gt;Here are some instructions to cause a deadlock between 3 transactions.&amp;nbsp; Most deadlocks are between 2 transactions, but a deadlock chain can have any number of transactions.&amp;nbsp; By the way, there are many ways to set up a deadlock, the only common characteristic is that there are a chain of processes that are waiting on each other for locks.&amp;nbsp; So in a 2 process chain, Process A is waiting on a resource held by Process B and Process B is waiting on a resource held by Process A.&amp;nbsp; As you can imagine, the only solution is for one of the transactions to be rolled back.&amp;nbsp; They can't both go forward.&lt;br /&gt;&lt;br /&gt;In my experience, a common class of deadlocks is the one caused by update (update, insert or delete) transactions that update so many rows that the lock must be escalated.&amp;nbsp; For example, escalate several page locks to a table lock.&amp;nbsp; When there are two transactions like this starting about the same time, they both start updating, and taking locks, until one of them escalates to a table lock.&amp;nbsp; At this moment that transaction waits on the other.&amp;nbsp; But if the other is also destined to escalate to a table lock on the same table, there will be a deadlock.&lt;br /&gt;&lt;br /&gt;Now, to cause a deadlock between 3 tasks follow these instructions.&lt;br /&gt;You might like to start SQL Profiler and enable a trace for Lock:Deadlock graph, Lock:Deadlock Chain and Lock:Deadlock, which will give you considerable information on the deadlock chain participants and resource locks.&lt;br /&gt;Create 3 tables&lt;br /&gt;&lt;ul&gt;&lt;ul&gt;&lt;li&gt;&lt;span style="color: blue; font-size: x-small;"&gt;&lt;span style="color: blue; font-size: x-small;"&gt;create &lt;span style="color: blue; font-size: x-small;"&gt;&lt;span style="color: blue; font-size: x-small;"&gt;table&lt;/span&gt;&lt;/span&gt;&lt;span style="color: black; font-size: x-small;"&gt; t1&lt;/span&gt;&lt;span style="color: blue; font-size: x-small;"&gt;&lt;span style="color: blue; font-size: x-small;"&gt; &lt;/span&gt;&lt;/span&gt;&lt;span style="color: grey; font-size: x-small;"&gt;&lt;span style="color: grey; font-size: x-small;"&gt;(&lt;/span&gt;&lt;/span&gt;&lt;span style="color: black; font-size: x-small;"&gt;c1 &lt;/span&gt;&lt;span style="color: blue; font-size: x-small;"&gt;&lt;span style="color: blue; font-size: x-small;"&gt;int&lt;/span&gt;&lt;/span&gt;&lt;span style="color: grey; font-size: x-small;"&gt;&lt;span style="color: grey; font-size: x-small;"&gt;,&lt;/span&gt;&lt;/span&gt;&lt;span style="color: black; font-size: x-small;"&gt; c2 &lt;/span&gt;&lt;span style="color: blue; font-size: x-small;"&gt;&lt;span style="color: blue; font-size: x-small;"&gt;varchar&lt;/span&gt;&lt;/span&gt;&lt;span style="color: grey; font-size: x-small;"&gt;&lt;span style="color: grey; font-size: x-small;"&gt;(&lt;/span&gt;&lt;/span&gt;&lt;span style="color: black; font-size: x-small;"&gt;50&lt;/span&gt;&lt;span style="color: grey; font-size: x-small;"&gt;&lt;span style="color: grey; font-size: x-small;"&gt;))&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span style="font-size: x-small;"&gt;&lt;span style="color: blue;"&gt;create table&lt;/span&gt; t2&lt;span style="color: blue;"&gt;&lt;span style="color: blue;"&gt; &lt;/span&gt;&lt;/span&gt;&lt;span style="color: grey;"&gt;&lt;span style="color: grey;"&gt;(&lt;/span&gt;&lt;/span&gt;c1 &lt;span style="color: blue;"&gt;&lt;span style="color: blue;"&gt;int&lt;/span&gt;&lt;/span&gt;&lt;span style="color: grey;"&gt;&lt;span style="color: grey;"&gt;,&lt;/span&gt;&lt;/span&gt; c2 &lt;span style="color: blue;"&gt;&lt;span style="color: blue;"&gt;varchar&lt;/span&gt;&lt;/span&gt;&lt;span style="color: grey;"&gt;&lt;span style="color: grey;"&gt;(&lt;/span&gt;&lt;/span&gt;50&lt;span style="color: grey;"&gt;&lt;span style="color: grey;"&gt;))&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span style="font-size: x-small;"&gt;&lt;span style="color: blue;"&gt;create table&lt;/span&gt; t3&lt;span style="color: blue;"&gt;&lt;span style="color: blue;"&gt; &lt;/span&gt;&lt;/span&gt;&lt;span style="color: grey;"&gt;&lt;span style="color: grey;"&gt;(&lt;/span&gt;&lt;/span&gt;c1 &lt;span style="color: blue;"&gt;&lt;span style="color: blue;"&gt;int&lt;/span&gt;&lt;/span&gt;&lt;span style="color: grey;"&gt;&lt;span style="color: grey;"&gt;,&lt;/span&gt;&lt;/span&gt; c2 &lt;span style="color: blue;"&gt;&lt;span style="color: blue;"&gt;varchar&lt;/span&gt;&lt;/span&gt;&lt;span style="color: grey;"&gt;&lt;span style="color: grey;"&gt;(&lt;/span&gt;&lt;/span&gt;50&lt;span style="color: grey;"&gt;&lt;span style="color: grey;"&gt;))&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/ul&gt;&lt;span style="color: grey; font-size: x-small;"&gt;&lt;span style="color: grey; font-size: x-small;"&gt;&lt;/span&gt;&lt;/span&gt;Create 3 query windows in SQL Server Enterprise Manager.&lt;br /&gt;In the first query window execute&lt;br /&gt;&lt;span style="color: blue; font-size: x-small;"&gt;&lt;span style="color: blue; font-size: x-small;"&gt;begin tran&lt;br /&gt;insert into&amp;nbsp;t1 &lt;span style="color: blue; font-size: x-small;"&gt;&lt;span style="color: blue; font-size: x-small;"&gt;select&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size: x-small;"&gt; 1&lt;/span&gt;&lt;span style="color: grey; font-size: x-small;"&gt;&lt;span style="color: grey; font-size: x-small;"&gt;,&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size: x-small;"&gt; &lt;/span&gt;&lt;span style="color: red; font-size: x-small;"&gt;&lt;span style="color: red; font-size: x-small;"&gt;'abc'&lt;/span&gt;&lt;/span&gt; &lt;br /&gt; &lt;br /&gt;&lt;span style="color: black; font-size: small;"&gt;In the second query window execute&lt;/span&gt;&lt;br /&gt;&lt;span style="color: blue; font-size: x-small;"&gt;&lt;span style="color: blue; font-size: x-small;"&gt;begin tran&lt;br /&gt;insert into t2 select 2, &lt;span style="color: red;"&gt;'xyz'&lt;/span&gt;&lt;br /&gt;Select * from t1&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="color: blue; font-size: x-small;"&gt;&lt;span style="color: blue; font-size: x-small;"&gt;&lt;span style="color: blue; font-size: x-small;"&gt;&lt;span style="color: black; font-size: small;"&gt;The second query window will be waiting on the first query.&lt;/span&gt;&lt;br /&gt;&lt;span style="color: black; font-size: small;"&gt;&lt;span style="color: black; font-size: small;"&gt;In the third query window execute&lt;/span&gt;&lt;br /&gt;&lt;span style="color: blue; font-size: x-small;"&gt;&lt;span style="color: blue; font-size: x-small;"&gt;begin tran&lt;br /&gt;insert into t3 select 3, &lt;span style="color: red;"&gt;'mno'&lt;/span&gt;&lt;br /&gt;Select * from t2&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="color: blue; font-size: x-small;"&gt;&lt;span style="color: blue; font-size: x-small;"&gt;&lt;span style="color: blue; font-size: x-small;"&gt;&lt;span style="color: blue; font-size: x-small;"&gt;&lt;span style="color: blue; font-size: x-small;"&gt;&lt;span style="color: black; font-size: small;"&gt;The third query window will be waiting on the second query.&amp;nbsp; At this point in time there is no deadlock.&amp;nbsp; We just have a locking chain, with query 1 at the head.&amp;nbsp; You can see the locking chain if you execute.&lt;/span&gt;&lt;br /&gt;&lt;span style="color: blue;"&gt;sp_who2&lt;/span&gt;&lt;br /&gt;&lt;span style="color: black; font-size: small;"&gt;&lt;span style="color: blue; font-size: x-small;"&gt; &lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="color: black; font-size: small;"&gt;&lt;span style="color: black; font-size: small;"&gt;In the first query window execute&lt;/span&gt;&lt;br /&gt;&lt;span style="color: blue; font-size: x-small;"&gt;&lt;span style="color: blue; font-size: x-small;"&gt;&lt;span style="color: #38761d;"&gt;--begin tran&lt;/span&gt;&lt;br /&gt;&lt;span style="color: #38761d;"&gt;--insert into t1 select 1, 'abc'&lt;/span&gt;&lt;br /&gt;Select * from t3&lt;/span&gt;&lt;span style="color: black; font-size: small;"&gt;&lt;br /&gt;&lt;span style="color: black; font-size: small;"&gt;Now, query 1 will be waiting on query 3, which is waiting on query 2, which is waiting on query 1.&amp;nbsp; We have a deadlock.&amp;nbsp; You should notice that within a couple of seconds, one of the queries is cancelled and the transaction rolled back.&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="color: red; font-size: x-small;"&gt;Msg 1205, Level 13, State 45, Line 4&lt;br /&gt;Transaction (Process ID 52) was deadlocked on lock resources with another process and has been chosen as the deadlock victim. Rerun the transaction.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="color: black;"&gt;Now, have a look at the Profiler output, and you should see your deadlock chart with the three processes and the locked resources.&amp;nbsp; The transaction that was selected as the victim, and rolled back, has a cross through it.&amp;nbsp; If you hover over the transactions, you will see the last batch to be submitted.&amp;nbsp; Note, the last batch is not necessarily the entire transaction.&amp;nbsp; There may have been an explicit BEGIN TRAN and many statements within the transaction that aren't included in the chart.&amp;nbsp; If you manage to capture a blocking transaction in action, you can see the locks it has (and ones its waiting on) with the sp_lock command.&amp;nbsp; &lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Remember to COMMIT or ROLLBACK the two queries that weren't rolled back.&amp;nbsp; There is nothing worse that a query holding locks while the user has gone off to do something else.&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://2.bp.blogspot.com/-AINH8hsIZuw/TtILbd7il3I/AAAAAAAAAiI/2VUIOTDg5aQ/s1600/DeadlockGraph.png" imageanchor="1" style="clear: right; float: right; margin-bottom: 1em; margin-left: 1em;"&gt;&lt;img border="0" height="230" src="http://2.bp.blogspot.com/-AINH8hsIZuw/TtILbd7il3I/AAAAAAAAAiI/2VUIOTDg5aQ/s640/DeadlockGraph.png" width="640" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;span style="font-size: x-small;"&gt;&lt;/span&gt;&lt;span style="color: black;"&gt;&lt;span style="color: blue;"&gt;&lt;span style="color: black;"&gt;&lt;span style="color: blue;"&gt;&lt;span style="color: blue;"&gt;&lt;span style="color: blue;"&gt;&lt;span style="color: blue;"&gt;&lt;span style="color: blue;"&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="color: black;"&gt;&lt;span style="color: blue;"&gt;&lt;span style="color: black;"&gt;&lt;span style="color: blue;"&gt;&lt;span style="color: blue;"&gt;&lt;span style="color: blue;"&gt;&lt;span style="color: blue;"&gt;&lt;span style="color: blue;"&gt;&lt;span style="color: grey;"&gt;&lt;span style="color: grey;"&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3689647052791307812-667495503181742934?l=richardlees.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://richardlees.blogspot.com/feeds/667495503181742934/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3689647052791307812&amp;postID=667495503181742934' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3689647052791307812/posts/default/667495503181742934'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3689647052791307812/posts/default/667495503181742934'/><link rel='alternate' type='text/html' href='http://richardlees.blogspot.com/2011/11/causing-deadlock-in-sql-server.html' title='Causing a Deadlock in SQL Server'/><author><name>Richard Lees</name><uri>http://www.blogger.com/profile/05671716466559973540</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='31' height='32' src='http://2.bp.blogspot.com/-AdeWScnQel8/Thj9ePsjpQI/AAAAAAAAAew/XnIn3xt4a0s/s220/RichardUni.png'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://2.bp.blogspot.com/-AINH8hsIZuw/TtILbd7il3I/AAAAAAAAAiI/2VUIOTDg5aQ/s72-c/DeadlockGraph.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3689647052791307812.post-6880704360581073495</id><published>2011-11-22T06:40:00.001+11:00</published><updated>2011-11-23T07:00:27.921+11:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='SQL Server 2008 R2'/><category scheme='http://www.blogger.com/atom/ns#' term='SQL Performance'/><title type='text'>Fix Query Performance by Creating Statistics</title><content type='html'>&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: left;"&gt;&lt;a href="http://1.bp.blogspot.com/-vL65ZiUf_e0/TsqsOmXauFI/AAAAAAAAAhg/orIZsE9fWaQ/s1600/DatabaseOptions.png" imageanchor="1" style="clear: right; float: right; margin-bottom: 1em; margin-left: 1em;"&gt;&lt;img border="0" height="287" src="http://1.bp.blogspot.com/-vL65ZiUf_e0/TsqsOmXauFI/AAAAAAAAAhg/orIZsE9fWaQ/s320/DatabaseOptions.png" width="320" /&gt;&lt;/a&gt;SQL Server's optimiser will automatically create statistics on individual columns that are used in predicates.&amp;nbsp; See picture of database properties to the right.&amp;nbsp; However, it will not create statistics on column combinations unless they are composite columns in an index.&amp;nbsp; This is where you might be able to help the optimiser by creating statistics on column combinations.&amp;nbsp;&amp;nbsp;This technique is most appropriate where the columns in your predicates have a relationship.&amp;nbsp; For example, my InternetLogtable (1.2 Million rows) contains a row for each internet request.&amp;nbsp; It has columns ClientAgentId and ClientHostId, where ClientAgent is the name of the browser, and ClientHost is the IP address of the client machine.&amp;nbsp; As you can imagine, most requests from one ClientHost will tend to have the same ClientAgent.&amp;nbsp; However, SQL Server's optimiser does not know this.&amp;nbsp; It has statistics on ClientAgentId and ClientHostID so if when it determines the cost of the following queries it can quite accurately determine how many rows will be returned.&amp;nbsp; For the first query it estimates 332 rows will be returned.&amp;nbsp; For the second query it estimates 110 rows will be returned.&amp;nbsp; In both cases, the estimate is very accurate.&amp;nbsp; (There do happen to be indexes on each of the individual predicate columns, although even if there weren't indexes, it would automatically create statistics and get similar estimates.)&amp;nbsp;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: left;"&gt;&lt;span style="font-size: x-small;"&gt;&lt;/span&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: left;"&gt;&lt;span style="font-size: x-small;"&gt;&lt;span style="color: blue;"&gt;select&lt;/span&gt; &lt;/span&gt;&lt;span style="color: grey; font-size: x-small;"&gt;&lt;span style="color: grey; font-size: x-small;"&gt;*&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size: x-small;"&gt; &lt;/span&gt;&lt;span style="color: blue; font-size: x-small;"&gt;&lt;span style="color: blue; font-size: x-small;"&gt;from&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size: x-small;"&gt; internetlogtable &lt;/span&gt;&lt;span style="color: blue; font-size: x-small;"&gt;&lt;span style="color: blue; font-size: x-small;"&gt;with &lt;/span&gt;&lt;/span&gt;&lt;span style="color: grey; font-size: x-small;"&gt;&lt;span style="color: grey; font-size: x-small;"&gt;(&lt;/span&gt;&lt;/span&gt;&lt;span style="color: blue; font-size: x-small;"&gt;&lt;span style="color: blue; font-size: x-small;"&gt;nolock&lt;/span&gt;&lt;/span&gt;&lt;span style="color: grey; font-size: x-small;"&gt;&lt;span style="color: grey; font-size: x-small;"&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: left;"&gt;&lt;span style="color: grey; font-size: x-small;"&gt;&lt;span style="color: grey; font-size: x-small;"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="color: blue; font-size: x-small;"&gt;&lt;span style="color: blue; font-size: x-small;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; where &lt;span style="color: black;"&gt;ClientAgentId&lt;/span&gt;=72383&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: left;"&gt;&lt;span style="font-size: x-small;"&gt;&lt;span style="color: blue;"&gt;select&lt;/span&gt; &lt;/span&gt;&lt;span style="color: grey; font-size: x-small;"&gt;&lt;span style="color: grey; font-size: x-small;"&gt;*&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size: x-small;"&gt; &lt;/span&gt;&lt;span style="color: blue; font-size: x-small;"&gt;&lt;span style="color: blue; font-size: x-small;"&gt;from&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size: x-small;"&gt; internetlogtable &lt;/span&gt;&lt;span style="color: blue; font-size: x-small;"&gt;&lt;span style="color: blue; font-size: x-small;"&gt;with &lt;/span&gt;&lt;/span&gt;&lt;span style="color: grey; font-size: x-small;"&gt;&lt;span style="color: grey; font-size: x-small;"&gt;(&lt;/span&gt;&lt;/span&gt;&lt;span style="color: blue; font-size: x-small;"&gt;&lt;span style="color: blue; font-size: x-small;"&gt;nolock&lt;/span&gt;&lt;/span&gt;&lt;span style="color: grey; font-size: x-small;"&gt;&lt;span style="color: grey; font-size: x-small;"&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: left;"&gt;&lt;span style="color: grey; font-size: x-small;"&gt;&lt;span style="color: grey; font-size: x-small;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;&lt;/span&gt;&lt;span style="color: blue; font-size: x-small;"&gt;&lt;span style="color: blue; font-size: x-small;"&gt;where &lt;span style="color: black;"&gt;ClientHostID&lt;/span&gt;&lt;span style="color: grey; font-size: x-small;"&gt;&lt;span style="color: grey; font-size: x-small;"&gt;=&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size: x-small;"&gt;417408&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;span style="color: blue; font-size: x-small;"&gt;&lt;span style="color: blue; font-size: x-small;"&gt;&lt;/span&gt;&lt;/span&gt;However if we combine both predicates, as in the following query&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size: x-small;"&gt;&lt;span style="color: blue;"&gt;select&lt;/span&gt; &lt;/span&gt;&lt;span style="color: grey; font-size: x-small;"&gt;&lt;span style="color: grey; font-size: x-small;"&gt;*&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size: x-small;"&gt; &lt;/span&gt;&lt;span style="color: blue; font-size: x-small;"&gt;&lt;span style="color: blue; font-size: x-small;"&gt;from&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size: x-small;"&gt; internetlogtable &lt;/span&gt;&lt;span style="color: blue; font-size: x-small;"&gt;&lt;span style="color: blue; font-size: x-small;"&gt;with &lt;/span&gt;&lt;/span&gt;&lt;span style="color: grey; font-size: x-small;"&gt;&lt;span style="color: grey; font-size: x-small;"&gt;(&lt;/span&gt;&lt;/span&gt;&lt;span style="color: blue; font-size: x-small;"&gt;&lt;span style="color: blue; font-size: x-small;"&gt;nolock&lt;/span&gt;&lt;/span&gt;&lt;span style="color: grey; font-size: x-small;"&gt;&lt;span style="color: grey; font-size: x-small;"&gt;)&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: left;"&gt;&lt;span style="color: grey; font-size: x-small;"&gt;&lt;span style="color: grey; font-size: x-small;"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="color: blue; font-size: x-small;"&gt;&lt;span style="color: blue; font-size: x-small;"&gt;       where &lt;span style="color: black;"&gt;ClientAgentId&lt;/span&gt;=72383&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: left;"&gt;&lt;span style="color: blue; font-size: x-small;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; and &lt;span style="color: black;"&gt;ClientHostID&lt;/span&gt;&lt;span style="color: grey; font-size: x-small;"&gt;&lt;span style="color: grey; font-size: x-small;"&gt;=&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size: x-small;"&gt;417408&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: left;"&gt;&lt;a href="http://4.bp.blogspot.com/-XPyc-QejbQA/TstTVgxflpI/AAAAAAAAAhw/9sWwV7Vcu1k/s1600/QueryPlanWithoutStats.png" imageanchor="1" style="clear: right; float: right; margin-bottom: 1em; margin-left: 1em;"&gt;&lt;img border="0" height="210" src="http://4.bp.blogspot.com/-XPyc-QejbQA/TstTVgxflpI/AAAAAAAAAhw/9sWwV7Vcu1k/s400/QueryPlanWithoutStats.png" width="400" /&gt;&lt;/a&gt;The optimiser assumes that ClientAgentId and ClientHostId are independent and thus there will be very few, if any, records satisfying both predicates.&amp;nbsp;&amp;nbsp;Essentially it is guessing that the number of rows returned will be 332/1200000 * 110/1200000.&amp;nbsp; It rounds the number up to 1.&amp;nbsp;&amp;nbsp; Here is the query plan without statistics.&amp;nbsp; Notice how the Estimated Number of Rows is 1.&amp;nbsp; Actually, when it says 1, it typically is estimating &amp;lt;1 records, but it always rounds up to 1.&lt;/div&gt;&lt;br /&gt;Now, what would happen if we create statistics on the two predicate columns?&lt;br /&gt;&lt;span style="color: blue; font-size: x-small;"&gt;&lt;span style="color: blue; font-size: x-small;"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size: x-small;"&gt;&lt;span style="color: blue;"&gt;create&lt;/span&gt; &lt;/span&gt;&lt;span style="color: blue; font-size: x-small;"&gt;&lt;span style="color: blue; font-size: x-small;"&gt;statistics&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size: x-small;"&gt; StatsAgentIdHostId &lt;/span&gt;&lt;br /&gt;&lt;span style="font-size: x-small;"&gt;&lt;/span&gt;&lt;span style="font-size: x-small;"&gt;&amp;nbsp; &lt;span style="color: blue;"&gt;on&lt;/span&gt; internetlogtable&lt;/span&gt;&lt;span style="color: blue; font-size: x-small;"&gt;&lt;span style="color: blue; font-size: x-small;"&gt; &lt;/span&gt;&lt;/span&gt;&lt;span style="color: grey; font-size: x-small;"&gt;&lt;span style="color: grey; font-size: x-small;"&gt;(&lt;span style="color: black;"&gt;ClientHostID,&lt;/span&gt;&lt;span style="color: grey; font-size: x-small;"&gt;&lt;span style="color: grey; font-size: x-small;"&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size: x-small;"&gt;ClientAgentID&lt;/span&gt;&lt;span style="color: grey; font-size: x-small;"&gt;&lt;span style="color: grey; font-size: x-small;"&gt;)&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="color: grey; font-size: x-small;"&gt;&lt;span style="color: grey; font-size: x-small;"&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://3.bp.blogspot.com/-wJiOhBVA8yo/TstTf5GDp1I/AAAAAAAAAh4/s_SDYPo4DIc/s1600/QueryPlanWithStats.png" imageanchor="1" style="clear: right; float: right; margin-bottom: 1em; margin-left: 1em;"&gt;&lt;img border="0" height="348" src="http://3.bp.blogspot.com/-wJiOhBVA8yo/TstTf5GDp1I/AAAAAAAAAh4/s_SDYPo4DIc/s400/QueryPlanWithStats.png" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;SQL Server's optimiser now has data on the combinations of ClientHostId and ClientAgentId and finds that this particular combination will return about 110 rows.&amp;nbsp; Here is the query plan.&amp;nbsp; Notice that the Estimated Number of Rows is now 110.96!&amp;nbsp; This is much more accurate.&lt;br /&gt;&lt;br /&gt;In the query example I have used here, the query plans are different, although not&amp;nbsp; dramatically different.&amp;nbsp; However, if this table was joined with a few others, some of which very large, the query plan can change dramatically and make the difference between a sub second query and a several minute query.&lt;br /&gt;&lt;br /&gt;Understanding these subtleties of the SQL Server query optimiser will help you tune queries in an efficient manner.&amp;nbsp; If you were not aware of this technique to create bespoke statistics, you might solve this issue with a new index on the two columns, which would solve this query performance but at a great cost to overall&amp;nbsp;performance.&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: left;"&gt;&lt;a href="http://3.bp.blogspot.com/-rCDzUsD02kk/TstV_MtGz8I/AAAAAAAAAiA/Sed4lli8xwU/s1600/Show_Statistics.png" imageanchor="1" style="clear: right; float: right; margin-bottom: 1em; margin-left: 1em;"&gt;&lt;img border="0" height="240" src="http://3.bp.blogspot.com/-rCDzUsD02kk/TstV_MtGz8I/AAAAAAAAAiA/Sed4lli8xwU/s400/Show_Statistics.png" width="400" /&gt;&lt;/a&gt;By the way, here is an illustration of what statistics SQL Server keeps.&amp;nbsp; There is a lot of information.&amp;nbsp; For example, there are even as many key ranges as can fit in an 8KB page with the number of distinct keys,&amp;nbsp;equal keys&amp;nbsp;and rows in the range.&amp;nbsp; Notice the &lt;strong&gt;All density&lt;/strong&gt; column in the second set of statistics.&amp;nbsp; It is telling us that a single value for&amp;nbsp;ClientHostId, will on average return 0.0001507841 of the total rows.&amp;nbsp; It also tells us that ClientHostId and ClientAgentId will, on average return 0.0001226091 of the rows.&amp;nbsp; Not much less!&amp;nbsp; Ie, if you have the ClientHostId, the ClientAgentId does not filter the rows much more.&lt;/div&gt;&lt;br /&gt;I encourage you to get to know the SQL optimiser better.&amp;nbsp; The more you know about the optimiser, the more you will be able to tune your queries.&amp;nbsp; And the way to get to know the optimiser is through the query show plan.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3689647052791307812-6880704360581073495?l=richardlees.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://richardlees.blogspot.com/feeds/6880704360581073495/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3689647052791307812&amp;postID=6880704360581073495' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3689647052791307812/posts/default/6880704360581073495'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3689647052791307812/posts/default/6880704360581073495'/><link rel='alternate' type='text/html' href='http://richardlees.blogspot.com/2011/11/fix-query-performance-by-creating.html' title='Fix Query Performance by Creating Statistics'/><author><name>Richard Lees</name><uri>http://www.blogger.com/profile/05671716466559973540</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='31' height='32' src='http://2.bp.blogspot.com/-AdeWScnQel8/Thj9ePsjpQI/AAAAAAAAAew/XnIn3xt4a0s/s220/RichardUni.png'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://1.bp.blogspot.com/-vL65ZiUf_e0/TsqsOmXauFI/AAAAAAAAAhg/orIZsE9fWaQ/s72-c/DatabaseOptions.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3689647052791307812.post-7378638238645554908</id><published>2011-09-17T16:59:00.001+10:00</published><updated>2011-09-17T17:00:25.119+10:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='IO Performance'/><category scheme='http://www.blogger.com/atom/ns#' term='Analysis Services'/><category scheme='http://www.blogger.com/atom/ns#' term='SQL Performance'/><category scheme='http://www.blogger.com/atom/ns#' term='MAXDOP'/><title type='text'>Parallelised Queries are Selfish Queries</title><content type='html'>&lt;strong&gt;SQL Server Parallel Query Processing and MAXDOP&lt;/strong&gt;&lt;br /&gt;&lt;br /&gt;&lt;a href="http://4.bp.blogspot.com/-DlhQMeR15fU/TnPSsPdfR2I/AAAAAAAAAfk/lFt1N37l148/s1600/ParallelTask.jpg" imageanchor="1" style="clear: right; float: right; margin-bottom: 1em; margin-left: 1em;"&gt;&lt;img border="0" src="http://4.bp.blogspot.com/-DlhQMeR15fU/TnPSsPdfR2I/AAAAAAAAAfk/lFt1N37l148/s1600/ParallelTask.jpg" /&gt;&lt;/a&gt;I am prompted into writing this post after reading the msdn article &lt;strong&gt;&lt;a href="http://msdn.microsoft.com/en-us/library/gg415714.aspx"&gt;&lt;span style="font-size: x-small;"&gt;Understanding and Controlling Parallel Query Processing in SQL Server&lt;/span&gt;&lt;/a&gt;&lt;/strong&gt;&lt;span style="font-size: x-small;"&gt;.&amp;nbsp; &lt;/span&gt;&lt;span style="font-size: small;"&gt;It is a very informative article.&amp;nbsp; I cannot disagree with anything in the article, but there is an elephant in the room.&amp;nbsp; The article dissregards IO performance bottlenecks.&amp;nbsp; It&amp;nbsp;actually states that it will disregard IO performance.&amp;nbsp; &lt;em&gt;&lt;span style="color: purple;"&gt;"We assume that your I/O system is configured properly ..... so that the I/O system is not the primary performance limiter."&lt;/span&gt;&lt;/em&gt;&amp;nbsp; I am going to discuss IO performance with MAXDOP, because I believe it is often the &lt;u&gt;main&lt;/u&gt; performance limiter.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;As a bit of background, SQL Server has a parallel query processing feature that is enabled by default.&amp;nbsp; This permits SQL Optimiser to choose a plan that involves breaking one query into sub tasks that can run in parallel.&amp;nbsp; This can involve executing, essentially,&amp;nbsp;the same plan, but in parallel subtasks, or it can involve a completely different plan&amp;nbsp;and again&amp;nbsp;breaking the query into subtasks that can run in parallel.&amp;nbsp; Really quite an amazing piece of technology.  When it is working well for a query, it is interesting to see figures like cpu=10 seconds, elapsed=1 second!&lt;br /&gt;&lt;br /&gt;In many of the very large database environments where I work, IO is the most critical resource.&amp;nbsp; Perhaps this is my misfortune, but&amp;nbsp;I suggest that IO performance is often a performance limiter in large database systems, and probably will be so&amp;nbsp;until SSD (&lt;a href="http://richardlees.blogspot.com/2010/05/solid-state-disks-and-io-performance.html"&gt;solid state disks&lt;/a&gt;) are more commonly employed.&amp;nbsp; The problem with parallel query plans is that one query can simultaneously request multiple table scans, selfishly requesting more resources in parallel in the hope of getting a faster response.&amp;nbsp; Just one table scan from one query will tend to have parallel IO requests, as multiple SQL Server read engines are given multiple page read ahead requests.&amp;nbsp; So, even with a single non parallelised SQL query, there will already be some parallel IO requests.&amp;nbsp; Run several non parallelised queries at the same time and there are many more parallel IO requests than there are queries.&amp;nbsp; That is all before Parallel Query Processing kicks in!&lt;br /&gt;&lt;br /&gt;Let's take a very simple example, where the SQL query has a predicate on a non indexed column of a very large table (the table is larger than SQL cache), therefore requiring a sequential scan of the table.&amp;nbsp; Without Parallel Query Processing, SQL Server will sequentially scan the very large table, using several read engines working in parallel to get multiple page ranges.&amp;nbsp; Note, there is already parallel IO requests with a single non parallelised SQL query.&amp;nbsp; Now if we enable Parallel Query, SQL can break the scan into several sub tasks, each of which will ask for range scans of the table.&amp;nbsp; Each of these scans can busy up multiple read engines, which can lead to IO throughput saturation.&amp;nbsp; That is the parallelised query will result in scanning the same table, but it will flood the IO system with many&amp;nbsp;more parallel&amp;nbsp;requests that can end up in a long&amp;nbsp;queue.&amp;nbsp; A good way to think about it is to imagine an old fashioned bank branch with 30 tellers and everyone queues up for the next available teller.&amp;nbsp; That would be non parallelised.&amp;nbsp; Now imagine that someone comes in with bags of money that requires lots of counting and when he/she gets to the front of the queue, all the tellers attend to counting his/her money together&amp;nbsp;(in parallel).&amp;nbsp; This might be good for the selfish&amp;nbsp;individual, they get served faster,&amp;nbsp;but it is not good for the performance of the branch, as now all customers will be waiting for the selfish customer to finish.&amp;nbsp; For this reason, I like to think of parallelised queries as selfish, or unsocial.&amp;nbsp; They want to take all the resources, even though this is not an efficient use of the overall&amp;nbsp;resources and others are negatively impacted.&amp;nbsp; This is fine in a system with very few (perhaps one) customer.&amp;nbsp; Note, there is always an overhead in parallelising a query, even if it is using essentially the same plan as a non parallelised query.&amp;nbsp; This is because there is overhead in distributing and regathering the sub tasks.&lt;br /&gt;&lt;br /&gt;There is an argument that data warehouses have very few users so that Parallel Query is an ideal performance option.&amp;nbsp; However, I see modern data warehouses&amp;nbsp;with large numbers of users simultaneously&amp;nbsp;submitting queries (dashboards, web pages, reports etc), and you typically&amp;nbsp;don't want one user to selfishly hog critical resources such as IO.&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;/div&gt;&lt;a href="http://3.bp.blogspot.com/-772dYyLgXHk/TnPTO5knM7I/AAAAAAAAAfs/A9KtBDn7ohQ/s1600/ParallelQuery.png" imageanchor="1" style="clear: right; float: right; margin-bottom: 1em; margin-left: 1em;"&gt;&lt;img border="0" height="287" src="http://3.bp.blogspot.com/-772dYyLgXHk/TnPTO5knM7I/AAAAAAAAAfs/A9KtBDn7ohQ/s320/ParallelQuery.png" width="320" /&gt;&lt;/a&gt;I might sound like I am against Parallel Query.&amp;nbsp; That isn't true.&amp;nbsp; It is a fantastic feature.&amp;nbsp; However, I would encourage DBAs to consider setting Max Degree of Parallelism to 1.&amp;nbsp; Note; that is not disabling Parallel Query, it is just the default.&amp;nbsp; So, by default, queries will not be selfish.&amp;nbsp;&amp;nbsp;If you have a particular ETL task or report query that you want to parallelise, you can simple add OPTION (MAXDOP n) to the query, where n is the number of potential sub tasks running in parallel.&amp;nbsp; So you are not turning it off, you are just ensuring that queries aren't selfish by default.&amp;nbsp; Also, just to be clear, with Max Degree of Parallelism&amp;nbsp;set to 1, your system can still have many SQL queries running in parallel (with each other) it is only that&amp;nbsp;individual queries will not be broken into parallel running&amp;nbsp;subtasks.&lt;br /&gt;&lt;br /&gt;The reason I configured Max Degree of Parallelism to 1 the first&amp;nbsp;few times&amp;nbsp;was because&amp;nbsp;I was working in environments with a wayward parallelised queries. &amp;nbsp;SQL Optimiser chose a very very poor plan thinking that parallelisation would make this plan fast.&amp;nbsp; The same queries with Max Degree of Parallelism&amp;nbsp;=1 (or MAXDOP 1) the optimiser chose a very different, and efficient plan.&amp;nbsp; When I reduced the "Max Degree of Parallelism" to 1, these queries, and the whole system ran more efficiently.&lt;br /&gt;&lt;br /&gt;An annoying limitation of MAXDOP is that you are unable to set MAXDOP for queries that SQL Server Analysis Services uses when processing fact tables.&amp;nbsp; You can only provide SSAS with a table or view name, and it constructs the query.&amp;nbsp; There is no way to inject a MAXDOP option.&amp;nbsp; This is another reason I like to set Max Degree of Parallelism = 1, so that the default setting is non parallelised.&lt;br /&gt;&lt;br /&gt;One word of warning.&amp;nbsp; Changing Max Degree of Parallelism to 1,&amp;nbsp;can (in fact by design it should) make some queries run slower.&amp;nbsp; It can also change dramatically the query plan (as I discussed above) and while this will usually result in more efficient queries, it can also involve a poorer performing plan.&amp;nbsp; In my experience the reverse is more likely (when there are considerably different plans between parallelised and non parallelised, the non parallelised plan is more often the more efficient.&amp;nbsp; Consequently, it is less risky to configure Max Degree of Parallelism&amp;nbsp;=1 while developing a solution (and using MAXDOP n for individual queries where appropriate) than to change a production environment.&lt;br /&gt;&lt;br /&gt;The effect of Max Degree of Parallelism is not just on the individual query.&amp;nbsp; If you want to know the impact of Max Degree of Parallelism, you should monitor vital statistics (cpu, memory, IO), get average query response times (for common queries) and compare them with before and after&amp;nbsp; configuration changes.&amp;nbsp; Another, albeit small, cost of Max Degree of Parallelism&amp;lt;&amp;gt;1 is that each parallelised query will now have two plans (it always keeps a non parallel version for when cpu is high) so that more pressure is put on the&amp;nbsp;plan cache (it is bigger, or it holds fewer plans).&amp;nbsp; Note, SQL Server is great in that you can change the Max Degree of Parallelism option dynamically.&amp;nbsp; No SQL Server restart required.&amp;nbsp; You might even consider setting Max Degree of Parallelism =1 for the online day and resetting Max Degree of Parallelism =0 for the overnight ETL.&lt;br /&gt;&lt;br /&gt;A closely related issue is &lt;strong&gt;Resource Governor&lt;/strong&gt;, which also has a MAXDOP option that looks like it can override the MAXDOP option in the query.&amp;nbsp; However, as I understand it, the Resource Governor does not work like MAXDOP in a query or sp_configure.&amp;nbsp; If you set MAXDOP to 1 in the Resource Governor, the underlying queries can still chose parallel plans (no change there).&amp;nbsp; What the Resource Governor does is to restrict the query to only one thread!&amp;nbsp;&amp;nbsp; This is almost the worst of both worlds, as the optimiser can choose the selfish (potentially faster) parallel plan, but the Resource Governor will ensure that the parallel&amp;nbsp;tasks are serialised with just one thread.&lt;br /&gt;&lt;br /&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3689647052791307812-7378638238645554908?l=richardlees.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://richardlees.blogspot.com/feeds/7378638238645554908/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3689647052791307812&amp;postID=7378638238645554908' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3689647052791307812/posts/default/7378638238645554908'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3689647052791307812/posts/default/7378638238645554908'/><link rel='alternate' type='text/html' href='http://richardlees.blogspot.com/2011/09/parallelised-queries-are-selfish.html' title='Parallelised Queries are Selfish Queries'/><author><name>Richard Lees</name><uri>http://www.blogger.com/profile/05671716466559973540</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='31' height='32' src='http://2.bp.blogspot.com/-AdeWScnQel8/Thj9ePsjpQI/AAAAAAAAAew/XnIn3xt4a0s/s220/RichardUni.png'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://4.bp.blogspot.com/-DlhQMeR15fU/TnPSsPdfR2I/AAAAAAAAAfk/lFt1N37l148/s72-c/ParallelTask.jpg' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3689647052791307812.post-1286089658700535791</id><published>2011-09-11T11:22:00.000+10:00</published><updated>2011-09-12T06:12:28.076+10:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='MDX'/><title type='text'>Why are there less data in the cube than the fact table?</title><content type='html'>Sereral customer have asked me why they have less data in their cube than they have in the fact table.&amp;nbsp; Essentially, when they view the cube Measures.NetSales does not equal Sum(NetSales) from the fact table.&amp;nbsp; So here are the&amp;nbsp;some possible causes, that I would investigate in such circumstances.&lt;br /&gt;&lt;ol&gt;&lt;li&gt;The measure&amp;nbsp;datatype cannot hold the aggregated number.&amp;nbsp; For example, the fact table might have an integer datatype for the SalesQuantity column, which might be fine for the SQL table.&amp;nbsp; However, by default, SSAS will give the SalesQuantity measure the same datatype, but now it must be able to contain the sum(SalesQuantity).&amp;nbsp; If sum(SalesQuantity) is greater than 2^31 SSAS will not display any errors, and the Measures.SalesQuantity will show numbers that appear to wrap around.&amp;nbsp; So the numbers are much smaller than expected (almost random)&amp;nbsp;at an aggregated level.&amp;nbsp; The solution is simple, just change the datatype in the cube to a much larger number, for example bigint, which can be up to 2^63.&amp;nbsp; One of the good things about using bigint over int, is that the cube is almost exactly the same size, as SSAS will compress the leading zeroes in&amp;nbsp;binary numbers very effectively.&amp;nbsp; If you need numbers larger than 2^63, you might need to use a double datatype, which, although potentially much larger is an imprecise number.&amp;nbsp; Also, a double datatype measure, will typically make the cube significantly larger than a binary measure containing the same information.&lt;/li&gt;&lt;li&gt;There are missing members on one or more dimensions and missing members are not&amp;nbsp;enabled.&amp;nbsp; The missing member facts&amp;nbsp;will not be included in the cube.&amp;nbsp;&amp;nbsp; I personally believe the cube should contain all the facts in the fact able.&amp;nbsp; Even if there is a missing member, the data should still be available.&amp;nbsp; For example, if there are sales for a ProductId that does not exist, typically, you will still want to know about the sales even though you cannot say which product it was for.&amp;nbsp; I prefer to enable missing members, and just hide them if you must.&amp;nbsp; It is also a good practice to regularly check for missing members, either in the DW or in the cube (through missing members).&lt;/li&gt;&lt;li&gt;The third situation where the cube numbers are light is an unusual one.&amp;nbsp; It is where the fact table (actually a view) returns an arithmetic error such as divide by zero.&amp;nbsp; Most times SSAS will sympathetically return the SQL error, and you will know there is a problem during cube processing.&amp;nbsp; However, sometimes SSAS does not sympathetically return the SQL error, rather, it returns a successful cube process with the rows up until the divide by zero.&amp;nbsp; There are two ways of locating this error.&amp;nbsp; Firstly, you could simply execute the same SQL query in SQLEM window and wait for the full resultset or the arithmetic error to return.&amp;nbsp; This is not practical in many circumstances due to the number of rows in the resultset.&amp;nbsp; I guess you could get SSIS to help you, but an easier way is to use SQL Profiler and trace Error events.&lt;/li&gt;&lt;li&gt;You have a referenced dimension and the INNER JOIN clause that SSAS is generating for SQL Server is removing fact records that don't join with the intermediate dimension.&amp;nbsp; To identify this issue, you need to execute the SQL generated by SSAS, and compare its counts with the raw fact table.&lt;/li&gt;&lt;li&gt;Is your cube (measure group) processed incrementally?&amp;nbsp;&amp;nbsp; If so, there might be a logic issue with the way you are incrementally processing.&amp;nbsp; To verify, you could reprocess the entire measure group and see if the numbers now equal the fact table.&lt;/li&gt;&lt;li&gt;Is the data in&amp;nbsp;your fact table changing?&amp;nbsp; Of course, if the fact table is changing, the MOLAP cube won't know about it.&amp;nbsp; This can be related to incremental processing, where the incremental processing logic is good, but historic data records are changing.&amp;nbsp; Strictly speaking you cannot incrementally process updates, only new records.&amp;nbsp; If the data has changed, the cube (partition)&amp;nbsp;will require processing.&lt;/li&gt;&lt;li&gt;Have you a default member that is not the [All] mmeber, or do you have a dimension without an [All] member.&amp;nbsp; In these cases, you would need to take this into account when comparing Measures.NetSales with sum(NetSales).&lt;/li&gt;&lt;/ol&gt;I hope this help you identify reconciliation issues between the cube and fact table.&amp;nbsp; A couple of suggestions when reconciling such issues&lt;br /&gt;&lt;ol&gt;&lt;li&gt;Use physical measures in the cube.&amp;nbsp; Even if they are hidden, you can still query the physical measures.&lt;/li&gt;&lt;li&gt;Ideally, start off by reconciling record counts.&amp;nbsp; If you can, put a record count in the cube.&lt;/li&gt;&lt;li&gt;When you are running SQL queries against the fact table, use the SQL statement that is generated by SSAS, since it might be referencing a different table/view than you expected.&amp;nbsp; Also, as in point 4 above, it might be performing an INNER JOIN with an intermediate dimension that has missing members.&lt;/li&gt;&lt;/ol&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3689647052791307812-1286089658700535791?l=richardlees.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://richardlees.blogspot.com/feeds/1286089658700535791/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3689647052791307812&amp;postID=1286089658700535791' title='6 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3689647052791307812/posts/default/1286089658700535791'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3689647052791307812/posts/default/1286089658700535791'/><link rel='alternate' type='text/html' href='http://richardlees.blogspot.com/2011/09/why-are-there-less-data-in-cube-than.html' title='Why are there less data in the cube than the fact table?'/><author><name>Richard Lees</name><uri>http://www.blogger.com/profile/05671716466559973540</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='31' height='32' src='http://2.bp.blogspot.com/-AdeWScnQel8/Thj9ePsjpQI/AAAAAAAAAew/XnIn3xt4a0s/s220/RichardUni.png'/></author><thr:total>6</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3689647052791307812.post-2510265621393028595</id><published>2011-08-10T07:41:00.009+10:00</published><updated>2011-08-14T20:13:41.523+10:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='MDX'/><category scheme='http://www.blogger.com/atom/ns#' term='OLAP Performance'/><title type='text'>Improving MDX join performance</title><content type='html'>Analysis Services will tend to instantiate sets created from joins. Some sets can be extremely large and will actually fail with insufficient resources.&lt;br /&gt;&lt;span style="font-family:arial;color:#ff0000;"&gt;Server: The operation has been cancelled.&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;The solution is to write the MDX query in such a way that the intermediate sets are smaller. Here is an example of a problematic query that is asking for the top 10 hour/City/Date combinations. The output is simply 10 rows, but AS must create a set with hundreds of Millions of members to get the top 10.&lt;br /&gt;&lt;span style="COLOR: rgb(102,0,204);font-family:courier new;font-size:85%;"  &gt;&lt;br /&gt;select&lt;br /&gt;[Measures].[Bytes Total] on Columns,&lt;br /&gt;topcount(&lt;br /&gt;[Client Host].[Client Geography].[City]&lt;br /&gt;*[HoursOfDay].[HoursOfDay].[Hour Of Day]&lt;br /&gt;*[Date].[Year Month Day].[Day]&lt;br /&gt;,10,[Measures].[Bytes Total])&lt;br /&gt;on Rows&lt;br /&gt;from EasternMining&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Here is a way to improve performance using the generate() function, another one of my favourite MDX functions.&lt;br /&gt;&lt;span style="COLOR: rgb(102,0,204);font-family:courier new;font-size:85%;"  &gt;&lt;br /&gt;select&lt;br /&gt;[Measures].[Bytes Total] on Columns,&lt;br /&gt;topcount(&lt;br /&gt;generate([Client Host].[Client Geography].[City]&lt;br /&gt;,{[Client Host].[Client Geography].currentmember}&lt;br /&gt;*[HoursOfDay].[HoursOfDay].[Hour Of Day]&lt;br /&gt;*[Date].[Year Month Day].[Day])&lt;br /&gt;,10,[Measures].[Bytes Total])&lt;br /&gt;on Rows&lt;br /&gt;from EasternMining&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;The generate statement is breaking the query into a couple of steps, so that the largest set will probably be the Days*Hours, from which it is only keeping the top 10 for each city. The outside set, for every city, will only have 10 rows per city. The query is logically equivalent to the first query, meaning it will return the same results.&lt;br /&gt;&lt;br /&gt;We could take this logic further with a second generate statement so the inside query only has 1o rows per city day combination.&lt;br /&gt;&lt;span style="COLOR: rgb(102,0,204);font-family:courier new;font-size:85%;"  &gt;&lt;br /&gt;select&lt;br /&gt;[Measures].[Bytes Total] on Columns,&lt;br /&gt;topcount(&lt;br /&gt;generate([Client Host].[Client Geography].[City]&lt;br /&gt;,topcount(&lt;br /&gt;generate([HoursOfDay].[HoursOfDay].[Hour Of Day]&lt;br /&gt;,topcount({[HoursOfDay].[HoursOfDay].currentmember}&lt;br /&gt;*{[Client Host].[Client Geography].&lt;br /&gt;currentmember}&lt;br /&gt;*[Date].[Year Month Day].[Day]&lt;br /&gt;,10,[Measures].[Bytes Total]))&lt;br /&gt;,10,[Measures].[Bytes Total]))&lt;br /&gt;,10,[Measures].[Bytes Total])&lt;br /&gt;on Rows&lt;br /&gt;from EasternMining&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;We can improve this query further by requesting nonempty() sets&lt;br /&gt;&lt;span style="COLOR: rgb(102,0,204);font-family:courier new;font-size:85%;"  &gt;&lt;br /&gt;select&lt;br /&gt;[Measures].[Bytes Total] on Columns,&lt;br /&gt;topcount(&lt;br /&gt;generate(nonempty([Client Host].[Client Geography].[City],[Measures].[Bytes Total])&lt;br /&gt;,topcount(&lt;br /&gt;generate(nonempty([HoursOfDay].[HoursOfDay].[Hour Of Day],[Measures].[Bytes Total])&lt;br /&gt;,topcount({[HoursOfDay].[HoursOfDay].currentmember}&lt;br /&gt;*{[Client Host].[Client Geography].currentmember}&lt;br /&gt;*nonempty([Date].[Year Month Day].[Day],[Measures].[Bytes Total])&lt;br /&gt;,10,[Measures].[Bytes Total]))&lt;br /&gt;,10,[Measures].[Bytes Total]))&lt;br /&gt;,10,[Measures].[Bytes Total])&lt;br /&gt;on Rows&lt;br /&gt;from EasternMining&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;The NonEmpty() function is a very simple way to reduce the set size, and of course it can be done without using the generate() function. So going back to the original query, we might have found that the following change was sufficient to avoid resource errors.&lt;br /&gt;&lt;br /&gt;&lt;span style="COLOR: rgb(102,0,204);font-family:courier new;font-size:85%;"  &gt;&lt;br /&gt;select&lt;br /&gt;[Measures].[Bytes Total] on Columns,&lt;br /&gt;topcount(&lt;br /&gt;nonempty(&lt;br /&gt;[Client Host].[Client Geography].[City]&lt;br /&gt;*[HoursOfDay].[HoursOfDay].[Hour Of Day]&lt;br /&gt;*[Date].[Year Month Day].[Day]&lt;br /&gt;,[Measures].[Bytes Total])&lt;br /&gt;,10,[Measures].[Bytes Total])&lt;br /&gt;on Rows&lt;br /&gt;from EasternMining&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;However, I wanted to show the generate() function first, as it can improve the query in a way that the nonempty() cannot.&lt;br /&gt;&lt;br /&gt;I should add that generate() and nonempty() functions are not panaceas for MDX query performance. The more you know MDX and the way SSAS executes the query, the more you will have in your toolkit to improve query performance.&lt;br /&gt;&lt;br /&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3689647052791307812-2510265621393028595?l=richardlees.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://richardlees.blogspot.com/feeds/2510265621393028595/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3689647052791307812&amp;postID=2510265621393028595' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3689647052791307812/posts/default/2510265621393028595'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3689647052791307812/posts/default/2510265621393028595'/><link rel='alternate' type='text/html' href='http://richardlees.blogspot.com/2011/08/improving-mdx-join-performance.html' title='Improving MDX join performance'/><author><name>Richard Lees</name><uri>http://www.blogger.com/profile/05671716466559973540</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='31' height='32' src='http://2.bp.blogspot.com/-AdeWScnQel8/Thj9ePsjpQI/AAAAAAAAAew/XnIn3xt4a0s/s220/RichardUni.png'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3689647052791307812.post-8240844940285665739</id><published>2011-07-16T17:10:00.008+10:00</published><updated>2011-07-16T17:36:55.494+10:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='MDX'/><category scheme='http://www.blogger.com/atom/ns#' term='Analysis Services'/><title type='text'>MDX Root() function doesn't support multiselect</title><content type='html'>&lt;a href="http://2.bp.blogspot.com/-McqP86C-PwA/TiE-W40Hq_I/AAAAAAAAAfQ/SCXHqWdy5V4/s1600/RootFunction.png"&gt;&lt;img style="MARGIN: 0px 0px 10px 10px; WIDTH: 400px; FLOAT: right; HEIGHT: 331px; CURSOR: pointer" id="BLOGGER_PHOTO_ID_5629849572118866930" border="0" alt="" src="http://2.bp.blogspot.com/-McqP86C-PwA/TiE-W40Hq_I/AAAAAAAAAfQ/SCXHqWdy5V4/s400/RootFunction.png" /&gt;&lt;/a&gt;One of my other favourite MDX functions is Root(). It's a really easy way to get all the filters off a dimension. I often use it in calculations defined in the cube.&lt;br /&gt;&lt;br /&gt;However the Root() function does not support multi-select and you might get this error message.&lt;br /&gt;&lt;br /&gt;&lt;span style="COLOR: rgb(255,0,0)"&gt;#Error Query (6, 30) The MDX function ROOT failed because the coordinate for the &lt;em&gt;'Region'&lt;/em&gt; attribute contains a set.&lt;/span&gt;&lt;br /&gt;Fortunately, there is a workaround. Unfortunately, it means not using the Root() function and going back to the .[All] member of all the attributes (or all the ones being used in the query) of the dimension.&lt;br /&gt;For example, the following query will hit the error.&lt;br /&gt;&lt;span style="COLOR: rgb(102,0,0);font-family:courier new;font-size:85%;"  &gt;WITH&lt;br /&gt;MEMBER [Client Host].Region.[AfricaAsia] AS&lt;br /&gt;AGGREGATE({[Client Host].Region.&amp;amp;[Africa]&lt;br /&gt;,[Client Host].Region.&amp;amp;[Asia]})&lt;br /&gt;MEMBER Measures.[HitsPercent] as&lt;br /&gt;[Measures].[Request Count]/(ROOT([Client Host]), [Measures].[Request Count])&lt;br /&gt;SELECT&lt;br /&gt;{Measures.[HitsPercent]}&lt;br /&gt;ON COLUMNS&lt;br /&gt;,[Date].[Month].[Month]&lt;br /&gt;ON ROWS&lt;br /&gt;FROM [EasternMining]&lt;br /&gt;WHERE&lt;br /&gt;([Client Host].Region.[AfricaAsia])&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;The fix is simply to change &lt;span style="COLOR: rgb(102,0,0);font-family:courier new;font-size:85%;"  &gt;Root([Client Host])&lt;/span&gt; to &lt;span style="COLOR: rgb(102,0,0);font-family:courier new;font-size:85%;"  &gt;[Client Host].Region.[All]&lt;/span&gt;. ie.&lt;br /&gt;&lt;span style="COLOR: rgb(102,0,0);font-family:courier new;font-size:85%;"  &gt;WITH&lt;br /&gt;MEMBER [Client Host].Region.[AfricaAsia] AS&lt;br /&gt;AGGREGATE({[Client Host].Region.&amp;amp;[Africa]&lt;br /&gt;,[Client Host].Region.&amp;amp;[Asia]})&lt;br /&gt;MEMBER Measures.[HitsPercent] as&lt;br /&gt;&lt;span style="COLOR: rgb(0,102,0)"&gt;// [Measures].[Request Count]/(ROOT([Client Host]), [Measures].[Request Count])&lt;br /&gt;&lt;/span&gt;[Measures].[Request Count]/([Client Host].Region.[All], [Measures].[Request Count])&lt;br /&gt;,format_string="#,#0.00%"&lt;br /&gt;,non_empty_behavior="measures.[Request Count]"&lt;br /&gt;SELECT&lt;br /&gt;{Measures.[HitsPercent]}&lt;br /&gt;ON COLUMNS&lt;br /&gt;,[Date].[Month].[Month]&lt;br /&gt;ON ROWS&lt;br /&gt;FROM [EasternMining]&lt;br /&gt;WHERE&lt;br /&gt;([Client Host].Region.[AfricaAsia])&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Note, I have only put the .[All] member in for the Region attribute, whereas you really need to add in a .[All] member for all the dimension attributes to be equivalent to the Root() function.&lt;br /&gt;&lt;br /&gt;By the way, I don't know why the Root() function doesn't support multi-select. One would imagine that it should.&lt;br /&gt;&lt;br /&gt;Because of this limitation, I might be using the Root() function less liberally in calculations stored in the cube.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3689647052791307812-8240844940285665739?l=richardlees.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://richardlees.blogspot.com/feeds/8240844940285665739/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3689647052791307812&amp;postID=8240844940285665739' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3689647052791307812/posts/default/8240844940285665739'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3689647052791307812/posts/default/8240844940285665739'/><link rel='alternate' type='text/html' href='http://richardlees.blogspot.com/2011/07/mdx-root-function-doesnt-support.html' title='MDX Root() function doesn&apos;t support multiselect'/><author><name>Richard Lees</name><uri>http://www.blogger.com/profile/05671716466559973540</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='31' height='32' src='http://2.bp.blogspot.com/-AdeWScnQel8/Thj9ePsjpQI/AAAAAAAAAew/XnIn3xt4a0s/s220/RichardUni.png'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://2.bp.blogspot.com/-McqP86C-PwA/TiE-W40Hq_I/AAAAAAAAAfQ/SCXHqWdy5V4/s72-c/RootFunction.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3689647052791307812.post-3201940939767360215</id><published>2011-06-25T13:26:00.004+10:00</published><updated>2011-06-25T13:39:24.719+10:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='SQL Server 2008'/><title type='text'>Single Click SQL Management Studio</title><content type='html'>&lt;a href="http://3.bp.blogspot.com/-VTNGYebh1vw/TgVWufm6cpI/AAAAAAAAAeo/235HzbzRtPw/s1600/SQLMgmtStudio.png"&gt;&lt;img style="MARGIN: 0px 0px 10px 10px; WIDTH: 400px; FLOAT: right; HEIGHT: 209px; CURSOR: pointer" id="BLOGGER_PHOTO_ID_5621995066600616594" border="0" alt="" src="http://3.bp.blogspot.com/-VTNGYebh1vw/TgVWufm6cpI/AAAAAAAAAeo/235HzbzRtPw/s400/SQLMgmtStudio.png" /&gt;&lt;/a&gt; &lt;br /&gt;&lt;div&gt;Here is a simple way to avoid clicking on the inevitable SQL prompt when starting SQL Server Management studio.&lt;br /&gt;On the shortcut simple add -S &lt;servername&gt;-d &lt;dbname&gt;-E&lt;br /&gt;&lt;br /&gt;For eample, on my PC, the shortcut is&lt;br /&gt;"C:\Program Files (x86)\Microsoft SQL Server\100\Tools\Binn\VSShell\Common7\IDE\Ssms.exe" -S Manly -d master -E&lt;/dbname&gt;&lt;/servername&gt;&lt;/div&gt;&lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;&lt;div&gt;Now when you click on SQL Server Management studio, it will connect automatically to the server/dbname. No more prompts to the server/dbname that you connect to every day.&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3689647052791307812-3201940939767360215?l=richardlees.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://richardlees.blogspot.com/feeds/3201940939767360215/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3689647052791307812&amp;postID=3201940939767360215' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3689647052791307812/posts/default/3201940939767360215'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3689647052791307812/posts/default/3201940939767360215'/><link rel='alternate' type='text/html' href='http://richardlees.blogspot.com/2011/06/single-click-sql-management-studio.html' title='Single Click SQL Management Studio'/><author><name>Richard Lees</name><uri>http://www.blogger.com/profile/05671716466559973540</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='31' height='32' src='http://2.bp.blogspot.com/-AdeWScnQel8/Thj9ePsjpQI/AAAAAAAAAew/XnIn3xt4a0s/s220/RichardUni.png'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://3.bp.blogspot.com/-VTNGYebh1vw/TgVWufm6cpI/AAAAAAAAAeo/235HzbzRtPw/s72-c/SQLMgmtStudio.png' height='72' width='72'/><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3689647052791307812.post-3373066838910422594</id><published>2011-06-10T06:30:00.005+10:00</published><updated>2011-07-03T13:29:50.473+10:00</updated><title type='text'>linkmember, my favourite MDX function</title><content type='html'>Linkmember is one of my favourite MDX functions. It tends to be useful whenever you have role playing dimensions. For example, when you have multiple (role playing date dimensions or role playing geography dimensions) such as OrderDate and DespatchDate, or CustomerStore and PurchaseStore.&lt;br /&gt;&lt;br /&gt;You might have a report that displays the order date on rows, with sales on columns. You would also like add a column to show sales, but sliced by delivery date. So what you want to do, is put the same, sales, measure on columns, but have the "delivery date" dimension on rows.&lt;br /&gt;&lt;br /&gt;This is exactly what the linkmember does. Simply create a calc member that uses linkmember to switch the OrderDate on rows to DeliveryDate. Here is a simple example.&lt;br /&gt;&lt;br /&gt;&lt;span style="color:#6600cc;"&gt;with member&lt;br /&gt;Measures.AmountDelivered as&lt;br /&gt;(Measures.Amount,&lt;br /&gt;linkmember(OrderDate.CalendarYWD,DeliveryDate.CalendarYWD),&lt;/span&gt;&lt;br /&gt;&lt;span style="color:#6600cc;"&gt;root(OrderDate))&lt;br /&gt;select&lt;br /&gt;{Measures.Amount,&lt;br /&gt;Measures.AmountDelivered}&lt;br /&gt;on Columns,&lt;br /&gt;non empty&lt;br /&gt;OrderDate.CalendarYWD.Day on Rows&lt;br /&gt;from MyCube&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;One thing that you need to remember is that the linkmember will effectively get the member with the same key in another dimension. However, it will not change the original member. So you probably want to also override the original member with a root() or .[All].&lt;br /&gt;&lt;br /&gt;Another way of achieving the same functionality as LinkMember is to extract the key and use StrToMember() to construct the appropriate member of the other dimension. However, LinkMember is much more efficient, and is the preferred tool.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3689647052791307812-3373066838910422594?l=richardlees.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://richardlees.blogspot.com/feeds/3373066838910422594/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3689647052791307812&amp;postID=3373066838910422594' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3689647052791307812/posts/default/3373066838910422594'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3689647052791307812/posts/default/3373066838910422594'/><link rel='alternate' type='text/html' href='http://richardlees.blogspot.com/2011/06/linkmember.html' title='linkmember, my favourite MDX function'/><author><name>Richard Lees</name><uri>http://www.blogger.com/profile/05671716466559973540</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='31' height='32' src='http://2.bp.blogspot.com/-AdeWScnQel8/Thj9ePsjpQI/AAAAAAAAAew/XnIn3xt4a0s/s220/RichardUni.png'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3689647052791307812.post-6180944089797086871</id><published>2011-06-05T10:18:00.005+10:00</published><updated>2011-06-10T13:22:40.222+10:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Learning'/><title type='text'>Tech Ed 2011 Sessions available</title><content type='html'>&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/-0iZ6Br4AFO4/TerMTuErbTI/AAAAAAAAAeg/UwWRJAGHHUM/s1600/TechEd2911.png"&gt;&lt;img style="MARGIN: 0px 0px 10px 10px; WIDTH: 320px; FLOAT: right; HEIGHT: 66px; CURSOR: pointer" id="BLOGGER_PHOTO_ID_5614524524627979570" border="0" alt="" src="http://2.bp.blogspot.com/-0iZ6Br4AFO4/TerMTuErbTI/AAAAAAAAAeg/UwWRJAGHHUM/s320/TechEd2911.png" /&gt;&lt;/a&gt; &lt;br /&gt;&lt;div&gt;If you didn't afford the time or money to go to Tech Ed, don't worry, you can still view the sessions online, and in your own time.&lt;/div&gt;&lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;&lt;div&gt;&lt;a href="http://channel9.msdn.com/Events/TechEd/NorthAmerica/2011"&gt;TechEd North America 2011&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3689647052791307812-6180944089797086871?l=richardlees.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://richardlees.blogspot.com/feeds/6180944089797086871/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3689647052791307812&amp;postID=6180944089797086871' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3689647052791307812/posts/default/6180944089797086871'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3689647052791307812/posts/default/6180944089797086871'/><link rel='alternate' type='text/html' href='http://richardlees.blogspot.com/2011/06/tech-ed-2011-sessions-available.html' title='Tech Ed 2011 Sessions available'/><author><name>Richard Lees</name><uri>http://www.blogger.com/profile/05671716466559973540</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='31' height='32' src='http://2.bp.blogspot.com/-AdeWScnQel8/Thj9ePsjpQI/AAAAAAAAAew/XnIn3xt4a0s/s220/RichardUni.png'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://2.bp.blogspot.com/-0iZ6Br4AFO4/TerMTuErbTI/AAAAAAAAAeg/UwWRJAGHHUM/s72-c/TechEd2911.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3689647052791307812.post-8839149357563266213</id><published>2011-05-04T20:20:00.007+10:00</published><updated>2011-12-13T07:06:19.921+11:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='olap cubes'/><category scheme='http://www.blogger.com/atom/ns#' term='cube design'/><title type='text'>How many cubes</title><content type='html'>I am often surprised how many cubes some BI developers create. It would appear that each time they have a requirement for a piece of information, they will create a new cube.&amp;nbsp; Sometimes this is amplified because they are using denormalised summary tables, and creating new ones when they need a bit more dimensionality or a new measure, leading to a plethora of overlapping data.&lt;br /&gt;&lt;br /&gt;Since SQL Server 2005, it is possible to put dozens of fact tables (cubes in the old days) into one cube with separate measure groups for each fact table. This is a great feature. For example, Sales and Budget have slightly different dimensionality as Sales is by cashier and hour of day, whereas, budget does not have cashier and time granularity is only available down to week. &amp;nbsp;Having these in the same cube is great, since it makes querying of sales against budget (by all the common dimensions) very easy. Also, if a user is only interested in one measure group, say sales, they can ask the browser that measure group, and they will on see the associated measures and dimensions.&amp;nbsp; In the same way they might browse a separate cube.&lt;br /&gt;&lt;br /&gt;You might ask, are there any performance implications of having one super cube?&amp;nbsp; &lt;br /&gt;&lt;ul&gt;&lt;li&gt;In terms of cube size and processing overhead, the design alternatives&amp;nbsp;are comparable.&amp;nbsp; &lt;/li&gt;&lt;li&gt;MDX queries can be broken down to FE (formula engine) and SE (storage engine) components.&amp;nbsp;&amp;nbsp; The SE component of a query isn't materially affected by unrelated dimensions, since the storage is comparable.&amp;nbsp; However, the FE component of a query is affected by the unrelated dimensions as it is works within the entire cube space (larger with more dimensions).&amp;nbsp; &lt;/li&gt;&lt;/ul&gt;There is a LookupCube function in MDX that allows you to get the value from another cube.&amp;nbsp; This might be useful in a design that exploits multiple cubes.&amp;nbsp; But, don't see this as a panacea for multiple cubes.&amp;nbsp; It has its own performance overhead and is not as functional as having the measures in one cube.&lt;br /&gt;&lt;br /&gt;There might also be operational considerations.&amp;nbsp; The design alternative I have discussed is having multiple cubes in the same database, but if the cubes really are unrelated, you might consider having separate SSAS databases for them.&amp;nbsp; This would lead to more operational options including, parallel processing, and even hosting databases on separate servers.&lt;br /&gt;&lt;br /&gt;So, as a cube designer, you must weigh up the performance, functional and maintenance&amp;nbsp;costs.&amp;nbsp; As an over simplified summary, on the side of mini cubes is the minimisation of FE performance costs, while on side of super cubes are increased user functionality.&lt;br /&gt;&lt;br /&gt;For another discussion on this topic see Chris Webb's &lt;a href="http://cwebbbi.wordpress.com/2010/09/02/one-cube-vs-multiple-cubes/" target="_blank"&gt;blog&lt;/a&gt;&lt;br /&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3689647052791307812-8839149357563266213?l=richardlees.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://richardlees.blogspot.com/feeds/8839149357563266213/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3689647052791307812&amp;postID=8839149357563266213' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3689647052791307812/posts/default/8839149357563266213'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3689647052791307812/posts/default/8839149357563266213'/><link rel='alternate' type='text/html' href='http://richardlees.blogspot.com/2011/05/how-many-cubes.html' title='How many cubes'/><author><name>Richard Lees</name><uri>http://www.blogger.com/profile/05671716466559973540</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='31' height='32' src='http://2.bp.blogspot.com/-AdeWScnQel8/Thj9ePsjpQI/AAAAAAAAAew/XnIn3xt4a0s/s220/RichardUni.png'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3689647052791307812.post-3602898599118214768</id><published>2011-05-04T20:18:00.003+10:00</published><updated>2011-05-07T12:20:08.124+10:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='MDX'/><title type='text'>MDX for Top n by group</title><content type='html'>&lt;a href="http://4.bp.blogspot.com/-GgVgig0eBto/TcSsMvf3SeI/AAAAAAAAAeM/ZZsrXvjBpTk/s1600/TopnGrouping.jpg"&gt;&lt;img style="MARGIN: 0px 0px 10px 10px; WIDTH: 254px; FLOAT: right; HEIGHT: 400px; CURSOR: hand" id="BLOGGER_PHOTO_ID_5603793171264260578" border="0" alt="" src="http://4.bp.blogspot.com/-GgVgig0eBto/TcSsMvf3SeI/AAAAAAAAAeM/ZZsrXvjBpTk/s400/TopnGrouping.jpg" /&gt;&lt;/a&gt;Here is a little MDX that will get the top n list for each of another set of members.&lt;br /&gt;&lt;br /&gt;For example, for Each region, I might want to get a list of the top images in that region.&lt;br /&gt;&lt;br /&gt;The query is very simple, just a matter of employing the generate function.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family:arial;color:#3333ff;"&gt;select [Measures].[Hits] on 0,&lt;br /&gt;non empty&lt;br /&gt;generate([Client Host].[Client Geography].[Region],&lt;br /&gt;([Client Host].[Client Geography],&lt;br /&gt;topcount(order([Target].[Resource Hierarchy].[Resource Type].&amp;amp;[.jpg].children,[Measures].[Hits],bdesc),10)))&lt;br /&gt;on 1&lt;br /&gt;from EasternMining&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;You can plug in the grouping, ordering that you want. Even nested top n lists.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3689647052791307812-3602898599118214768?l=richardlees.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://richardlees.blogspot.com/feeds/3602898599118214768/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3689647052791307812&amp;postID=3602898599118214768' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3689647052791307812/posts/default/3602898599118214768'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3689647052791307812/posts/default/3602898599118214768'/><link rel='alternate' type='text/html' href='http://richardlees.blogspot.com/2011/05/mdx-for-top-n-by-group.html' title='MDX for Top n by group'/><author><name>Richard Lees</name><uri>http://www.blogger.com/profile/05671716466559973540</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='31' height='32' src='http://2.bp.blogspot.com/-AdeWScnQel8/Thj9ePsjpQI/AAAAAAAAAew/XnIn3xt4a0s/s220/RichardUni.png'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://4.bp.blogspot.com/-GgVgig0eBto/TcSsMvf3SeI/AAAAAAAAAeM/ZZsrXvjBpTk/s72-c/TopnGrouping.jpg' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3689647052791307812.post-5974175460189809086</id><published>2011-03-13T08:16:00.010+11:00</published><updated>2011-03-13T09:28:28.272+11:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='SSIS Packages'/><category scheme='http://www.blogger.com/atom/ns#' term='SQL Performance'/><title type='text'>(re) Clustering a Large Online Table</title><content type='html'>&lt;a href="http://1.bp.blogspot.com/-1MGPZdodlHo/TXvsAJF7OEI/AAAAAAAAAcc/GxFwEWMoKMQ/s1600/DebugRun.JPG"&gt;&lt;img style="MARGIN: 0px 0px 10px 10px; WIDTH: 206px; FLOAT: right; HEIGHT: 400px; CURSOR: hand" id="BLOGGER_PHOTO_ID_5583315650240788546" border="0" alt="" src="http://1.bp.blogspot.com/-1MGPZdodlHo/TXvsAJF7OEI/AAAAAAAAAcc/GxFwEWMoKMQ/s400/DebugRun.JPG" /&gt;&lt;/a&gt;One of the most important performance controls you have on a relational database is the clustering sequence of your large tables. Unfortunately, it is often not considered until there is a performance issue. Clustered indexes are important on their own, but they are especially significant when you are joining multiple large tables. There are often orders of magnitude performance gains by clustering the large tables in the same sequence. However if you have a large table that needs to be online 24 hours a day, seven days a week, it can be difficult to make such a big change.&lt;br /&gt;&lt;div&gt;&lt;div&gt;&lt;br /&gt;&lt;div&gt;I have achieved this a few times now, on tables that are only inserted into (ie transaction tables). The only requirements are that the table is inserted into only (no updates) and that the table has a column that is inserted into in ascending sequence only (eg transactionid, datetime etc). Essentially, this is done by creating a new table, which has all the live table's records inserted into it using bulk load (SSIS package task). This makes it very efficient, but it will take a long time on a large table, so the new table won't be up to date. The trick is to keep low and high water marks, then you can open a transaction to copy in the last few records and rename the tables within the same transaction. In this way, at the point of committing, both tables will have the same content, and any active transactions will, at worst, wait a couple of seconds for the last insert and renaming to occur.&lt;/div&gt;&lt;br /&gt;&lt;div&gt;Here are the steps involved in the SSIS package &lt;/div&gt;&lt;ol&gt;&lt;li&gt;Create the new table with the clustered (and non clustered) indexes you want. &lt;/li&gt;&lt;li&gt;Create a control table to maintain high-low watermarks. Set the high water mark to the maximum ascending key value in the live table.&lt;a href="http://4.bp.blogspot.com/-yqODfIIEarQ/TXvvSgNACAI/AAAAAAAAAc0/4zSGJrBNyr0/s1600/LoadTask.JPG"&gt;&lt;img style="MARGIN: 0px 0px 10px 10px; WIDTH: 272px; FLOAT: right; HEIGHT: 400px; CURSOR: hand" id="BLOGGER_PHOTO_ID_5583319264217008130" border="0" alt="" src="http://4.bp.blogspot.com/-yqODfIIEarQ/TXvvSgNACAI/AAAAAAAAAc0/4zSGJrBNyr0/s400/LoadTask.JPG" /&gt;&lt;/a&gt; &lt;/li&gt;&lt;li&gt;Perform a bulk load (SSIS Data Flow task) for all the live table rows up to and including the high water mark. This task might take several hours.&lt;/li&gt;&lt;li&gt;Set the low water mark to the high water mark, then re set the high water mark to the new maximum ascending key value in the live table.&lt;/li&gt;&lt;li&gt;Perform the bulk load again. This time it will just be the rows that have been inserted during our earlier bulk load. After this task, the new table will be very close, but not quite, up to date to the live table.&lt;/li&gt;&lt;li&gt;Within the scope of a transaction lock the live table, insert records above the high water mark, rename the tables (and PKs) and commit the transaction. The new table is now live.&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;A few notes on the process&lt;/p&gt;&lt;ul&gt;&lt;li&gt;You will need to have sufficient space in your database to accommodate two copies of the table.&lt;/li&gt;&lt;li&gt;You will need to have sufficient space in your database logs to hold log records from the start of the Main Bulk Load to the end of the Main Bulk Load. So, if the bulk load takes 4 hours, their will be a transaction open for 4 hours and a log truncation won't truncate anything within the 4 hours.&lt;/li&gt;&lt;li&gt;You will need to have sufficient space in tempdb to perform the sorts for the clustered index and nonclustered indexes.&lt;a href="http://1.bp.blogspot.com/-_IRE0jiwIwk/TXvsAZB-ROI/AAAAAAAAAck/9yS7Fk-ZPKs/s1600/DataPump.JPG"&gt;&lt;img style="MARGIN: 0px 0px 10px 10px; WIDTH: 400px; FLOAT: right; HEIGHT: 391px; CURSOR: hand" id="BLOGGER_PHOTO_ID_5583315654519178466" border="0" alt="" src="http://1.bp.blogspot.com/-_IRE0jiwIwk/TXvsAZB-ROI/AAAAAAAAAck/9yS7Fk-ZPKs/s400/DataPump.JPG" /&gt;&lt;/a&gt; &lt;/li&gt;&lt;li&gt;If you have sufficient space in the database files and logs, you should set the maximum commit size on the destination to 0 (zero). Having other values will tend to slow down the load process (although less disk space is required) since the indexes will be maintained at each commit.&lt;/li&gt;&lt;li&gt;It is quite likely that your large tables are partitioned and compressed. This doesn't need to make any difference to the reclustering process above. However, if the table is already partitioned, you could improve performance by looping through each partition. If your table isn't already partitioned, this process could be the opportunity to partition your table.&lt;/li&gt;&lt;li&gt;Don't be tempted to use NOLOCK when reading the live table.  &lt;/li&gt;&lt;li&gt;Test your package before you run it in production.&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;There is no special code in the package I have created. The most significant task is the very last one, which is performed within the scope of a transaction. To ensure there is no opportunity of a deadlock, the task first asks for an X lock on the live table. Here is some sample SQL from each of the tasks.&lt;/p&gt;&lt;p&gt;Create Control table&lt;/p&gt;&lt;p&gt;&lt;span style="font-size:85%;color:#330099;"&gt;create table ReclusteringControl( ReclusteringControlID int default 1, LowWater bigint not null default 0, HighWater bigint not null default 0, CONSTRAINT [PK_ReclusteringControl] PRIMARY KEY NONCLUSTERED (ReclusteringControlID ASC))&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="font-size:85%;color:#330099;"&gt;Insert into ReclusteringControl values(1,0,0)&lt;/span&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="font-size:85%;color:#330099;"&gt;update ReclusteringControl set HighWater=(select max(rid) from MyLargeTable) &lt;/span&gt;&lt;/p&gt;&lt;p&gt;Input for Main Bulk Load&lt;/p&gt;&lt;p&gt;&lt;span style="font-size:85%;color:#330099;"&gt;select * from MyLargeTablewhere rid&lt;=(select HighWater from ReclusteringControl)&lt;/span&gt;&lt;/p&gt;&lt;p&gt;Set New HighWatermark&lt;/p&gt;&lt;p&gt;&lt;span style="font-size:85%;color:#330099;"&gt;update ReclusteringControl set LowWater=HighWater&lt;br /&gt;update ReclusteringControl set HighWater=(select max(rid) from MyLargeTable)&lt;/span&gt;&lt;/p&gt;&lt;p&gt;Second Bulk Load (input statement)&lt;/p&gt;&lt;p&gt;&lt;span style="font-size:85%;color:#330099;"&gt;select * from MyLargeTablewhere rid&lt;=(select HighWater from ReclusteringControl) and rid&gt;(select LowWater from ReclusteringControl)&lt;/span&gt;&lt;/p&gt;&lt;p&gt;Final INSERT and Rename&lt;/p&gt;&lt;p&gt;&lt;span style="font-size:85%;color:#330099;"&gt;--set identity_insert MyLargeTable_new on&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="font-size:85%;color:#330099;"&gt;--This is required if there is an identity column on the table&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="font-size:85%;color:#330099;"&gt;GO&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="font-size:85%;color:#330099;"&gt;begin transaction &lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="font-size:85%;color:#330099;"&gt;update ReclusteringControl &lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="font-size:85%;color:#330099;"&gt;set LowWater=HighWater &lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="font-size:85%;color:#330099;"&gt;select top 0 * from MyLargeTable with (tablockx) &lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="font-size:85%;color:#330099;"&gt;update ReclusteringControl set HighWater=(select max(rid) from MyLargeTable) &lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="font-size:85%;color:#330099;"&gt;insert into MyLargeTable_new --columns need to be explicitly declared if identity cols are inserted &lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="font-size:85%;color:#330099;"&gt;select * from MyLargeTable where rid&lt;=(select HighWater from ReclusteringControl) and rid&gt;(select LowWater from ReclusteringControl);&lt;br /&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="font-size:85%;color:#330099;"&gt;exec sp_rename MyLargeTable,XXXX_MyLargeTable;--should delete this table &lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="font-size:85%;color:#330099;"&gt;exec sp_rename MyLargeTable_new,MyLargeTable; &lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="font-size:85%;color:#330099;"&gt;exec sp_rename PK_MyLargeTable, PK_MyLargeTable_XXXX &lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="font-size:85%;color:#330099;"&gt;exec sp_rename PK_MyLargeTable_new, PK_MyLargeTable&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="font-size:85%;color:#330099;"&gt;commit transaction&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="font-size:85%;color:#330099;"&gt;--set identity_insert MyLargeTable off&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3689647052791307812-5974175460189809086?l=richardlees.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://richardlees.blogspot.com/feeds/5974175460189809086/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3689647052791307812&amp;postID=5974175460189809086' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3689647052791307812/posts/default/5974175460189809086'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3689647052791307812/posts/default/5974175460189809086'/><link rel='alternate' type='text/html' href='http://richardlees.blogspot.com/2011/03/re-clustering-large-online-table.html' title='(re) Clustering a Large Online Table'/><author><name>Richard Lees</name><uri>http://www.blogger.com/profile/05671716466559973540</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='31' height='32' src='http://2.bp.blogspot.com/-AdeWScnQel8/Thj9ePsjpQI/AAAAAAAAAew/XnIn3xt4a0s/s220/RichardUni.png'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://1.bp.blogspot.com/-1MGPZdodlHo/TXvsAJF7OEI/AAAAAAAAAcc/GxFwEWMoKMQ/s72-c/DebugRun.JPG' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3689647052791307812.post-1326084904988600718</id><published>2011-02-14T20:05:00.005+11:00</published><updated>2011-02-14T20:26:12.799+11:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='MDX'/><title type='text'>Anything is possible with MDX</title><content type='html'>I was with one of my favourite customers last week and had an impromptu challenge put before me. We had a cube containing the company's sales data and the challenge was to reproduce a report they had created in their legacy system. (I found out later that this report had taken a couple of months to produce.) The requirement was to create a report that would display the last 7 days of a particular outlet and for each day show the sales and transactions for the top hour of that day.&lt;br /&gt;&lt;div&gt;I have often been quoted as saying that if the data is in the cube, one MDX query can answer any question that you might have. So the pressure was on to create the report. Fortunately, they were using ReportPortal software, which is a highly functional thin client tool, and it has good charting capability. The web browser tool allows you to create your own sets and calculated members. So all I really needed to do was create a set for the last 7 days. I will reproduce a similar query using my weblog database, which has comparable dimensions.&lt;/div&gt;&lt;br /&gt;&lt;div&gt;Here is a set declaration for the last 7 business days.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;span style="font-family:courier new;font-size:78%;color:#3333ff;"&gt;&lt;strong&gt;tail(nonempty([Date].[Year Month Day].[Day],[Measures].[Bytes Total]),7)&lt;/strong&gt;&lt;/span&gt;&lt;/div&gt;&lt;br /&gt;&lt;div&gt;The calculated members for the top hour etc are simply a matter of taking the top hour and getting the attribute/tuple require. Here is the full query on my weblog database. Note, the good thing about a tool like ReportPortal is that you can declare sets and calculated members and it will still honour filters and hierarchies that you have dragged and dropped on rows/columns. So the only MDX you need to write is for the set and calculated member, the rest of the report is created by dragging and dropping from the palette.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;span style="font-size:78%;color:#660000;"&gt;&lt;span style="color:#330033;"&gt;with&lt;br /&gt;&lt;/span&gt;&lt;span style="color:#330033;"&gt;set Last7Days as&lt;/span&gt;&lt;span style="color:#3333ff;"&gt; tail(nonempty([Date].[Year Month Day].[Day],[Measures].[Bytes Total]),7)&lt;br /&gt;&lt;span style="color:#330033;"&gt;member Measures.TopHourName as&lt;/span&gt; order([HoursOfDay].[Hour Of Day],[Measures].[Hits],BDESC).item(0).member_caption&lt;br /&gt;&lt;span style="color:#330033;"&gt;member Measures.TopHourSessions as&lt;/span&gt; (order([HoursOfDay].[Hour Of Day],[Measures].[Hits],BDESC).item(0),[Measures].[Sessions])&lt;br /&gt;&lt;span style="color:#330033;"&gt;member Measures.TopHourHits as&lt;/span&gt; (order([HoursOfDay].[Hour Of Day],[Measures].[Hits],BDESC).item(0),[Measures].[Hits])&lt;br /&gt;&lt;/span&gt;&lt;span style="color:#330033;"&gt;select&lt;br /&gt;{Measures.TopHourName,Measures.TopHourSessions,Measures.TopHourHits} on columns,&lt;br /&gt;[Date].[Month].[Month]*Last7Days on rows&lt;br /&gt;from EasternMining&lt;br /&gt;where ([Client Host].[Client Geography].[Region].&amp;amp;[Americas].[UNITED STATES])&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;br /&gt;&lt;div&gt;Like all good cube browers, ReportPortal will graph your res&lt;a href="http://3.bp.blogspot.com/-8fa1Rtxdth0/TVj09Wm34BI/AAAAAAAAAcU/o5e4TIS17Ho/s1600/topHourLast7Days.JPG"&gt;&lt;img style="MARGIN: 0px 0px 10px 10px; WIDTH: 351px; FLOAT: right; HEIGHT: 392px; CURSOR: hand" id="BLOGGER_PHOTO_ID_5573473873748811794" border="0" alt="" src="http://3.bp.blogspot.com/-8fa1Rtxdth0/TVj09Wm34BI/AAAAAAAAAcU/o5e4TIS17Ho/s400/topHourLast7Days.JPG" /&gt;&lt;/a&gt;ults. In this case, the most appropriate chart was a hybrid column/line chart, with sales as columns on the left hand y axis and transactions as a line on the right hand y axis.&lt;/div&gt;&lt;br /&gt;&lt;div&gt;The takeaway from this is that, so long as the cube has the necessary data, a single MDX query can get the answer to any question.&lt;/div&gt;&lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3689647052791307812-1326084904988600718?l=richardlees.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://richardlees.blogspot.com/feeds/1326084904988600718/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3689647052791307812&amp;postID=1326084904988600718' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3689647052791307812/posts/default/1326084904988600718'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3689647052791307812/posts/default/1326084904988600718'/><link rel='alternate' type='text/html' href='http://richardlees.blogspot.com/2011/02/anything-is-possible-with-mdx.html' title='Anything is possible with MDX'/><author><name>Richard Lees</name><uri>http://www.blogger.com/profile/05671716466559973540</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='31' height='32' src='http://2.bp.blogspot.com/-AdeWScnQel8/Thj9ePsjpQI/AAAAAAAAAew/XnIn3xt4a0s/s220/RichardUni.png'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://3.bp.blogspot.com/-8fa1Rtxdth0/TVj09Wm34BI/AAAAAAAAAcU/o5e4TIS17Ho/s72-c/topHourLast7Days.JPG' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3689647052791307812.post-1394362918484280142</id><published>2011-01-16T09:51:00.008+11:00</published><updated>2011-01-17T06:58:01.720+11:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Powerpivot'/><title type='text'>PowerPivot versus Analysis Services</title><content type='html'>&lt;a href="http://2.bp.blogspot.com/_SHRRgijB18E/TTIqblGPpLI/AAAAAAAAAcI/NOMHld2y-24/s1600/PowerPivotvsSSAS.JPG"&gt;&lt;img style="MARGIN: 0px 0px 10px 10px; WIDTH: 400px; FLOAT: right; HEIGHT: 205px; CURSOR: hand" id="BLOGGER_PHOTO_ID_5562555143059121330" border="0" alt="" src="http://2.bp.blogspot.com/_SHRRgijB18E/TTIqblGPpLI/AAAAAAAAAcI/NOMHld2y-24/s400/PowerPivotvsSSAS.JPG" /&gt;&lt;/a&gt; Chris Webb has done more to demystify Microsoft's PowerPivot story than any other person. Recently, he has put up a "quiz" to help people decide between PowerPivot and SSAS. The quiz is presented in a light hearted fashion, but it contains a lot of Chris' analysis on the strengths and weaknesses of each technology. You can see Chris' quiz on &lt;a href="http://cwebbbi.wordpress.com/2010/11/19/powerpivot-vs-ssas-quiz/"&gt;http://cwebbbi.wordpress.com/2010/11/19/powerpivot-vs-ssas-quiz/&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;If you are really interested in hearing Chris speak on this topic, there is a recording of him talking in Belgium on this topic here &lt;a href="http://sqlserverday.be/video/view/?id=2"&gt;http://sqlserverday.be/video/view/?id=2&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;I found the quiz very interesting. Not the quiz final recommendation (PP vs AS), but the quiz questions and how the answers were interpreted, which exposes Chris' analysis. Here is a copy of Chris' quiz with the questions shaded in green and red interpreting Chris' analysis of the question answer here &lt;a href="https://spreadsheets0.google.com/ccc?key=tcyHIY2MtHCkj-wTJUAb3Tg&amp;amp;hl=en_GB"&gt;https://spreadsheets0.google.com/ccc?key=tcyHIY2MtHCkj-wTJUAb3Tg&amp;amp;hl=en_GB&lt;/a&gt;#&lt;br /&gt;&lt;p&gt;You can use this copy of the quiz to quickly get an understanding of Chris' analysis of the strengths and weaknesses of PowerPivot.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3689647052791307812-1394362918484280142?l=richardlees.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://richardlees.blogspot.com/feeds/1394362918484280142/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3689647052791307812&amp;postID=1394362918484280142' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3689647052791307812/posts/default/1394362918484280142'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3689647052791307812/posts/default/1394362918484280142'/><link rel='alternate' type='text/html' href='http://richardlees.blogspot.com/2011/01/powerpivot-versus-analysis-services.html' title='PowerPivot versus Analysis Services'/><author><name>Richard Lees</name><uri>http://www.blogger.com/profile/05671716466559973540</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='31' height='32' src='http://2.bp.blogspot.com/-AdeWScnQel8/Thj9ePsjpQI/AAAAAAAAAew/XnIn3xt4a0s/s220/RichardUni.png'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://2.bp.blogspot.com/_SHRRgijB18E/TTIqblGPpLI/AAAAAAAAAcI/NOMHld2y-24/s72-c/PowerPivotvsSSAS.JPG' height='72' width='72'/><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3689647052791307812.post-5797634642067250004</id><published>2010-12-12T08:26:00.010+11:00</published><updated>2010-12-12T09:07:31.333+11:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='MDX'/><category scheme='http://www.blogger.com/atom/ns#' term='OLAP Performance'/><title type='text'>Warming the OLAP cache</title><content type='html'>Analysis Services has a data (and aggregation cache) not dissimilar to a relational database cache. For really good cube performance it is desirable to have most of your cube resident in the cache. Querying the cube will help bring data into the cache, while cube processing will tend to clear the cache. More accurately, partition processing (incremental or full) will clear that entire partition from the cache. &lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt; &lt;/div&gt;&lt;div&gt;Analysis Services, by default, will freely use 65% of a computer's memory, so data should not be pushed out of the cache unless AS is using 65% of memory.  With commodity 64 bit servers, the memory available to Analysis Services should be, at least, in the tens of GBytes.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Here is an example of a query that has been run on a cold cache. Notice how the first Query Subcube has an EventClass of "non-cache data". This means that the storage engine is going to disk since the data/aggregations are not in cache.&lt;/div&gt;&lt;img style="TEXT-ALIGN: center; MARGIN: 0px auto 10px; WIDTH: 400px; DISPLAY: block; HEIGHT: 122px; CURSOR: hand" id="BLOGGER_PHOTO_ID_5549542758329278050" border="0" alt="" src="http://4.bp.blogspot.com/_SHRRgijB18E/TQPvvGgO3mI/AAAAAAAAAb0/GRV2AW51f3M/s400/ProfilerNoCache.JPG" /&gt; You can help warm the cache by executing MDX queries, or you can execute the CREATE CACHE statement. The CREATE CACHE statement looks similar to a regular MDX query, the main difference being a resultset is not returned. For example&lt;br /&gt;&lt;div&gt;&lt;br /&gt;&lt;span style="font-size:78%;"&gt;&lt;span style="font-family:courier new;color:#000099;"&gt;CREATE CACHE FOR [EasternMining] AS&lt;br /&gt;[Port].[Ports].&amp;amp;[80]&lt;br /&gt;*{[Measures].Members}&lt;br /&gt;*{[Date].[Year Month Day].[Year].&amp;amp;[2010].[January].[1]&lt;br /&gt;:[Date].[Year Month Day].[Year].&amp;amp;[2010].[December].[3]}&lt;br /&gt;go&lt;br /&gt;CREATE CACHE FOR [EasternMining] AS&lt;br /&gt;[Port].[Ports].&amp;amp;[80]&lt;br /&gt;*{[Measures].Members }&lt;br /&gt;*[HoursOfDay].[Hour Of Day]&lt;br /&gt;*{[Date].[Year Month Day].[Year].&amp;amp;[2010].[November].[1]&lt;br /&gt;&lt;span style="color:#000099;"&gt;:[Date].[Year Month Day].[Year].&amp;amp;[2010].[December].[3]}&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="color:#000099;"&gt;go&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;Now let's run the query again and check the Profiler trace. Here is the output&lt;/div&gt;&lt;img style="TEXT-ALIGN: center; MARGIN: 0px auto 10px; WIDTH: 400px; DISPLAY: block; HEIGHT: 110px; CURSOR: hand" id="BLOGGER_PHOTO_ID_5549544392631680018" border="0" alt="" src="http://4.bp.blogspot.com/_SHRRgijB18E/TQPxOOwSfBI/AAAAAAAAAb8/GXIaPALytz0/s400/ProfilerWithCache.JPG" /&gt;Notice now, all the Query subcubes have an EventClass of "Cache data".  This means that AS has found the data for this query in the cache.  Also note, the duration for the first subcube is 0ms.  This is a great improvement over the original cold cache subcube of 47ms.&lt;br /&gt;&lt;div&gt; &lt;/div&gt;&lt;div&gt;Generally, it is a good practice to warm the cache after cube processing.  This will help the performance of the first few queries that hit the cube after processing.&lt;/div&gt;&lt;div&gt; &lt;/div&gt;&lt;div&gt;Also, as a cube designer, you should be aware of the cache warming effect and design partitions so that you isolate the volatile data as much as possible, so that you minimise cache clearing when processing.&lt;/div&gt;&lt;div&gt; &lt;/div&gt;&lt;/div&gt;&lt;div&gt;If you want to experiment with cache warming, you will probably want the ability to clear the cache quickly.  Here is an example of a cache clearing statement for a cube&lt;/div&gt;&lt;div&gt;&lt;span style="font-family:courier new;font-size:78%;color:#000099;"&gt;&lt;clearcache xmlns="http://schemas.microsoft.com/analysisservices/2003/engine"&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="font-family:courier new;font-size:78%;color:#000099;"&gt;&lt;object&gt;&lt;br /&gt;&lt;databaseid&gt;IISLog&lt;/databaseid&gt;&lt;br /&gt;&lt;cubeid&gt;EasternMining&lt;/cubeid&gt;&lt;br /&gt;&lt;/object&gt;&lt;br /&gt;&lt;/clearcache&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span style="font-family:Courier New;font-size:78%;color:#000099;"&gt;&lt;/span&gt; &lt;/div&gt;&lt;div&gt;Here is the MDX query I used in the demonstration above&lt;/div&gt;&lt;div&gt;&lt;span style="font-size:78%;color:#000099;"&gt;WITH&lt;br /&gt;member measures.MyCalc as&lt;br /&gt;avg(tail(nonempty([Date].[Year Month Day].[Year].&amp;amp;[2010].[November].[1]&lt;br /&gt;:[Date].[Year Month Day].[Year].&amp;amp;[2010].[December].[3].lag(1)&lt;br /&gt;,[Measures].[Bytes Total]),6)&lt;br /&gt;,[Measures].[Bytes Total])&lt;br /&gt;SELECT {measures.MyCalc} ON COLUMNS ,&lt;br /&gt;{[HoursOfDay].[All HoursOfDay].[00:00-01:59].[00:00-00:59],[HoursOfDay].[All HoursOfDay].[00:00-01:59].[01:00-01:59],&lt;br /&gt;[HoursOfDay].[All HoursOfDay].[02:00-03:59].[02:00-02:59],[HoursOfDay].[All HoursOfDay].[02:00-03:59].[03:00-03:59],&lt;br /&gt;[HoursOfDay].[All HoursOfDay].[04:00-05:59].[04:00-04:59],[HoursOfDay].[All HoursOfDay].[04:00-05:59].[05:00-05:59],&lt;br /&gt;[HoursOfDay].[All HoursOfDay].[06:00-07:59].[06:00-06:59] ,[HoursOfDay].[All HoursOfDay].[06:00-07:59].[07:00-07:59],&lt;br /&gt;[HoursOfDay].[All HoursOfDay].[08:00-09:59].[08:00-08:59],[HoursOfDay].[All HoursOfDay].[08:00-09:59].[09:00-09:59] }&lt;br /&gt;ON ROWS&lt;br /&gt;FROM EasternMining&lt;br /&gt;WHERE ([Port].[Ports].&amp;amp;[80] )&lt;/span&gt;&lt;/div&gt;&lt;div&gt; &lt;/div&gt;&lt;div&gt;&lt;br /&gt; &lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3689647052791307812-5797634642067250004?l=richardlees.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://richardlees.blogspot.com/feeds/5797634642067250004/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3689647052791307812&amp;postID=5797634642067250004' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3689647052791307812/posts/default/5797634642067250004'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3689647052791307812/posts/default/5797634642067250004'/><link rel='alternate' type='text/html' href='http://richardlees.blogspot.com/2010/12/warming-olap-cache.html' title='Warming the OLAP cache'/><author><name>Richard Lees</name><uri>http://www.blogger.com/profile/05671716466559973540</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='31' height='32' src='http://2.bp.blogspot.com/-AdeWScnQel8/Thj9ePsjpQI/AAAAAAAAAew/XnIn3xt4a0s/s220/RichardUni.png'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://4.bp.blogspot.com/_SHRRgijB18E/TQPvvGgO3mI/AAAAAAAAAb0/GRV2AW51f3M/s72-c/ProfilerNoCache.JPG' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3689647052791307812.post-2908006470536521687</id><published>2010-10-16T18:45:00.007+11:00</published><updated>2010-10-16T19:14:32.526+11:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Online Demonstrations'/><category scheme='http://www.blogger.com/atom/ns#' term='olap cubes'/><title type='text'>What's happening on my site?</title><content type='html'>&lt;a href="http://2.bp.blogspot.com/_SHRRgijB18E/TLldNAp-R5I/AAAAAAAAAbk/8zwPv0ioW4A/s1600/Dashboard.JPG"&gt;&lt;img style="MARGIN: 0px 0px 10px 10px; WIDTH: 400px; FLOAT: right; HEIGHT: 294px; CURSOR: hand" id="BLOGGER_PHOTO_ID_5528552495669725074" border="0" alt="" src="http://2.bp.blogspot.com/_SHRRgijB18E/TLldNAp-R5I/AAAAAAAAAbk/8zwPv0ioW4A/s400/Dashboard.JPG" /&gt;&lt;/a&gt; My internet site has a series of close to real-time dashboards showing, among other things, internet activity on my site. It is primarily meant as a demonstration tool, but it has prooved useful to me in quickly seeing what's happing on my site (and my servers, since there is also a Perfmon dashboard).&lt;br /&gt;&lt;br /&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;Interesting, over the last couple of weeks, I have seen some suspicious looking referrers from Russia. You can probably still see them on the default dashboard on &lt;a href="http://richardlees.com.au/Sites/Demonstrations"&gt;http://RichardLees.com.au/Sites/Demonstrations&lt;/a&gt;. See the Top Referrers table on the bottom. At the moment the top referrer includes "buy ultram.narod.ru" at the top, and there are a few more suspicious ones lower down. I am used to seeing Google, Bing or my blogspot compete for the top spot. &lt;/div&gt;&lt;br /&gt;&lt;div&gt;&lt;/div&gt;It's great with Microsoft PerformancePoint dashboards that you can dynamically drill down the table as an ad hoc cube browsing tool. I have drilled down to see the pages from the referrer, the http command &lt;a href="http://4.bp.blogspot.com/_SHRRgijB18E/TLldM0iZikI/AAAAAAAAAbc/F6BCvB4mOc8/s1600/TopReferrers.JPG"&gt;&lt;img style="MARGIN: 0px 0px 10px 10px; WIDTH: 380px; FLOAT: right; HEIGHT: 400px; CURSOR: hand" id="BLOGGER_PHOTO_ID_5528552492416731714" border="0" alt="" src="http://4.bp.blogspot.com/_SHRRgijB18E/TLldM0iZikI/AAAAAAAAAbc/F6BCvB4mOc8/s400/TopReferrers.JPG" /&gt;&lt;/a&gt;(HEAD), the ip address, http return code etc. See picture. You can do this too, simply right click on the chart and take the direction you want. A tip; click the triangle at the top right of the chart and open in a new window to see the chart/grid on a new page.&lt;br /&gt;&lt;br /&gt;&lt;div&gt;Although this activity is suspicious, and might represent a potentially malicious attack, it doesn't appear to have had much success. Or perhaps all it wanted to do was populate my logs.&lt;/div&gt;&lt;br /&gt;&lt;div&gt;In any case, I feel that the dashboard has shown the value of processing logs and providing an easy and intuitive dashboard to this information. Everyone who has an internet site should have access to information on the site's activity. Also, many of the off-the-shelf web analytic tools only show you a subset of what is really happening. For example, I have Google Analytics, which are quite useful, although they only show me a subset of the activity, which doesn't include these referrers.&lt;a href="http://1.bp.blogspot.com/_SHRRgijB18E/TLldMvrjKpI/AAAAAAAAAbU/kCY8g8NO684/s1600/ReferrerDetails.JPG"&gt;&lt;img style="MARGIN: 0px 0px 10px 10px; WIDTH: 400px; FLOAT: right; HEIGHT: 57px; CURSOR: hand" id="BLOGGER_PHOTO_ID_5528552491112934034" border="0" alt="" src="http://1.bp.blogspot.com/_SHRRgijB18E/TLldMvrjKpI/AAAAAAAAAbU/kCY8g8NO684/s400/ReferrerDetails.JPG" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;&lt;div&gt;Wouldn't it be nice if Microsoft Windows included an install option that went something like&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;&lt;div&gt;Would you like incoming http activity processed and kept in an OLAP cube for ad hoc browsing?&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;&lt;div&gt;For more examples of online, close to real-time OLAP cubes and data mining models see &lt;a href="http://richardlees.com.au/sites/demonstrations"&gt;http://RichardLees.com.au/sites/demonstrations&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3689647052791307812-2908006470536521687?l=richardlees.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://richardlees.blogspot.com/feeds/2908006470536521687/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3689647052791307812&amp;postID=2908006470536521687' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3689647052791307812/posts/default/2908006470536521687'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3689647052791307812/posts/default/2908006470536521687'/><link rel='alternate' type='text/html' href='http://richardlees.blogspot.com/2010/10/whats-happening-on-my-site.html' title='What&apos;s happening on my site?'/><author><name>Richard Lees</name><uri>http://www.blogger.com/profile/05671716466559973540</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='31' height='32' src='http://2.bp.blogspot.com/-AdeWScnQel8/Thj9ePsjpQI/AAAAAAAAAew/XnIn3xt4a0s/s220/RichardUni.png'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://2.bp.blogspot.com/_SHRRgijB18E/TLldNAp-R5I/AAAAAAAAAbk/8zwPv0ioW4A/s72-c/Dashboard.JPG' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3689647052791307812.post-2848676240258198521</id><published>2010-10-10T08:41:00.011+11:00</published><updated>2010-10-10T09:36:26.854+11:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='MDX'/><category scheme='http://www.blogger.com/atom/ns#' term='Security'/><category scheme='http://www.blogger.com/atom/ns#' term='Analysis Services'/><title type='text'>SSAS Dynamic Security</title><content type='html'>Imagine the scenario where there are hundreds or thousands of cube use&lt;a href="http://3.bp.blogspot.com/_SHRRgijB18E/TLDqZj9eE6I/AAAAAAAAAbM/vaMZaYK2qSY/s1600/DySecurity.JPG"&gt;&lt;img style="MARGIN: 0px 0px 10px 10px; WIDTH: 339px; FLOAT: right; HEIGHT: 400px; CURSOR: hand" id="BLOGGER_PHOTO_ID_5526174467654292386" border="0" alt="" src="http://3.bp.blogspot.com/_SHRRgijB18E/TLDqZj9eE6I/AAAAAAAAAbM/vaMZaYK2qSY/s400/DySecurity.JPG" /&gt;&lt;/a&gt;rs, most of which are only permitted to see "their" data.   And there is a dimension with an attribute containing their Windows userid. It would be very tedious to set up a security role for every individual. Not to mention the maintenance in adding and deleting roles. Although this could be somewhat automated with AMO scripting.&lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;&lt;div&gt;A technique I like to employ is to set up just one SSAS role, which essentially contains everyone that might have some access to the cube (integrated security only).  So, in the Dimension Data security tab, I restrict the "Allowed member set" to just those members where the member name is equal to the username(). Username() is an mdx function that returns the Windows userid.&lt;/div&gt;&lt;br /&gt;&lt;div&gt;The Allowed member set might look something like &lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;&lt;div&gt;&lt;span style="font-family:arial;color:#6600cc;"&gt;{StrToMember("Branch.BranchManager.&amp;amp;[" + UserName() + "]")}&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;&lt;div&gt;The default member could be&lt;/div&gt;&lt;br /&gt;&lt;div&gt;&lt;span style="font-family:arial;color:#6600cc;"&gt;StrToMember("Branch.BranchManager.&amp;amp;[" + UserName() + "]")&lt;/span&gt;&lt;/div&gt;&lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;You can add other roles, for example, one with read access to the entire cube. So the users that can only see their Branch, would be in the first role (they would only be able to see their own branch) and head office users in the second role would be able to see the entire cube.&lt;/div&gt;&lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;This is a very elegant solution. As you can see, it requires no maintenance, as new/old users will automatically be able to see their own data. If the branch manager changes, the old user will have no read access and the new user will have read access to that branch.&lt;/div&gt;&lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;There are many ways to extend this security. I have combined it with application tables that list users' access. This can be a many-to-one or many-to many relationship. No problem, just create a dimension (possibly many-to-many) , and grant read access to the dimension in the same way. Also, it might be that you only want part of the username, or that you want/don't want the domain. Again, that is no problem, you can use VBA functions in the MDX expression to get the substring that you want. You could also use this technique to manage a black list (Denied member set) instead of a white list (Allowed member set).&lt;/div&gt;&lt;div&gt; &lt;/div&gt;&lt;div&gt;A classic use of this technique would be to support sales persons to see information relating to their customers.  This technique might be combined with other restrictions.  For example the dynamic security role might let browsers see a subset of their data, such as quantities, but not dollar values.  And only for their customers.&lt;/div&gt;&lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;Note, there is an overhead in Dimension Data security, but in my experience it is almost undetectable. Of more concern, if you needed to use it, would be the many-to-many dimension. Be wary of performance if your m2m dimension and/or m2m fact table is large. &lt;/div&gt;&lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;Also note, you probably want to check the "Enable Visual Totals" check box, so that users can only see the total for members they are allowed to see.&lt;/div&gt;&lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;The use of this dynamic security technique can be used in conjunction with conventional roles in the same cube.&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3689647052791307812-2848676240258198521?l=richardlees.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://richardlees.blogspot.com/feeds/2848676240258198521/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3689647052791307812&amp;postID=2848676240258198521' title='12 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3689647052791307812/posts/default/2848676240258198521'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3689647052791307812/posts/default/2848676240258198521'/><link rel='alternate' type='text/html' href='http://richardlees.blogspot.com/2010/10/ssas-dynamic-security.html' title='SSAS Dynamic Security'/><author><name>Richard Lees</name><uri>http://www.blogger.com/profile/05671716466559973540</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='31' height='32' src='http://2.bp.blogspot.com/-AdeWScnQel8/Thj9ePsjpQI/AAAAAAAAAew/XnIn3xt4a0s/s220/RichardUni.png'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://3.bp.blogspot.com/_SHRRgijB18E/TLDqZj9eE6I/AAAAAAAAAbM/vaMZaYK2qSY/s72-c/DySecurity.JPG' height='72' width='72'/><thr:total>12</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3689647052791307812.post-7196783523258568194</id><published>2010-08-18T06:42:00.013+10:00</published><updated>2010-08-19T06:22:11.286+10:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='olap cubes'/><category scheme='http://www.blogger.com/atom/ns#' term='OLAP Performance'/><category scheme='http://www.blogger.com/atom/ns#' term='Excel 2010'/><title type='text'>Write Back cubes are easy with Excel 2010</title><content type='html'>&lt;div&gt;&lt;a href="http://3.bp.blogspot.com/_SHRRgijB18E/TGr5KQS93ZI/AAAAAAAAAak/ID5So0pqxg4/s1600/EnableWriteback.JPG"&gt;&lt;img style="MARGIN: 0px 0px 10px 10px; WIDTH: 262px; FLOAT: right; HEIGHT: 400px; CURSOR: hand" id="BLOGGER_PHOTO_ID_5506487448982379922" border="0" alt="" src="http://3.bp.blogspot.com/_SHRRgijB18E/TGr5KQS93ZI/AAAAAAAAAak/ID5So0pqxg4/s400/EnableWriteback.JPG" /&gt;&lt;/a&gt;SQL Server has supported write enabled cubes for over 10 years, but things have just got a whole lot better with Excel 2010 (and SQL Server 2008). You can now browse and update a write enabled cube in Excel without any add-ins and without writing any code.&lt;br /&gt;&lt;br /&gt;&lt;div&gt;&lt;div&gt;It's quite easy. Just right click on the partition (in the &lt;strong&gt;Partitions&lt;/strong&gt; tab of the cube editor) and select &lt;strong&gt;Writeback settings&lt;/strong&gt;. You can just take the default settings. Interestingly, the storage mode does not need to be ROLAP, so we get good performance without operational complexity.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Now, simply open Excel 2010, connect to the cube, and enable "&lt;strong&gt;What-If Analysis&lt;/strong&gt;". This is a button in the &lt;strong&gt;Tools&lt;/strong&gt; section (next to &lt;strong&gt;PivotChart&lt;/strong&gt; and &lt;strong&gt;OLAP&lt;/strong&gt; &lt;strong&gt;Tools&lt;/strong&gt;). &lt;a href="http://3.bp.blogspot.com/_SHRRgijB18E/TGr5KpzgcyI/AAAAAAAAAas/GvMUWS6XyfQ/s1600/WritebackSettings.JPG"&gt;&lt;img style="MARGIN: 0px 0px 10px 10px; WIDTH: 370px; FLOAT: right; HEIGHT: 381px; CURSOR: hand" id="BLOGGER_PHOTO_ID_5506487455829750562" border="0" alt="" src="http://3.bp.blogspot.com/_SHRRgijB18E/TGr5KpzgcyI/AAAAAAAAAas/GvMUWS6XyfQ/s400/WritebackSettings.JPG" /&gt;&lt;/a&gt;Now you can write over any of the cells in the cube. The numbers you have changed will have a little purple triangle in the bottom right corner. When you want to write these back simply click the "&lt;strong&gt;Publish Changes&lt;/strong&gt;" button under the "&lt;strong&gt;What-If Analysis&lt;/strong&gt;" button.  Excel will "distribute" your updates according to the &lt;strong&gt;settings&lt;/strong&gt; you have asked for.&lt;/div&gt;&lt;div&gt;&lt;br /&gt; &lt;/div&gt;&lt;div&gt;Excel has a &lt;strong&gt;Settings&lt;/strong&gt; button under "&lt;strong&gt;What-If Analysis&lt;/strong&gt;", which allows you to control&lt;a href="http://2.bp.blogspot.com/_SHRRgijB18E/TGr5KxOO8gI/AAAAAAAAAa0/1Yi-31AWskw/s1600/EnableWriteback2.JPG"&gt;&lt;img style="MARGIN: 0px 0px 10px 10px; WIDTH: 400px; FLOAT: right; HEIGHT: 323px; CURSOR: hand" id="BLOGGER_PHOTO_ID_5506487457820897794" border="0" alt="" src="http://2.bp.blogspot.com/_SHRRgijB18E/TGr5KxOO8gI/AAAAAAAAAa0/1Yi-31AWskw/s400/EnableWriteback2.JPG" /&gt;&lt;/a&gt; how values are spread, when you enter at a higher level than the cube granularity. For example, you could enter a number for the whole of 2010 and it will spread the amount across all periods based on whether you want an even spread and whether you want the updates to be incremented based on the old numbers.&lt;/div&gt;&lt;div&gt; &lt;/div&gt;&lt;/div&gt;&lt;div&gt; &lt;/div&gt;&lt;div&gt;Excel does not natively support the insertion of new dimension members, which would have been a nice feature for some applications.&lt;/div&gt;&lt;div&gt; &lt;/div&gt;&lt;div&gt; &lt;/div&gt;&lt;div&gt;Operationally, I don't think it is so important to migrate the writeback data to the main partition, since the writeback partition is typically MOLAP.  However, it would not be difficult to move the data over in an SSIS package on a scheduled basis.&lt;/div&gt;&lt;div&gt;&lt;a href="http://2.bp.blogspot.com/_SHRRgijB18E/TGr6ZXw427I/AAAAAAAAAa8/DdQ0tiy-eiY/s1600/Writeback.JPG"&gt;&lt;img style="MARGIN: 0px 0px 10px 10px; WIDTH: 400px; FLOAT: right; HEIGHT: 175px; CURSOR: hand" id="BLOGGER_PHOTO_ID_5506488808196594610" border="0" alt="" src="http://2.bp.blogspot.com/_SHRRgijB18E/TGr6ZXw427I/AAAAAAAAAa8/DdQ0tiy-eiY/s400/Writeback.JPG" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div&gt; &lt;/div&gt;&lt;div&gt;Of course, you can restrict read and write access rights based on roles, so, for example, the Australian users can only update the Australian numbers.&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;It now really is easy to set up an updateable cube and make available to users with Excel 2010.&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3689647052791307812-7196783523258568194?l=richardlees.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://richardlees.blogspot.com/feeds/7196783523258568194/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3689647052791307812&amp;postID=7196783523258568194' title='6 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3689647052791307812/posts/default/7196783523258568194'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3689647052791307812/posts/default/7196783523258568194'/><link rel='alternate' type='text/html' href='http://richardlees.blogspot.com/2010/08/write-back-cubes-are-easy-with-excel.html' title='Write Back cubes are easy with Excel 2010'/><author><name>Richard Lees</name><uri>http://www.blogger.com/profile/05671716466559973540</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='31' height='32' src='http://2.bp.blogspot.com/-AdeWScnQel8/Thj9ePsjpQI/AAAAAAAAAew/XnIn3xt4a0s/s220/RichardUni.png'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://3.bp.blogspot.com/_SHRRgijB18E/TGr5KQS93ZI/AAAAAAAAAak/ID5So0pqxg4/s72-c/EnableWriteback.JPG' height='72' width='72'/><thr:total>6</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3689647052791307812.post-5846581718112786964</id><published>2010-07-26T05:44:00.003+10:00</published><updated>2010-07-26T08:13:25.594+10:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='MDX'/><title type='text'>Getting the last non empty value</title><content type='html'>LastNonEmpty is an aggregation function available in the Enterprise version of SQL Server. However, you can create your own with a little bit of recursive MDX. Essentially, you simply create a calculated member that returns the non empty value, or if empty, it looks in the previous member. It really is very simple, and is an elegant way of writing a last non empty query. To create a LastNonEmpty calculate measure, simple use MDX such as the following&lt;a href="http://2.bp.blogspot.com/_SHRRgijB18E/TEy2nk-dylI/AAAAAAAAAac/qmNa-lB5ZEI/s1600/LastNonEmpty.JPG"&gt;&lt;img style="MARGIN: 0px 0px 10px 10px; WIDTH: 284px; FLOAT: right; HEIGHT: 400px; CURSOR: hand" id="BLOGGER_PHOTO_ID_5497970036169362002" border="0" alt="" src="http://2.bp.blogspot.com/_SHRRgijB18E/TEy2nk-dylI/AAAAAAAAAac/qmNa-lB5ZEI/s400/LastNonEmpty.JPG" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;div&gt;&lt;div&gt;With Member &lt;span style="color:#ff0000;"&gt;Measures.LastHits&lt;/span&gt; as&lt;br /&gt;iif(isempty(Measures.Hits),&lt;br /&gt;([Date].[Year Month Day].prevmember,&lt;br /&gt;&lt;span style="color:#ff0000;"&gt;Measures.LastHits&lt;/span&gt;&lt;br /&gt;),Measures.Hits)&lt;/div&gt;&lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;Notice how the calculated member is called LastHits, and the calculation actually refers to LastHits. This is the recursion. It keeps referring back to itself until it gets a day with a non empty value. If there are no non empty values, the recursion will automatically stop at the first day in your dimension and return null.&lt;/div&gt;&lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;Naturally, you can add other calculations on top of this recursive calculation. For example, you might want to sum up the month to date figures using this non empty calculation. No problem, here is a MTD calculation using the last non empty calculation above.&lt;/div&gt;&lt;a href="http://2.bp.blogspot.com/_SHRRgijB18E/TEy2iHaj6gI/AAAAAAAAAaU/rw01a09vB4E/s1600/LastNonEmptyMTD.JPG"&gt;&lt;img style="MARGIN: 0px 0px 10px 10px; WIDTH: 400px; FLOAT: right; HEIGHT: 354px; CURSOR: hand" id="BLOGGER_PHOTO_ID_5497969942334794242" border="0" alt="" src="http://2.bp.blogspot.com/_SHRRgijB18E/TEy2iHaj6gI/AAAAAAAAAaU/rw01a09vB4E/s400/LastNonEmptyMTD.JPG" /&gt;&lt;/a&gt;&lt;br /&gt;With Member &lt;span style="color:#ff0000;"&gt;Measures.LastHits&lt;/span&gt; as&lt;br /&gt;iif(isempty(Measures.Hits),&lt;br /&gt;([Date].[Year Month Day].prevmember,&lt;br /&gt;&lt;span style="color:#ff0000;"&gt;Measures.LastHits&lt;br /&gt;&lt;/span&gt;),Measures.Hits)&lt;br /&gt;Member Measures.LastHitsMTD as&lt;br /&gt;sum(PeriodsToDate([Date].[Year Month Day].[Month],[Date].[Year Month Day]),Measures.LastHits)&lt;br /&gt;select&lt;br /&gt;{Measures.Hits,Measures.LastHits,Measures.LastHitsMTD}&lt;br /&gt;on Columns,&lt;br /&gt;tail([Date].[Year Month Day].[Day],31)&lt;br /&gt;on rows&lt;br /&gt;from&lt;br /&gt;EasternMining&lt;br /&gt;&lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;&lt;div&gt;For examples of real-time OLAP queries on continually updating databases see &lt;a href="http://richardlees.com.au/Sites/Demonstrations"&gt;http://RichardLees.com.au/Sites/Demonstrations&lt;/a&gt; &lt;/div&gt;&lt;br /&gt;&lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3689647052791307812-5846581718112786964?l=richardlees.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://richardlees.blogspot.com/feeds/5846581718112786964/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3689647052791307812&amp;postID=5846581718112786964' title='10 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3689647052791307812/posts/default/5846581718112786964'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3689647052791307812/posts/default/5846581718112786964'/><link rel='alternate' type='text/html' href='http://richardlees.blogspot.com/2010/07/getting-last-non-empty-value.html' title='Getting the last non empty value'/><author><name>Richard Lees</name><uri>http://www.blogger.com/profile/05671716466559973540</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='31' height='32' src='http://2.bp.blogspot.com/-AdeWScnQel8/Thj9ePsjpQI/AAAAAAAAAew/XnIn3xt4a0s/s220/RichardUni.png'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://2.bp.blogspot.com/_SHRRgijB18E/TEy2nk-dylI/AAAAAAAAAac/qmNa-lB5ZEI/s72-c/LastNonEmpty.JPG' height='72' width='72'/><thr:total>10</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3689647052791307812.post-1642623381715124089</id><published>2010-07-19T07:07:00.005+10:00</published><updated>2010-07-20T19:28:44.625+10:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='SQL Server 2008'/><category scheme='http://www.blogger.com/atom/ns#' term='SQL Performance'/><title type='text'>VARCHAR(MAX) Performance in SQL Server 2008 R2</title><content type='html'>There has been some discussion around the relative performance of VARCHAR(MAX) versus VARCHAR(n) columns. I thought it would be useful to benchmark the performance of INSERT, UPDATE and BULK INSERT of these datatypes.&lt;br /&gt;&lt;div&gt;&lt;a href="http://1.bp.blogspot.com/_SHRRgijB18E/TENuw__8f8I/AAAAAAAAAaE/jZ9TcjKe6fY/s1600/Varchar_Max_Performance.JPG"&gt;&lt;img style="MARGIN: 0px 0px 10px 10px; WIDTH: 400px; FLOAT: right; HEIGHT: 151px; CURSOR: hand" id="BLOGGER_PHOTO_ID_5495357758414553026" border="0" alt="" src="http://1.bp.blogspot.com/_SHRRgijB18E/TENuw__8f8I/AAAAAAAAAaE/jZ9TcjKe6fY/s400/Varchar_Max_Performance.JPG" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;div&gt;Essentially, I found that there is a performance cost to using VARCHAR(MAX) over VARCHAR(8000). This would suggest that you should only use VARCHAR(MAX) when you need to. (Ie you have data that can be longer than 8000 bytes.) I would add to this by saying that data types should always be as tight as practical. It would not be a good practice to make all varchar data types MAX. For example, if you application has a screen that only permits 30 characters, it would make sense to make the data type VARCHAR(30). Although, I could make exceptions if I thought this might be increased in future.&lt;/div&gt;&lt;br /&gt;&lt;div&gt;Notice; in the results of my test that INSERT and BULK INSERT are only penalised by cpu. UPDATE statements appear are penalised&lt;a href="http://2.bp.blogspot.com/_SHRRgijB18E/TEVsElh01II/AAAAAAAAAaM/GOg5iCdZSto/s1600/TestTables.JPG"&gt;&lt;img style="MARGIN: 0px 0px 10px 10px; WIDTH: 400px; FLOAT: right; HEIGHT: 109px; CURSOR: hand" id="BLOGGER_PHOTO_ID_5495917746324821122" border="0" alt="" src="http://2.bp.blogspot.com/_SHRRgijB18E/TEVsElh01II/AAAAAAAAAaM/GOg5iCdZSto/s400/TestTables.JPG" /&gt;&lt;/a&gt; by reads and writes too. Consequently, inserts were only slower by a factor of 2, while updates were slower by a factor of 7. All tests were inserting or updating 110,879 rows taking 42MB. Compression was not used.&lt;/div&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3689647052791307812-1642623381715124089?l=richardlees.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://richardlees.blogspot.com/feeds/1642623381715124089/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3689647052791307812&amp;postID=1642623381715124089' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3689647052791307812/posts/default/1642623381715124089'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3689647052791307812/posts/default/1642623381715124089'/><link rel='alternate' type='text/html' href='http://richardlees.blogspot.com/2010/07/varcharmax-performance-in-sql-server.html' title='VARCHAR(MAX) Performance in SQL Server 2008 R2'/><author><name>Richard Lees</name><uri>http://www.blogger.com/profile/05671716466559973540</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='31' height='32' src='http://2.bp.blogspot.com/-AdeWScnQel8/Thj9ePsjpQI/AAAAAAAAAew/XnIn3xt4a0s/s220/RichardUni.png'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://1.bp.blogspot.com/_SHRRgijB18E/TENuw__8f8I/AAAAAAAAAaE/jZ9TcjKe6fY/s72-c/Varchar_Max_Performance.JPG' height='72' width='72'/><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3689647052791307812.post-8308772207984637894</id><published>2010-07-15T20:05:00.007+10:00</published><updated>2010-07-15T20:42:05.174+10:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='MDX'/><category scheme='http://www.blogger.com/atom/ns#' term='Metadata'/><category scheme='http://www.blogger.com/atom/ns#' term='Analysis Services'/><title type='text'>Querying Analysis Services For Cube Metadata</title><content type='html'>SSAS DMVs can be very useful in providing metadata information.  Ie.&lt;br /&gt;&lt;p&gt;&lt;span style="font-family:courier new;font-size:85%;color:#ff0000;"&gt;$SYSTEM.MDSCHEMA_CUBES&lt;br /&gt;$SYSTEM.MDSCHEMA_DIMENSIONS&lt;br /&gt;$SYSTEM.MDSCHEMA_FUNCTIONS&lt;br /&gt;$SYSTEM.MDSCHEMA_HIERARCHIES&lt;br /&gt;$SYSTEM.MDSCHEMA_INPUT_DATASOURCES&lt;br /&gt;$SYSTEM.MDSCHEMA_KPIS&lt;br /&gt;$SYSTEM.MDSCHEMA_LEVELS&lt;br /&gt;$SYSTEM.MDSCHEMA_MEASUREGROUP_DIMENSIONS&lt;br /&gt;$SYSTEM.MDSCHEMA_MEASUREGROUPS&lt;br /&gt;$SYSTEM.MDSCHEMA_MEASURES&lt;br /&gt;$SYSTEM.MDSCHEMA_MEMBERS&lt;br /&gt;$SYSTEM.MDSCHEMA_PROPERTIES&lt;br /&gt;$SYSTEM.MDSCHEMA_SETS&lt;/span&gt;&lt;/p&gt;SQL Server Enterprise Manager's Query editor supports "Select * From " these view.  Interestingly, a lot of this information is also available through MDX. For example, here is an easy way of getting a list of all attributes (strictly speaking, hierarchies) and their level names.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family:courier new;font-size:85%;color:#ff0000;"&gt;WITH&lt;br /&gt;member measures.HierarchyCount as dimensions.count&lt;br /&gt;set HierarchySet as tail([Date].[Year Month Day].[Day],measures.HierarchyCount)&lt;br /&gt;member measures.MyHierarchyNo as rank([Date].[Year Month Day],HierarchySet)-1&lt;br /&gt;member measures.DimensionName as&lt;br /&gt;left(dimensions(measures.MyHierarchyNo).dimension.unique_name,&lt;br /&gt;instr(dimensions(measures.MyHierarchyNo).dimension.unique_name,".")-1)&lt;br /&gt;member measures.MyHierarchy as dimensions(measures.MyHierarchyNo).name&lt;br /&gt;member measures.HierarchyLevels as dimensions(measures.MyHierarchyNo).levels.count&lt;br /&gt;member Measures.Level1Name as&lt;br /&gt;case when Measures.HierarchyLevels&gt;1 then dimensions(measures.MyHierarchyNo).levels(1).name else null end&lt;br /&gt;member Measures.Level2Name as&lt;br /&gt;case when Measures.HierarchyLevels&gt;2 then dimensions(measures.MyHierarchyNo).levels(2).name else null end&lt;br /&gt;member Measures.Level3Name as&lt;br /&gt;case when Measures.HierarchyLevels&gt;3 then dimensions(measures.MyHierarchyNo).levels(3).name else null end&lt;br /&gt;member Measures.Level4Name as&lt;br /&gt;case when Measures.HierarchyLevels&gt;4 then dimensions(measures.MyHierarchyNo).levels(4).name else null end&lt;br /&gt;member Measures.Level5Name as&lt;br /&gt;case when Measures.HierarchyLevels&gt;5 then dimensions(measures.MyHierarchyNo).levels(5).name else null end&lt;br /&gt;member Measures.Level6Name as&lt;br /&gt;case when Measures.HierarchyLevels&gt;6 then dimensions(measures.MyHierarchyNo).levels(6).name else null end&lt;br /&gt;member Measures.Level7Name as&lt;br /&gt;case when Measures.HierarchyLevels&gt;7 then dimensions(measures.MyHierarchyNo).levels(7).name else null end&lt;br /&gt;member Measures.Level8Name as&lt;br /&gt;case when Measures.HierarchyLevels&gt;8 then dimensions(measures.MyHierarchyNo).levels(8).name else null end&lt;br /&gt;SELECT NON EMPTY&lt;br /&gt;{measures.DimensionName&lt;br /&gt;,Measures.MyHierarchy&lt;br /&gt;,Measures.HierarchyLevels&lt;br /&gt;,Measures.Level1Name&lt;br /&gt;,Measures.Level2Name&lt;br /&gt;,Measures.Level3Name&lt;br /&gt;,Measures.Level4Name&lt;br /&gt;,Measures.Level5Name&lt;br /&gt;,Measures.Level6Name&lt;br /&gt;,Measures.Level7Name&lt;br /&gt;,Measures.Level8Name&lt;br /&gt;} ON Columns,&lt;br /&gt;HierarchySet on Rows&lt;br /&gt;FROM EasternMining&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;&lt;a href="http://1.bp.blogspot.com/_SHRRgijB18E/TD7fPhiWlHI/AAAAAAAAAZ8/KJW_EN99YxM/s1600/MDX_Metadata.JPG"&gt;&lt;img style="MARGIN: 0px 0px 10px 10px; WIDTH: 400px; FLOAT: right; HEIGHT: 239px; CURSOR: hand" id="BLOGGER_PHOTO_ID_5494074053232071794" border="0" alt="" src="http://1.bp.blogspot.com/_SHRRgijB18E/TD7fPhiWlHI/AAAAAAAAAZ8/KJW_EN99YxM/s400/MDX_Metadata.JPG" /&gt;&lt;/a&gt;Just replace the Date hierarchy name with a hierarchy from your own cube. This hierarchy is used to list out the attributes, so just ensure there are at least a few hundred members. These members are used to drive the query.&lt;br /&gt;&lt;br /&gt;Not sure when this would be useful.  Perhaps if you only had browser access to a cube, it did not support DMVs, and you needed to see hidden attributes.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3689647052791307812-8308772207984637894?l=richardlees.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://richardlees.blogspot.com/feeds/8308772207984637894/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3689647052791307812&amp;postID=8308772207984637894' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3689647052791307812/posts/default/8308772207984637894'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3689647052791307812/posts/default/8308772207984637894'/><link rel='alternate' type='text/html' href='http://richardlees.blogspot.com/2010/07/querying-analysis-services-for-cube.html' title='Querying Analysis Services For Cube Metadata'/><author><name>Richard Lees</name><uri>http://www.blogger.com/profile/05671716466559973540</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='31' height='32' src='http://2.bp.blogspot.com/-AdeWScnQel8/Thj9ePsjpQI/AAAAAAAAAew/XnIn3xt4a0s/s220/RichardUni.png'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://1.bp.blogspot.com/_SHRRgijB18E/TD7fPhiWlHI/AAAAAAAAAZ8/KJW_EN99YxM/s72-c/MDX_Metadata.JPG' height='72' width='72'/><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3689647052791307812.post-3944315066998396065</id><published>2010-06-28T07:27:00.008+10:00</published><updated>2010-06-28T07:41:31.894+10:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='SQL Server 2008'/><category scheme='http://www.blogger.com/atom/ns#' term='SQL Performance'/><title type='text'>Easy Tuning Options for SQL Server OLTP</title><content type='html'>Even though I have specialised in business intelligence applications, I still help customers with OLTP performance issues. Here are a couple of very simple option that can make quite a difference without changing &lt;a href="http://3.bp.blogspot.com/_SHRRgijB18E/TCfEnZtUgZI/AAAAAAAAAZ0/wYKI7W08zIY/s1600/AsyncStats.JPG"&gt;&lt;img style="MARGIN: 0px 0px 10px 10px; WIDTH: 400px; FLOAT: right; HEIGHT: 293px; CURSOR: hand" id="BLOGGER_PHOTO_ID_5487570852169154962" border="0" alt="" src="http://3.bp.blogspot.com/_SHRRgijB18E/TCfEnZtUgZI/AAAAAAAAAZ0/wYKI7W08zIY/s400/AsyncStats.JPG" /&gt;&lt;/a&gt;any code.&lt;br /&gt;&lt;br /&gt;&lt;div&gt;&lt;ol&gt;&lt;li&gt;Turn on asynchronous statistics collection. This can be a great help where there are locking (and/or deadlocking) because you want to reduce the duration of transactions. The probability of deadlock tends to be exponentially proportional to the transaction duration. Asynchronous statistics collection will enable your transactions to continue to execute without having to wait for the statistics gathering. By the way, if you really want to manage statistics in a critically high tp system, you might want to disable autostats and run them on a schedule off peak. This option is set for the server on the advanced properties tab.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Disable query decomposition. In an OLTP system, you really don't want one query to break up into many parallel tasks consuming more resources. &lt;a href="http://1.bp.blogspot.com/_SHRRgijB18E/TCfEL5dUnJI/AAAAAAAAAZs/VjFKpuNphEg/s1600/Parallelism.JPG"&gt;&lt;img style="MARGIN: 0px 0px 10px 10px; WIDTH: 400px; FLOAT: right; HEIGHT: 250px; CURSOR: hand" id="BLOGGER_PHOTO_ID_5487570379655650450" border="0" alt="" src="http://1.bp.blogspot.com/_SHRRgijB18E/TCfEL5dUnJI/AAAAAAAAAZs/VjFKpuNphEg/s400/Parallelism.JPG" /&gt;&lt;/a&gt;The property is "Max Degree of Parallelism" and is set by database.&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;Of course there are a million things that you can do differently in your application to make it perform better, but I would need to write a book to cover the essentials. Also, hardware is always an option to improve performance, but it will only improve performance if it is hardware that is a critical resource.&lt;/p&gt;&lt;p&gt;For real time BI and data mining demonstrations see &lt;a href="http://richardlees.com.au/Sites/Demonstrations"&gt;http://RichardLees.com.au/Sites/Demonstrations&lt;/a&gt;&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&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/3689647052791307812-3944315066998396065?l=richardlees.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://richardlees.blogspot.com/feeds/3944315066998396065/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3689647052791307812&amp;postID=3944315066998396065' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3689647052791307812/posts/default/3944315066998396065'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3689647052791307812/posts/default/3944315066998396065'/><link rel='alternate' type='text/html' href='http://richardlees.blogspot.com/2010/06/easy-tuning-options-for-sql-server-oltp.html' title='Easy Tuning Options for SQL Server OLTP'/><author><name>Richard Lees</name><uri>http://www.blogger.com/profile/05671716466559973540</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='31' height='32' src='http://2.bp.blogspot.com/-AdeWScnQel8/Thj9ePsjpQI/AAAAAAAAAew/XnIn3xt4a0s/s220/RichardUni.png'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://3.bp.blogspot.com/_SHRRgijB18E/TCfEnZtUgZI/AAAAAAAAAZ0/wYKI7W08zIY/s72-c/AsyncStats.JPG' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3689647052791307812.post-8621552869425567019</id><published>2010-05-06T19:12:00.004+10:00</published><updated>2010-05-07T18:18:37.867+10:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='IO Performance'/><title type='text'>Solid State Disks and IO Performance</title><content type='html'>&lt;a href="http://2.bp.blogspot.com/_SHRRgijB18E/S-KLpzO2rkI/AAAAAAAAAZk/qsuPXYBBYdA/s1600/SolidStateDisks.JPG"&gt;&lt;img style="MARGIN: 0px 0px 10px 10px; WIDTH: 400px; FLOAT: right; HEIGHT: 214px; CURSOR: hand" id="BLOGGER_PHOTO_ID_5468086447824612930" border="0" alt="" src="http://2.bp.blogspot.com/_SHRRgijB18E/S-KLpzO2rkI/AAAAAAAAAZk/qsuPXYBBYdA/s400/SolidStateDisks.JPG" /&gt;&lt;/a&gt;I just read a non bulls**t whitepaper from EMC's site about disk performance and solid state disks for the enterprise. They are calling the disks EFD (Enterprise Flash Drives). I have tired of reading storage vendors's whitepapers that dodge real IO bottleneck performance issues. This paper is very informative and discusses IO throughput, which, in my experience, tends to be one of the most limiting factors in hardware performance.&lt;br /&gt;&lt;div&gt;Download from &lt;a href="http://www.emc.com/collateral/hardware/white-papers/h6018-symmetrix-dmx-enterprise-flash-with-sql-server-databases-wp.pdf"&gt;emc.com&lt;/a&gt;&lt;/div&gt;&lt;div&gt;Or download my copy from &lt;a href="http://richardlees.com.au:8080/DropZone/h6018-symmetrix-dmx-enterprise-flash-with-sql-server-databases-wp.pdf"&gt;here&lt;/a&gt;.&lt;/div&gt;&lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;I have always known that solid state disks have faster IO than magnetic disks, but was not sure about their IO throughput. This paper has a performance test (conducted by Microsoft's SQLCAT team) and they found over 1000% throughput improvement by moving to solid state disks. One solid state disk could sustain over 2500 IOs per second! This is great news for any IO bound database server.&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;&lt;div&gt;The whitepaper also discusses SQL Server's system checkpoint the implication on the log, if you didn't know already.&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;&lt;div&gt;I suggest the whitepaper is a must read for any DBA interested in system performance.&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3689647052791307812-8621552869425567019?l=richardlees.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://richardlees.blogspot.com/feeds/8621552869425567019/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3689647052791307812&amp;postID=8621552869425567019' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3689647052791307812/posts/default/8621552869425567019'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3689647052791307812/posts/default/8621552869425567019'/><link rel='alternate' type='text/html' href='http://richardlees.blogspot.com/2010/05/solid-state-disks-and-io-performance.html' title='Solid State Disks and IO Performance'/><author><name>Richard Lees</name><uri>http://www.blogger.com/profile/05671716466559973540</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='31' height='32' src='http://2.bp.blogspot.com/-AdeWScnQel8/Thj9ePsjpQI/AAAAAAAAAew/XnIn3xt4a0s/s220/RichardUni.png'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://2.bp.blogspot.com/_SHRRgijB18E/S-KLpzO2rkI/AAAAAAAAAZk/qsuPXYBBYdA/s72-c/SolidStateDisks.JPG' height='72' width='72'/><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3689647052791307812.post-392671611630457135</id><published>2010-04-16T11:14:00.016+10:00</published><updated>2010-04-17T06:43:20.164+10:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='MDX'/><category scheme='http://www.blogger.com/atom/ns#' term='Excel 2007'/><category scheme='http://www.blogger.com/atom/ns#' term='OLAP Performance'/><title type='text'>Improving Excel's Cube Performance</title><content type='html'>I am often asked to help with Excel's query performance. The cube is fast and efficient, but some Excel queries are slow due to the MDX that Excel is generating.&lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;&lt;div&gt;This is unfortunate, but there a couple of things you can do about it.  Let me describe this particular issue.  Essentially, you have a dimension with a large number of attributes. In Excel, you have crossjoined many of these attributes on rows, which you feel is reasonable since you have also added a filter so that the query will only return a small number of rows. For example, here is a query of the FoodmartFull database, where I have filtered on just one customer surname (Peacock) and asked for many customer attributes on rows. This query takes 9 seconds for SQL Server 2008 to execute. The reason it takes so long is that the MDX is asking for a crossjoin of all the attributes, at &lt;strong&gt;all&lt;/strong&gt; levels. Note, this issue can be so bad that the results are not returned before timeout.&lt;/div&gt;&lt;div&gt;&lt;img style="TEXT-ALIGN: center; MARGIN: 0px auto 10px; WIDTH: 400px; DISPLAY: block; HEIGHT: 43px; CURSOR: hand" id="BLOGGER_PHOTO_ID_5460540005378169266" border="0" alt="" src="http://2.bp.blogspot.com/_SHRRgijB18E/S8e8NCZgGbI/AAAAAAAAAZE/PMnscf5hZM8/s400/Excel.JPG" /&gt;I have traced the MDX using SQL Profiler. See the MDX in the picture to the&lt;a href="http://2.bp.blogspot.com/_SHRRgijB18E/S8e-DR7Tq8I/AAAAAAAAAZM/EW0mvluLNc0/s1600/ExcelQuery.JPG"&gt;&lt;img style="MARGIN: 0px 0px 10px 10px; WIDTH: 400px; FLOAT: right; HEIGHT: 288px; CURSOR: hand" id="BLOGGER_PHOTO_ID_5460542036771056578" border="0" alt="" src="http://2.bp.blogspot.com/_SHRRgijB18E/S8e-DR7Tq8I/AAAAAAAAAZM/EW0mvluLNc0/s400/ExcelQuery.JPG" /&gt;&lt;/a&gt; right. (I have formatted the MDX. Wouldn't it be a nice thing if SQL EM would format SQL/MDX for us? That's a topic for another blog.) If you execute this MDX, you will receive &lt;strong&gt;3840&lt;/strong&gt; rows!  That is a huge number, when you consider there is only one customer and Excel is only displaying 1 row. (I asked for a tabular format. But even if I ask for a compact format, I only get 14 rows.)  Understandably, this MDX takes 14 seconds to execute.&lt;/div&gt;&lt;br /&gt;&lt;div&gt;The reason I chose this particular query is that all of the attributes you see in the Excel report are from the same dimension. Having all these attributes from the same dimension should be very easy for Analysis Services to return quickly, since it can determine, from the dimension, exactly which Customer cells/aggregations it needs.&lt;/div&gt;&lt;br /&gt;&lt;div&gt;I can edit the MDX to simply ask for the crossjoin of the leaf members, which will execute in just 1 second. This is much easier for Analysis Services since we are only asking for asingle row rather than 3840. See the second query. &lt;a href="http://2.bp.blogspot.com/_SHRRgijB18E/S8e_dc3bn_I/AAAAAAAAAZU/ZCPhOmBBa0g/s1600/ExcelQueryImproved.JPG"&gt;&lt;img style="MARGIN: 0px 0px 10px 10px; WIDTH: 400px; FLOAT: right; HEIGHT: 178px; CURSOR: hand" id="BLOGGER_PHOTO_ID_5460543585895817202" border="0" alt="" src="http://2.bp.blogspot.com/_SHRRgijB18E/S8e_dc3bn_I/AAAAAAAAAZU/ZCPhOmBBa0g/s400/ExcelQueryImproved.JPG" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;div&gt;Of course it is not possible to edit Excel's MDX to make the query faster, but we do have other tricks we can employ. Since the problem is that Excel is inefficiently crossjoin-ing all of the attribute hierarchy sets, we can help Excel by creating a "whopper" user hierarchy that includes all (or many) the attributes we want in our report. If we had a Customer hierarchy with these attributes, Excel wouldn't need to ask for any (or so many) crossjoins and the query will be sub second. Of course, I am not suggesting that you create "whopper" hierarchies all over the place, but it is a consideration. Also note, you do not need to create one "whopper" hierarchy, just a couple of multi-attribute hierarchies might do the trick.  This is because the cost of adding (or reducing) the number of hierarchies has an exponential effect on query performance. Each attribute you add will crossjoin the new set (it is not a single member set) with all other rows. &lt;/div&gt;&lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;a href="http://4.bp.blogspot.com/_SHRRgijB18E/S8jH1aU8lGI/AAAAAAAAAZc/6hL_tC1Ikoc/s1600/Attributes.JPG"&gt;&lt;img style="MARGIN: 0px 0px 10px 10px; WIDTH: 400px; FLOAT: right; HEIGHT: 293px; CURSOR: hand" id="BLOGGER_PHOTO_ID_5460834268600898658" border="0" alt="" src="http://4.bp.blogspot.com/_SHRRgijB18E/S8jH1aU8lGI/AAAAAAAAAZc/6hL_tC1Ikoc/s400/Attributes.JPG" /&gt;&lt;/a&gt;Another, much cleaner, option, if possible, is to simply show properties of existing members, rather than ask for a crossjoin. I say, if possible, as this is only possible if the parent attribute is in the report.  Naturally, this is much more efficient,  as the generated MDX is simply asking for the member properties of existing members (crossjoin not required).&lt;br /&gt;&lt;br /&gt;&lt;div&gt;Whenever there is an Excel performance issue, I would encourage you to trace the MDX using Profiler.  Even though you can't directly change Excel's MDX, it will help you understand the problem and give you some ideas to work around it.&lt;/div&gt;&lt;br /&gt;&lt;div&gt;For a list of Excel PivotTable tips see &lt;a href="http://richardlees.blogspot.com/2009/10/excel-2007-olap-pivottable-tips.html"&gt;http://richardlees.blogspot.com/2009/10/excel-2007-olap-pivottable-tips.html&lt;/a&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3689647052791307812-392671611630457135?l=richardlees.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://richardlees.blogspot.com/feeds/392671611630457135/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3689647052791307812&amp;postID=392671611630457135' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3689647052791307812/posts/default/392671611630457135'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3689647052791307812/posts/default/392671611630457135'/><link rel='alternate' type='text/html' href='http://richardlees.blogspot.com/2010/04/improving-excels-cube-performance.html' title='Improving Excel&apos;s Cube Performance'/><author><name>Richard Lees</name><uri>http://www.blogger.com/profile/05671716466559973540</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='31' height='32' src='http://2.bp.blogspot.com/-AdeWScnQel8/Thj9ePsjpQI/AAAAAAAAAew/XnIn3xt4a0s/s220/RichardUni.png'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://2.bp.blogspot.com/_SHRRgijB18E/S8e8NCZgGbI/AAAAAAAAAZE/PMnscf5hZM8/s72-c/Excel.JPG' height='72' width='72'/><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3689647052791307812.post-8183203752138681562</id><published>2010-03-29T19:48:00.011+11:00</published><updated>2010-03-30T18:48:03.223+11:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='MDX'/><category scheme='http://www.blogger.com/atom/ns#' term='olap cubes'/><category scheme='http://www.blogger.com/atom/ns#' term='OLAP Performance'/><title type='text'>MDX - Group by measure range dynamically</title><content type='html'>&lt;div&gt;&lt;a href="http://2.bp.blogspot.com/_SHRRgijB18E/S7Grakm4nzI/AAAAAAAAAY0/GtxwVh-t4uo/s1600/RangeQuery.JPG"&gt;&lt;img style="MARGIN: 0px 0px 10px 10px; WIDTH: 400px; FLOAT: right; HEIGHT: 159px; CURSOR: hand" id="BLOGGER_PHOTO_ID_5454329096714100530" border="0" alt="" src="http://2.bp.blogspot.com/_SHRRgijB18E/S7Grakm4nzI/AAAAAAAAAY0/GtxwVh-t4uo/s400/RangeQuery.JPG" /&gt;&lt;/a&gt;Here is a similar MDX puzzle. The aim is to aggregate members by the range their measure falls into. For example, aggregate cities into ranges (multiples of 1000) of the number of hits.&lt;br /&gt;Of course, you can hand craft the MDX to define each range, but that's not elegant, scalable or flexible. How about some MDX that dynamically creates the ranges? &lt;div&gt;&lt;br /&gt;&lt;div&gt;&lt;a href="http://4.bp.blogspot.com/_SHRRgijB18E/S7GrfKyAs8I/AAAAAAAAAY8/Yixg4h1qdc8/s1600/RangeTotals.JPG"&gt;&lt;img style="MARGIN: 0px 0px 10px 10px; WIDTH: 266px; FLOAT: right; HEIGHT: 400px; CURSOR: hand" id="BLOGGER_PHOTO_ID_5454329175680791490" border="0" alt="" src="http://4.bp.blogspot.com/_SHRRgijB18E/S7GrfKyAs8I/AAAAAAAAAY8/Yixg4h1qdc8/s400/RangeTotals.JPG" /&gt;&lt;/a&gt;Here is a solution from my Weblogs database. The query is aggregating the number of hits for all cities with hits in the 0 - 999 range, 1000 - 1999 range etc. See how it uses recursive MDX, very similar to the preceding blog, which aggregates by distinct member_caption. Recursive MDX really is very useful.&lt;/div&gt;&lt;br /&gt;&lt;div&gt;Here is what the output looks like. Note; ignore the city name, that is just the last city in the range. The first measure column has the range limit, the second column has the number of cities and the third measure has the total hits for that range.&lt;/div&gt;&lt;br /&gt;&lt;div&gt;To view the cube I used, see &lt;a href="http://richardlees.com.au/Sites/Demonstrations"&gt;&lt;span style="font-size:85%;"&gt;http://RichardLees.com.au/Sites/Demonstrations&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;div&gt;&lt;a href="http://richardlees.blogspot.com/2010/03/mdx-aggregating-by-membercaption.html"&gt;&lt;br /&gt;&lt;span style="font-size:85%;color:#6600cc;"&gt;with&lt;br /&gt;member measures.MyRange as&lt;br /&gt;cstr(round(Measures.Hits/1000,0)*1000)+"-"+cstr(round(Measures.Hits/1000+1,0)*1000-1)&lt;br /&gt;set MyCities as&lt;br /&gt;Order(nonempty([Client Host].[Client Geography].[City],Measures.Hits)&lt;br /&gt;,Measures.MyRange,BASC)&lt;br /&gt;member measures.r as rank([Client Host].[Client Geography],MyCities)&lt;br /&gt;member measures.RangeTot as iif((MyCities.item(Measures.r-2),Measures.MyRange)=Measures.MyRange,&lt;br /&gt;Measures.Hits+(Measures.RangeTot,MyCities.item(Measures.r-2)),&lt;br /&gt;Measures.Hits)&lt;br /&gt;member Measures.RangeTotal as&lt;br /&gt;iif((MyCities.item(Measures.r),Measures.MyRange)=Measures.MyRange,&lt;br /&gt;null,Measures.RangeTot)&lt;br /&gt;select {Measures.MyRange,Measures.RangeTotal} on 0,&lt;br /&gt;nonempty(MyCities,Measures.RangeTotal) on 1&lt;br /&gt;from EasternMining&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3689647052791307812-8183203752138681562?l=richardlees.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://richardlees.blogspot.com/feeds/8183203752138681562/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3689647052791307812&amp;postID=8183203752138681562' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3689647052791307812/posts/default/8183203752138681562'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3689647052791307812/posts/default/8183203752138681562'/><link rel='alternate' type='text/html' href='http://richardlees.blogspot.com/2010/03/mdx-group-by-measure-range-dynamically.html' title='MDX - Group by measure range dynamically'/><author><name>Richard Lees</name><uri>http://www.blogger.com/profile/05671716466559973540</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='31' height='32' src='http://2.bp.blogspot.com/-AdeWScnQel8/Thj9ePsjpQI/AAAAAAAAAew/XnIn3xt4a0s/s220/RichardUni.png'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://2.bp.blogspot.com/_SHRRgijB18E/S7Grakm4nzI/AAAAAAAAAY0/GtxwVh-t4uo/s72-c/RangeQuery.JPG' height='72' width='72'/><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3689647052791307812.post-4583968120221101192</id><published>2010-03-29T19:26:00.006+11:00</published><updated>2010-03-29T20:03:46.055+11:00</updated><title type='text'>MDX - Aggregating by member_caption</title><content type='html'>Here is an interesting MDX puzzle. &lt;a href="http://4.bp.blogspot.com/_SHRRgijB18E/S7BmH3vOdTI/AAAAAAAAAYU/rT8MqxVj3OU/s1600/RawCities.JPG"&gt;&lt;img style="MARGIN: 0px 0px 10px 10px; WIDTH: 400px; FLOAT: right; HEIGHT: 195px; CURSOR: hand" id="BLOGGER_PHOTO_ID_5453971434152817970" border="0" alt="" src="http://4.bp.blogspot.com/_SHRRgijB18E/S7BmH3vOdTI/AAAAAAAAAYU/rT8MqxVj3OU/s400/RawCities.JPG" /&gt;&lt;/a&gt;Try aggregating a dimension hierarchy by member captions, where the members have unique keys, either by different hierarchies or just different keys. When you try to sum() a set, you have a tendancy of losing the position of the member on the axis.&lt;br /&gt;&lt;br /&gt;Here is a solution to the puzzle using recursive MDX. If the preceding member has the same name, it moves to the preceding member and adds the measure. I have used the client geography dimension in my Weblogs cube. There are many city names that exist in multiple states and/or multiple countries. See 'Springfield' appears in 7 states. The challenge is to get the sum of all distinct city names. I know this is not a likely query, but if it was a customer name that was in many states, it might be a real query.&lt;br /&gt;&lt;br /&gt;&lt;a href="http://4.bp.blogspot.com/_SHRRgijB18E/S7BmR3G-otI/AAAAAAAAAYc/RErawA5ElS8/s1600/CityTotals.JPG"&gt;&lt;span style="font-size:85%;color:#6600cc;"&gt;&lt;img style="MARGIN: 0px 0px 10px 10px; WIDTH: 250px; FLOAT: right; HEIGHT: 385px; CURSOR: hand" id="BLOGGER_PHOTO_ID_5453971605782700754" border="0" alt="" src="http://4.bp.blogspot.com/_SHRRgijB18E/S7BmR3G-otI/AAAAAAAAAYc/RErawA5ElS8/s400/CityTotals.JPG" /&gt;&lt;/span&gt;&lt;/a&gt;&lt;span style="font-size:85%;color:#6600cc;"&gt;with&lt;br /&gt;member measures.mc as [Client Host].[Client Geography].member_caption&lt;br /&gt;set MyCities as Order(nonempty([Client Host].[Client Geography].City,Measures.Hits)&lt;br /&gt;,[Client Host].[Client Geography].member_caption,BASC)&lt;br /&gt;member measures.r as rank([Client Host].[Client Geography],MyCities)&lt;br /&gt;member measures.CityTot as iif((MyCities.item(Measures.r-2),Measures.mc)=[Client Host].[Client Geography].member_caption,&lt;br /&gt;Measures.Hits+(Measures.CityTot,MyCities.item(Measures.r-2)),&lt;br /&gt;Measures.Hits)&lt;br /&gt;member Measures.CityTotal as&lt;br /&gt;iif((MyCities.item(Measures.r),Measures.mc)=[Client Host].[Client Geography].member_caption,&lt;br /&gt;null,Measures.CityTot)&lt;br /&gt;select {Measures.CityTotal} on 0,&lt;br /&gt;non empty&lt;br /&gt;MyCities on 1&lt;br /&gt;from EasternMining&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;You can slice and dice the real data on &lt;a href="http://richardlees.com.au/Sites/Demonstrations"&gt;http://RichardLees.com.au/Sites/Demonstrations&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3689647052791307812-4583968120221101192?l=richardlees.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://richardlees.blogspot.com/feeds/4583968120221101192/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3689647052791307812&amp;postID=4583968120221101192' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3689647052791307812/posts/default/4583968120221101192'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3689647052791307812/posts/default/4583968120221101192'/><link rel='alternate' type='text/html' href='http://richardlees.blogspot.com/2010/03/mdx-aggregating-by-membercaption.html' title='MDX - Aggregating by member_caption'/><author><name>Richard Lees</name><uri>http://www.blogger.com/profile/05671716466559973540</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='31' height='32' src='http://2.bp.blogspot.com/-AdeWScnQel8/Thj9ePsjpQI/AAAAAAAAAew/XnIn3xt4a0s/s220/RichardUni.png'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://4.bp.blogspot.com/_SHRRgijB18E/S7BmH3vOdTI/AAAAAAAAAYU/rT8MqxVj3OU/s72-c/RawCities.JPG' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3689647052791307812.post-3193979901229389522</id><published>2010-03-03T18:07:00.003+11:00</published><updated>2010-03-04T06:18:56.151+11:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='SQL Server 2008 R2'/><title type='text'>You Too Can Learn About R2</title><content type='html'>&lt;a href="http://2.bp.blogspot.com/_SHRRgijB18E/S44LlpWf0hI/AAAAAAAAAYM/SU1_WAp_vcw/s1600-h/DivingIntoSQLR2.png"&gt;&lt;img style="MARGIN: 0px 0px 10px 10px; WIDTH: 192px; FLOAT: right; HEIGHT: 144px; CURSOR: hand" id="BLOGGER_PHOTO_ID_5444301740920787474" border="0" alt="" src="http://2.bp.blogspot.com/_SHRRgijB18E/S44LlpWf0hI/AAAAAAAAAYM/SU1_WAp_vcw/s400/DivingIntoSQLR2.png" /&gt;&lt;/a&gt;Microsoft have just released an updated kit for developers learning about R2. If you want to know more about it and/or download a trial version goto &lt;a href="http://go.microsoft.com/?linkid=9710868" minmax_bound="true"&gt;http://go.microsoft.com/?linkid=9710868&lt;/a&gt;&lt;br /&gt;&lt;div&gt;There are lots of pptx, demos, videos and labs. &lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;&lt;div&gt;Expect more resources shortly, but this is a really good way to get a head start with R2.&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3689647052791307812-3193979901229389522?l=richardlees.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://richardlees.blogspot.com/feeds/3193979901229389522/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3689647052791307812&amp;postID=3193979901229389522' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3689647052791307812/posts/default/3193979901229389522'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3689647052791307812/posts/default/3193979901229389522'/><link rel='alternate' type='text/html' href='http://richardlees.blogspot.com/2010/03/sql-server-2008-r2-developers-training.html' title='You Too Can Learn About R2'/><author><name>Richard Lees</name><uri>http://www.blogger.com/profile/05671716466559973540</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='31' height='32' src='http://2.bp.blogspot.com/-AdeWScnQel8/Thj9ePsjpQI/AAAAAAAAAew/XnIn3xt4a0s/s220/RichardUni.png'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://2.bp.blogspot.com/_SHRRgijB18E/S44LlpWf0hI/AAAAAAAAAYM/SU1_WAp_vcw/s72-c/DivingIntoSQLR2.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3689647052791307812.post-6647481578616379587</id><published>2010-02-21T10:22:00.028+11:00</published><updated>2010-02-28T13:11:04.843+11:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Database Design'/><category scheme='http://www.blogger.com/atom/ns#' term='SQL Performance'/><category scheme='http://www.blogger.com/atom/ns#' term='SQL Profiler'/><title type='text'>Using Profiler to tune a SQL Server data warehouse</title><content type='html'>The last blog was on tuning TP applications. How about tuning a DW application? Surely it is quite similar. Well yes, the skills for tuning data warehouses and TP systems overlap. In this blog I will provide some hints for tracing/tuning the data warehouse SQL Server database. This includes any SQL statements (and utilities) on the warehouse database.&lt;br /&gt;&lt;br /&gt;A key difference is that we want to trace SQL statements, rather than individual procedure statements. The number of statements tends to be an order of magnitude less, so we can (and should) trace for a longer period, 24 hours, if possible. But at least spanning your main load process. It is still important to include a row limit, as some performance issues can manifest as iterative SQL calls. Such as INSERT statements when you are expecting BULK INSERT, or iterative UPDATE statements for the SCD SSIS task.&lt;br /&gt;&lt;br /&gt;There is a complication. That is, some statements might be dynamically compiled and executed, in which case the trace will group by the meaningless name of the procedure and parameters. To ensure that we have the prepare statements (that's where the SQL query is) we will clear the plan cache at the start of the Profiler trace. Then we will be able to find the prepare statement for the procedure by searching earlier records on the same SPID.&lt;br /&gt;&lt;br /&gt;To trace all SQL requests&lt;br /&gt;&lt;ol&gt;&lt;li&gt;Start Profiler, as in the previous blog, clear all events, and only include &lt;strong&gt;RPC:Completed&lt;/strong&gt; from &lt;strong&gt;Stored Procedures&lt;/strong&gt; and &lt;strong&gt;SQL:BatchCompleted&lt;/strong&gt; from &lt;strong&gt;TSQL&lt;/strong&gt;. This will capture all SQL requests to SQL Server.&lt;/li&gt;&lt;li&gt;Select the same columns as the previous blog.&lt;/li&gt;&lt;li&gt;Limit the number of records to 100,000. This should be plenty, since a data warehouse tends to have a small number of large requests.&lt;/li&gt;&lt;li&gt;Execute &lt;strong&gt;DBCC FREEPROCCACHE&lt;/strong&gt; to clear the procedure cache.&lt;/li&gt;&lt;li&gt;Now leave the Profiler trace to go over a long period, perhaps 24 hours. This way you will cover an entire load and process schedule.&lt;a href="http://1.bp.blogspot.com/_SHRRgijB18E/S4hyr7qfIzI/AAAAAAAAAX8/Pamzl-2aKo8/s1600-h/TableDesign.JPG"&gt;&lt;img style="MARGIN: 0px 0px 10px 10px; WIDTH: 400px; FLOAT: right; HEIGHT: 183px; CURSOR: hand" id="BLOGGER_PHOTO_ID_5442726248753079090" border="0" alt="" src="http://1.bp.blogspot.com/_SHRRgijB18E/S4hyr7qfIzI/AAAAAAAAAX8/Pamzl-2aKo8/s400/TableDesign.JPG" /&gt;&lt;/a&gt;&lt;/li&gt;&lt;li&gt;Check that the number of trace records is less than your limit. If you hit your limit, find out which query is iteratively called. Ask yourself the question, can this iterative statement be executed more efficiently in batch? If so, change it, if not, filter it out of the trace and start the trace again.&lt;/li&gt;&lt;li&gt;It is worthwhile altering the trace table. Right click on the table, select Design and change &lt;strong&gt;ntext&lt;/strong&gt; to &lt;strong&gt;varchar(max)&lt;/strong&gt;, &lt;strong&gt;nvarchar()&lt;/strong&gt; to &lt;strong&gt;varchar()&lt;/strong&gt; and remove any unnecessary columns. You might need to change your designer options on SQL 2008 to allow a table change that involves a drop table. see &lt;a href="http://richardlees.blogspot.com/2008/11/can-no-longer-change-tables-in-sql.html"&gt;Can't change SQL table&lt;/a&gt;&lt;/li&gt;&lt;li&gt;Some of the SQL requests won't have the actual SQL statement, since it has been prepared (optimised) into a temporary procedure for fast subsequent executions. To help query the results, we will to update the trace table to include a pointer to the prepare statement. To do this, add a column &lt;strong&gt;PrepareRowNumber INT&lt;/strong&gt; to the trace table and execute the following update statement to populate it.&lt;/li&gt;&lt;a href="http://1.bp.blogspot.com/_SHRRgijB18E/S4hzA5YfwLI/AAAAAAAAAYE/QVeR1jqTRFU/s1600-h/TraceQuery.JPG"&gt;&lt;img style="MARGIN: 0px 0px 10px 10px; WIDTH: 400px; FLOAT: right; HEIGHT: 216px; CURSOR: hand" id="BLOGGER_PHOTO_ID_5442726608918003890" border="0" alt="" src="http://1.bp.blogspot.com/_SHRRgijB18E/S4hzA5YfwLI/AAAAAAAAAYE/QVeR1jqTRFU/s400/TraceQuery.JPG" /&gt;&lt;/a&gt; &lt;li&gt;&lt;span style="color:#993399;"&gt;update t&lt;br /&gt;set PrepareRowNumber=(select top 1 RowNumber from t tt where tt.starttime&lt;=t.StartTime and tt.spid=t.spid and tt.TextData like '%sp_prepare%' order by starttime desc) where TextData like '%sp_execute %' update t set PrepareRowNumber= (select top 1 RowNumber from t tt where tt.starttime&lt;=t.StartTime and tt.spid=t.spid and tt.TextData like '%sp_cursorprep%' order by starttime desc) where TextData like '%sp_cursor %'&lt;/span&gt; &lt;/li&gt;&lt;li&gt;Now you can execute a query to get the top 50 SQL batches by total resource.&lt;/li&gt;&lt;li&gt;&lt;span style="color:#993399;"&gt;select top 50 *,Duration/Executions Duration_avg, CPU/Executions CPU_avg, Reads/Executions Reads_avg, Writes/Executions Writes_avg, RowCounts/Executions RowCounts_avg from (select t.DatabaseName, t.ApplicationName, tt.TextData PrepStmt, convert(char(100),t.TextData) TextData50, sum(t.Duration)/1000.0 Duration, sum(t.CPU)/1000.0 CPU, sum(t.Reads) Reads, sum(t.Writes) Writes, sum(t.RowCounts) RowCounts, count(*) Executions from t with (nolock) left outer join t tt on tt.RowNumber=t.PrepareRowNumber where t.ObjectName is not null group by t.DatabaseName, t.ApplicationName, convert(char(100),t.TextData),tt.textdata) t order by t.Duration desc&lt;/span&gt;&lt;/li&gt;&lt;li&gt;Notice how there is &lt;strong&gt;PrepStmt&lt;/strong&gt; column as well as the &lt;strong&gt;textdata&lt;/strong&gt;. If the &lt;strong&gt;textdata&lt;/strong&gt; contains a &lt;strong&gt;sp_execute&lt;/strong&gt; (or &lt;strong&gt;sp_cursor&lt;/strong&gt;) statement, the &lt;strong&gt;PrepStmt&lt;/strong&gt; column should contain the prepare statement. That was why we cleared the proc cache at the start of the Profiler trace. &lt;/li&gt;&lt;li&gt;Now the fun starts. You can review the SQL Statements and see if there is a way of tuning them.&lt;/li&gt;&lt;/ol&gt;Note&lt;br /&gt;&lt;br /&gt;&lt;ol&gt;&lt;li&gt;The above update queries don't use the variable number, which they should do to ensure the right prepare statement is fetched. The top 50 list displays both the execute statement and the prepare statement, so you can eyeball that the correct statement has been selected. If you find that the incorrect statements are being selected, you might want to amend the update statement. An SQL function to parse the text for the parameter number would work a treat.&lt;/li&gt;&lt;li&gt;Sometimes I will include a &lt;strong&gt;CASE&lt;/strong&gt; clause around the &lt;strong&gt;textdata&lt;/strong&gt;, so that I can manually group together statements, that I know about. You should see the above top 50 SQL query as a template, or starting point, from which you can taylor for your Profiler data.&lt;/li&gt;&lt;li&gt;The above steps will also work for a TP application that calls with SQL statements (ie, without stored procedures). However a TP system is likely to have many more overlapping dynamic procedures, so it is more difficult (and more important) to locate the right prepare statement.&lt;/li&gt;&lt;li&gt;You could filter the Profiler trace to only record statements that consume a minimum resource level. However, be careful that you don't ignore repeated statements that individually consume very little resource, but collectively take a long time. A couple of examples of this would be the UPDATE statement on the SSIS SCD task, or a load process that is firing INSERTs. &lt;/li&gt;&lt;li&gt;This profiler trace is simply on the SQL Server workload. Your data warehouse is likely to have Analysis Services processing work and cache warming, which you should also be interested in. Treat this as a separate tuning exercise.&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;For a real-time data warehouse demonstration see &lt;a href="http://richardlees.com.au/Sites/Demonstrations"&gt;http://RichardLees.com.au/Sites/Demonstrations&lt;/a&gt; This site inserts about 300 facts/second into SQL Server and incrementally processes a few OLAP cubes and data mining models. The above trace output was from a 24 hours trace of the SQL Server database server.&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3689647052791307812-6647481578616379587?l=richardlees.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://richardlees.blogspot.com/feeds/6647481578616379587/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3689647052791307812&amp;postID=6647481578616379587' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3689647052791307812/posts/default/6647481578616379587'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3689647052791307812/posts/default/6647481578616379587'/><link rel='alternate' type='text/html' href='http://richardlees.blogspot.com/2010/02/using-profiler-to-tune-sql-server-data.html' title='Using Profiler to tune a SQL Server data warehouse'/><author><name>Richard Lees</name><uri>http://www.blogger.com/profile/05671716466559973540</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='31' height='32' src='http://2.bp.blogspot.com/-AdeWScnQel8/Thj9ePsjpQI/AAAAAAAAAew/XnIn3xt4a0s/s220/RichardUni.png'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://1.bp.blogspot.com/_SHRRgijB18E/S4hyr7qfIzI/AAAAAAAAAX8/Pamzl-2aKo8/s72-c/TableDesign.JPG' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3689647052791307812.post-3847499451514322848</id><published>2010-02-18T06:09:00.016+11:00</published><updated>2010-02-20T09:16:13.901+11:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='SQL Performance'/><category scheme='http://www.blogger.com/atom/ns#' term='Profiler'/><title type='text'>Using Profiler to tune a high volume transactional application (written with stored procedures)</title><content type='html'>I am a great fan of SQL Server Profiler. I often use it to diagnose performance and functional issues with SQL Server and Analysis Services. There is a classic Profiler task (performance tuning transactional systems) that I would like to share with you. The situation is that you have a high volume transactional SQL Server application written with stored procedures. You want to identify the statements that are consuming the most resources, as tuning these statements will have the biggest impact on the system.&lt;br /&gt;&lt;div&gt;&lt;div&gt;&lt;br /&gt;&lt;div&gt;You are not looking for the slowest statement, since it is not necessarily noticeable from a system point of view. From a system point of view, a statement executing 1000 times per minute taking 200ms is more interesting than a statement executing 1 time per minute taking 10 seconds.&lt;/div&gt;&lt;a href="http://1.bp.blogspot.com/_SHRRgijB18E/S38GIwI8EQI/AAAAAAAAAXc/VfvVGlcwKEo/s1600-h/trace.JPG"&gt;&lt;img style="MARGIN: 0px 0px 10px 10px; WIDTH: 320px; FLOAT: right; HEIGHT: 202px; CURSOR: hand" id="BLOGGER_PHOTO_ID_5440073622317961474" border="0" alt="" src="http://1.bp.blogspot.com/_SHRRgijB18E/S38GIwI8EQI/AAAAAAAAAXc/VfvVGlcwKEo/s320/trace.JPG" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;div&gt;The Profiler trace for this task is quite simple. &lt;/div&gt;&lt;ol&gt;&lt;li&gt;Start SQL Server Profiler&lt;/li&gt;&lt;li&gt;Start a new trace with your SQL Server instance as the source&lt;/li&gt;&lt;li&gt;Check the &lt;strong&gt;Save to table&lt;/strong&gt; option and provide a location for the trace table. I have called the trace table t, but you can use any name.&lt;/li&gt;&lt;li&gt;Check the &lt;strong&gt;Set maximum rows&lt;/strong&gt; and enter a number &lt;=100000&lt;/li&gt;&lt;li&gt;In the Events Selection tab, uncheck all events. This window is a bit annoying, since you can't resize it.&lt;/li&gt;&lt;li&gt;Check the &lt;strong&gt;SP:Statement Completed&lt;/strong&gt; event under &lt;strong&gt;Stored Procedures&lt;/strong&gt;. This is the only event you will need. To see it, you might need to check the &lt;strong&gt;Show all events&lt;/strong&gt; box.&lt;/li&gt;&lt;li&gt;Check the Show all columns box, and select the columns you want. At this point you only really need databasename, objectname, textdata, duration, cpu, reads, writes, linenumber, rowcounts and SPID. However, applicationname, NTUsername and endtime can be useful too.&lt;/li&gt;&lt;li&gt;Click &lt;strong&gt;Run&lt;a href="http://2.bp.blogspot.com/_SHRRgijB18E/S38LbwOlAyI/AAAAAAAAAXk/7LlQBLr2-f8/s1600-h/Profiler.JPG"&gt;&lt;img style="MARGIN: 0px 0px 10px 10px; WIDTH: 320px; FLOAT: right; HEIGHT: 195px; CURSOR: hand" id="BLOGGER_PHOTO_ID_5440079446317269794" border="0" alt="" src="http://2.bp.blogspot.com/_SHRRgijB18E/S38LbwOlAyI/AAAAAAAAAXk/7LlQBLr2-f8/s320/Profiler.JPG" /&gt;&lt;/a&gt;&lt;/strong&gt;&lt;/li&gt;&lt;li&gt;You will be able to see how many records are being collected, and can stop the trace manually at any time.&lt;/li&gt;&lt;li&gt;Once the trace has stopped (or you have stopped it) run the following query to get the top 50 statements by their combined duration.&lt;/li&gt;&lt;/ol&gt;&lt;div&gt;&lt;span style="font-family:courier new;font-size:85%;color:#330099;"&gt;select top 50 *,Duration/Executions Duration_avg, CPU/Executions CPU_avg, Reads/Executions Reads_avg, Writes/Executions Writes_avg, RowCounts/Executions RowCounts_avg from (select DatabaseName, objectname, convert(char(100),TextData) TextData50, sum(Duration)/1000.0 Duration,sum(CPU)/1000.0 CPU,sum(Reads) Reads, sum(Writes) Writes, sum(RowCounts) RowCounts, count(*) Executions from t with (nolock) where ObjectName is not null group by DatabaseName, ObjectName, convert(char(100),TextData) ) t order by Duration desc&lt;/span&gt;&lt;/div&gt;&lt;a href="http://3.bp.blogspot.com/_SHRRgijB18E/S38EzXPkrtI/AAAAAAAAAXM/aE3iWZ4F77E/s1600-h/Query.JPG"&gt;&lt;img style="MARGIN: 0px 0px 10px 10px; WIDTH: 320px; FLOAT: right; HEIGHT: 202px; CURSOR: hand" id="BLOGGER_PHOTO_ID_5440072155346022098" border="0" alt="" src="http://3.bp.blogspot.com/_SHRRgijB18E/S38EzXPkrtI/AAAAAAAAAXM/aE3iWZ4F77E/s320/Query.JPG" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;Now comes the fun task of tuning the individual queries. Starting with the top query, open up the stored procedure, and look at the particular statement in the context of the procedure. Is there a way to improve this particular statement, or can the structure of the procedure be improved? The answer is invariably Yes, although you want to find improvements that can be introduced with minimal disruption.&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;&lt;div&gt;A couple of notes&lt;/div&gt;&lt;br /&gt;&lt;ol&gt;&lt;li&gt;Try to avoid running Profiler on the server itself. That will add more work to the server.&lt;/li&gt;&lt;li&gt;I like to alter the trace table, removing unnecessary columns, changing nvarchar to varchar, ntext to varchar(max) to reduce the size of the table, which will make subsequent queries faster.&lt;/li&gt;&lt;li&gt;You can ask Profiler to group the data, but since you will be aggregating the data in SQL there is no need.&lt;/li&gt;&lt;li&gt;Run the profiler trace during a busy, but not critical, time.&lt;/li&gt;&lt;li&gt;Earlier versions of SQL profiler could make SQL Server unstable, if you collected too many records. This isn't such an issue now, but I encourage you to always limit the number of event records so that you minimise the database server impact.&lt;/li&gt;&lt;li&gt;The overhead on the server is mostly the cost of the Profiler application (that's why you should run the app on a machine that isn't the database server) and inserting the event records into SQL Server (which is why you should target a non production server for the trace records)&lt;/li&gt;&lt;li&gt;On a very busy system, where you do not want to trace every statement, try a filter on reads. For example, you might only record events with more than 10 reads (these aren't physical reads). The logic being that a statement looking at less than n pages, is unlikely to be a big resource consumer.&lt;/li&gt;&lt;li&gt;Start the exercise with a small record limit, and look at the results. You might want to add some additional filters to exclude/include certain application components. &lt;/li&gt;&lt;li&gt;Keep a summary of the trace output. After you have tuned the top queries, you will be able to measure the improvements. I don't think you need to keep the atomic trace records. &lt;/li&gt;&lt;li&gt;Profiler will show duration in milliseconds, althought it is saving the data in microseconds. The trace table will have duration in microseconds. Don't be confused.&lt;/li&gt;&lt;li&gt;I will often not ask Profiler to save to a trace table while tracing. Once I have the records I want, I then ask Profiler to save to a trace table.&lt;/li&gt;&lt;li&gt;If you are going to repeat this exercise, you should save the trace template. That will help you avoid defining the events, columns and filters again.&lt;/li&gt;&lt;li&gt;The query above orders by duration. You might also like to order by reads, cpu or writes. This would be particularly appropriate where your system has parallelism enabled. With parallelism a query with a 500ms duration might be consuming 4 seconds cpu. Having said that, in a high volume transaction system, it is generally not appropriate to have parallelism enabled. In a tp system, I would tend to disable parallelism by default, and turn it on for individual queries that need it, but that's another blog.&lt;/li&gt;&lt;li&gt;This exercise is just looking at stored procedure statements. This trace exercise will not include statements that are not part of a procedure.&lt;/li&gt;&lt;/ol&gt;&lt;div&gt;Also note, the Profiler can trace so much information. The more you know about Profiler, the more it will help you diagnosing performance and functional issues. This exercise is just one very narrow performance task Profiler can help you with.&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3689647052791307812-3847499451514322848?l=richardlees.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://richardlees.blogspot.com/feeds/3847499451514322848/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3689647052791307812&amp;postID=3847499451514322848' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3689647052791307812/posts/default/3847499451514322848'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3689647052791307812/posts/default/3847499451514322848'/><link rel='alternate' type='text/html' href='http://richardlees.blogspot.com/2010/02/using-profiler-to-tune-high-volume.html' title='Using Profiler to tune a high volume transactional application (written with stored procedures)'/><author><name>Richard Lees</name><uri>http://www.blogger.com/profile/05671716466559973540</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='31' height='32' src='http://2.bp.blogspot.com/-AdeWScnQel8/Thj9ePsjpQI/AAAAAAAAAew/XnIn3xt4a0s/s220/RichardUni.png'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://1.bp.blogspot.com/_SHRRgijB18E/S38GIwI8EQI/AAAAAAAAAXc/VfvVGlcwKEo/s72-c/trace.JPG' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3689647052791307812.post-1395591143533219183</id><published>2010-02-15T19:43:00.004+11:00</published><updated>2010-02-15T19:59:55.502+11:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='SSIS Packages'/><category scheme='http://www.blogger.com/atom/ns#' term='SCD'/><title type='text'>SSIS Slowly Changing Dimension SCD Wizard is Too Slow</title><content type='html'>&lt;img style="MARGIN: 0px 0px 10px 10px; WIDTH: 320px; FLOAT: right; HEIGHT: 238px; CURSOR: hand" id="BLOGGER_PHOTO_ID_5438388326695892242" border="0" alt="" src="http://4.bp.blogspot.com/_SHRRgijB18E/S3kJXotW9RI/AAAAAAAAAW8/30LU6ClBoNk/s320/SSIS_SCD.JPG" /&gt;Pre SQL Server 2005, we had to write our own scripts for managing slowly changing dimensions (aka Type 2 dimensions). Since SQL Server 2005 we now have an easy to use SSIS wizard to maintain SCD. Unfortunately, the SSIS wizard generates an OLEDB update statement that is executed once for every record. Naturally the iterative statement is very slow with large volumes.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Hence if you have large volumes of data and need to have high performing SCD updates, you will need to use a batch update statement. This is not all that difficult. I would even suggest that if you can't write the SQL, you might not the be the best person to design a DW.&lt;br /&gt;&lt;br /&gt;Essentially, you need to "close off" the records that are getting replaced, and "insert" all new records. The "closing" of records can be done in a single UPDATE statement as shown in the query below. Note, the exact script you need will depend on your SCD and non-SCD attributes and the format of your source data. But essentially, you are updating all records in the DW where there exists a newer record. If you have some Type 1 attributes, will need another update statement for them.&lt;br /&gt;&lt;br /&gt;The second task is simply to insert the new (and updated) records. You should do this in an SSIS data flow task.  The select statement in the image below, is an example of what the query will look like to feed that load. Note, your query will be a little different depending on how you identify changes, but the query below will give you a starting template.&lt;br /&gt;&lt;br /&gt;By the way, if you really don't want to write the above queries (or prefer the elegance of a component) there are 3rd party SSIS components that manage SCD with set based operations. &lt;a href="http://4.bp.blogspot.com/_SHRRgijB18E/S3kJhIkkhjI/AAAAAAAAAXE/k3dHz5S7pdM/s1600-h/BatchSCD.JPG"&gt;&lt;img style="MARGIN: 0px 10px 10px 0px; WIDTH: 400px; FLOAT: left; HEIGHT: 116px; CURSOR: hand" id="BLOGGER_PHOTO_ID_5438388489867789874" border="0" alt="" src="http://4.bp.blogspot.com/_SHRRgijB18E/S3kJhIkkhjI/AAAAAAAAAXE/k3dHz5S7pdM/s400/BatchSCD.JPG" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Of course, I am sure a future version of SQL Server will have an SCD wizard that works with batch update commands.  Just not sure which one.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3689647052791307812-1395591143533219183?l=richardlees.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://richardlees.blogspot.com/feeds/1395591143533219183/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3689647052791307812&amp;postID=1395591143533219183' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3689647052791307812/posts/default/1395591143533219183'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3689647052791307812/posts/default/1395591143533219183'/><link rel='alternate' type='text/html' href='http://richardlees.blogspot.com/2010/02/ssis-slowly-changing-dimension-scd.html' title='SSIS Slowly Changing Dimension SCD Wizard is Too Slow'/><author><name>Richard Lees</name><uri>http://www.blogger.com/profile/05671716466559973540</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='31' height='32' src='http://2.bp.blogspot.com/-AdeWScnQel8/Thj9ePsjpQI/AAAAAAAAAew/XnIn3xt4a0s/s220/RichardUni.png'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://4.bp.blogspot.com/_SHRRgijB18E/S3kJXotW9RI/AAAAAAAAAW8/30LU6ClBoNk/s72-c/SSIS_SCD.JPG' height='72' width='72'/><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3689647052791307812.post-7896880897192406847</id><published>2010-02-14T19:56:00.006+11:00</published><updated>2010-02-14T20:29:39.145+11:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='olap cubes'/><category scheme='http://www.blogger.com/atom/ns#' term='PerformancePoint'/><category scheme='http://www.blogger.com/atom/ns#' term='Web Analytics'/><title type='text'>I like PerformancePoint Dashboards</title><content type='html'>&lt;a href="http://2.bp.blogspot.com/_SHRRgijB18E/S3fBq5rFazI/AAAAAAAAAW0/1_fu1ilnVZ8/s1600-h/PerformancePointDashboard.JPG"&gt;&lt;img style="MARGIN: 0px 0px 10px 10px; WIDTH: 400px; FLOAT: right; HEIGHT: 298px; CURSOR: hand" id="BLOGGER_PHOTO_ID_5438028017853360946" border="0" alt="" src="http://2.bp.blogspot.com/_SHRRgijB18E/S3fBq5rFazI/AAAAAAAAAW0/1_fu1ilnVZ8/s400/PerformancePointDashboard.JPG" /&gt;&lt;/a&gt;PerformancePoint (PPS) dashboards really are easy to create once you have an OLAP cube with useful information. One of my cubes, Weblogs, is continually updating with all the web activity on my site RichardLees.com.au. In a couple of days, two and a half years ago, I built some PPS dashboards as a public demonstration. I still find them very interesting and a useful guide for activity and performance on my site.&lt;br /&gt;&lt;div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;You can see the same information I can. The picture on the right, is a dashboard pointing at the weblogs cube. To get to it, just click on the &lt;strong&gt;Web Analytics&lt;/strong&gt; dashboard on the left hand menu of &lt;a href="http://richardlees.com.au/sites/demonstrations"&gt;http://RichardLees.com.au/sites/demonstrations&lt;/a&gt;. Then click on the &lt;strong&gt;Web Performance&lt;/strong&gt; tab on the top of the dashboard. The scorecard, is asking for the last 6 days on columns and the top 20 http resource types on rows. It will continually adapt to the date and web activity. It doesn't end there, you can click on any resource type (in the picture, I have clicked on &lt;strong&gt;.aspx&lt;/strong&gt;) so that the 3 right hand charts will dynamically filter to this resource type. At a glance I can see which particular resources are most active and their response time trends. From the bottom chart, it would appear that most of the .aspx resources have been progressively slowing down over the last 12 months. This might be due to the increased activity, database size growing, or perhaps my web site is competing for internet bandwidth with my boys web downloads. I'll investigate further.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;The point I would like to make, is that once you have useful information in a cube, it is really quite a simple job to create some dynamic and interesting dashboards. If you are heading towards dashboards, I would encourage you to start by very loosely defining what you want on the dashboard. You only need to determine what information your cube requires. Then once, you have the cube, you can be creative with the dashboards to see how to maximise the information you have available. In my experience, what tends to happen is that you will start with a generic dashboard, then, with feedback, make more dynamic functionality so that users are able to easily drill down to the interesting aspects.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;There is really only one gotcha that stands out for me and that is the client browser. Internet Explorer 7 and 8, obviously, are very good clients. Other browsers, don't support all PPS dashboard functionality. Of course, if you have a particular browser in question, you could browse to my demonstration and see how well it does. Feel free to post comments on this blog with your results.&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;By the way, I have similar beliefs about SQL Server Reporting Services. Once you have a cube with all the information, dynamic reporting is quite simple. Also, Reporting Services and PerformancePoint should be seen as complementary, rather than alternatives.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3689647052791307812-7896880897192406847?l=richardlees.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://richardlees.blogspot.com/feeds/7896880897192406847/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3689647052791307812&amp;postID=7896880897192406847' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3689647052791307812/posts/default/7896880897192406847'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3689647052791307812/posts/default/7896880897192406847'/><link rel='alternate' type='text/html' href='http://richardlees.blogspot.com/2010/02/i-like-performancepoint-dashboards.html' title='I like PerformancePoint Dashboards'/><author><name>Richard Lees</name><uri>http://www.blogger.com/profile/05671716466559973540</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='31' height='32' src='http://2.bp.blogspot.com/-AdeWScnQel8/Thj9ePsjpQI/AAAAAAAAAew/XnIn3xt4a0s/s220/RichardUni.png'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://2.bp.blogspot.com/_SHRRgijB18E/S3fBq5rFazI/AAAAAAAAAW0/1_fu1ilnVZ8/s72-c/PerformancePointDashboard.JPG' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3689647052791307812.post-3594624083770484743</id><published>2010-01-30T06:29:00.013+11:00</published><updated>2010-02-01T14:23:58.081+11:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='olap cubes'/><category scheme='http://www.blogger.com/atom/ns#' term='OLAP Performance'/><title type='text'>Aggregations Won't Fix Cube Performance</title><content type='html'>&lt;a href="http://2.bp.blogspot.com/_SHRRgijB18E/S2NKtfdnujI/AAAAAAAAAWM/UGJjgGAbcTc/s1600-h/Aggregations41.JPG"&gt;&lt;img id="BLOGGER_PHOTO_ID_5432267720939256370" style="FLOAT: right; MARGIN: 0px 0px 10px 10px; WIDTH: 320px; CURSOR: hand; HEIGHT: 283px" alt="" src="http://2.bp.blogspot.com/_SHRRgijB18E/S2NKtfdnujI/AAAAAAAAAWM/UGJjgGAbcTc/s320/Aggregations41.JPG" border="0" /&gt;&lt;/a&gt;There appears to be a temptation for SQL sales people to demonstrate the aggregation wizard with the impression that it is the tool to solve &lt;a href="http://1.bp.blogspot.com/_SHRRgijB18E/S2NHweqaAjI/AAAAAAAAAV0/gL-_dF9YlF8/s1600-h/Aggregations41.JPG"&gt;&lt;/a&gt;all performance issues. Unfortunately, it isn't, and I need to dispel that myth.&lt;br /&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;&lt;br /&gt;&lt;div&gt;The aggregation design wizard is not a performance panacea, but it can help. It's like tire pressure in a racing car. The optimal pressure will help performance, but it won't turn a slow car into a fast car.&lt;/div&gt;&lt;br /&gt;&lt;div&gt;Let's think about what the aggregation wizard does. The base cube cells only contain values at the intersection of all dimension keys. When a query asks for data at a higher level, AS must derive this data from the base cells by aggregating them. The aggregation wizard determines which of these aggregations to calculate at cube processing time and store on disk. So designing aggregations will impact your cube processing time. The more aggregations you have the longer the cube processing time, and the larger the cube on disk. At query time, if AS does not have the pre-aggregated cell on disk, it will get the lower level cells (these might be aggregations too) and calculate the aggregated cell. This new aggregation will reside in AS memory like any other aggregation or base cell. So there's the rub, even if you don't have any aggregations, AS will create them at query time (or cache warming) and try and keep them in memory for subsequent queries. So, if AS is not memory constrained, aggregations will only help the queries while the cache is cold. In essence, a cube warming query (CREATE CACHE) will have a similar effect as aggregation designs for a non-memory constrained cube.&lt;a href="http://3.bp.blogspot.com/_SHRRgijB18E/S2NK4g6LDbI/AAAAAAAAAWU/hRLL8zB_GXQ/s1600-h/AggregationUsage.JPG"&gt;&lt;img id="BLOGGER_PHOTO_ID_5432267910306008498" style="FLOAT: right; MARGIN: 0px 0px 10px 10px; WIDTH: 320px; CURSOR: hand; HEIGHT: 237px" alt="" src="http://3.bp.blogspot.com/_SHRRgijB18E/S2NK4g6LDbI/AAAAAAAAAWU/hRLL8zB_GXQ/s320/AggregationUsage.JPG" border="0" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;Cubes that benefit the most from aggregation design are ones that are updated frequently (for example, every minute or so during busy times) and are larger than the memory available to the AS data cache. Note, when a cube partition is processed, all the cells and aggregations in memory are dropped.&lt;br /&gt;&lt;br /&gt;&lt;div&gt;Another important note; by default, the aggregation wizard will only create aggregations on key attributes and attributes in natural hierarchies. For example, if you have a Date dimension with date as the key, the aggregation wizard by default, will only build aggregations on "date" and &lt;strong&gt;natural&lt;/strong&gt; hierarchies. If these other attributes are commonly used and you would like the aggregation wizard to consider them, you can ask for it in the Aggregation Usage window (or properties in the cube design). The "Default" usage for a key is "Unrestricted", while the "Default" usage for a non-key property is "None". However, don't see this as a silver bullet, by enabling all properties as "Unrestricted"! That will only result in very thin aggregation design. Also, aggregations on keys, tend to be the most important ones, so the default options suit most cubes.&lt;br /&gt;&lt;br /&gt;When using "Distinct Count" measure in a cube you need to be even more careful with aggregations, since each aggregation (and base cell) will include every distinct value of the Distinct Count. This can make aggregations very large and costly to build/query.&lt;/div&gt;&lt;br /&gt;&lt;div&gt;For very small non volatile cubes such as the New Zealand census cube &lt;a href="http://easternmining.com.au/sites/Demonstrations/Pages/NZCensus_Excel.aspx"&gt;http://EasternMining.com.au/sites/Demonstrations/Pages/NZCensus_Excel.aspx&lt;/a&gt; there is no &lt;a href="http://easternmining.com.au/sites/Demonstrations/Pages/NZCensus_Excel.aspx"&gt;&lt;img id="BLOGGER_PHOTO_ID_5432265283600395698" style="FLOAT: right; MARGIN: 0px 0px 10px 10px; WIDTH: 200px; CURSOR: hand; HEIGHT: 123px" alt="" src="http://4.bp.blogspot.com/_SHRRgijB18E/S2NIfnqwLbI/AAAAAAAAAWE/-avMaxMRpWk/s200/NZCensus.JPG" border="0" /&gt;&lt;/a&gt;benefit from aggregations, which is why that cube has no aggregations. &lt;/div&gt;&lt;br /&gt;&lt;div&gt;For really good OLAP cube performance you should ensure that the design is optimal, and use aggregation design (or usage based aggregation design) as a final operational tuning exercise. &lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3689647052791307812-3594624083770484743?l=richardlees.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://richardlees.blogspot.com/feeds/3594624083770484743/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3689647052791307812&amp;postID=3594624083770484743' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3689647052791307812/posts/default/3594624083770484743'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3689647052791307812/posts/default/3594624083770484743'/><link rel='alternate' type='text/html' href='http://richardlees.blogspot.com/2010/01/aggregations-wont-fix-cube-performance.html' title='Aggregations Won&apos;t Fix Cube Performance'/><author><name>Richard Lees</name><uri>http://www.blogger.com/profile/05671716466559973540</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='31' height='32' src='http://2.bp.blogspot.com/-AdeWScnQel8/Thj9ePsjpQI/AAAAAAAAAew/XnIn3xt4a0s/s220/RichardUni.png'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://2.bp.blogspot.com/_SHRRgijB18E/S2NKtfdnujI/AAAAAAAAAWM/UGJjgGAbcTc/s72-c/Aggregations41.JPG' height='72' width='72'/><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3689647052791307812.post-5962674377549611606</id><published>2010-01-15T20:36:00.009+11:00</published><updated>2010-01-15T21:18:44.692+11:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='SQL Server 2008'/><category scheme='http://www.blogger.com/atom/ns#' term='SQL Performance'/><category scheme='http://www.blogger.com/atom/ns#' term='SQL'/><title type='text'>Are Temp Tables Bad?</title><content type='html'>I have often said that temp tables are the hallmark of sloppy SQL programmers. Why can't these developers write correlated subqueries with outer joins without writing out intermediate tables? I even had a little wager for a bottle of whisky with Ron Soukup about the merits of temp tables just after I joined Microsoft in 1995. That was before I knew who Ron was. I was to show that a query could be faster without temp tables, but the old SQL Server 6.5 optimiser was didn't make it easy for me. Essentially, if you are using a temp table, you are saying that you can do it better than the database optimiser. Or in other words, the optimiser has room for improvement.&lt;br /&gt;&lt;br /&gt;However, a colleague has brought to my attention a query that reliably runs faster with a temp table, and there isn't much I can do about it.&lt;br /&gt;&lt;br /&gt;It would appear that SQL Server's optimiser uses the estimated rowcount when setting up/executing a sort. So that if the estimated rowcount is much higher than the actual, the sort becomes slightly more expensive than the same sort with an accurate estimation. This is quite intuitive to me, since a very large sort might use a different algorithm than a very small sort. (In the old mainframe days we always had to tell the sort program how many records it was going to sort.)&lt;br /&gt;&lt;br /&gt;In my query without a temp table, SQL Server estimates 26,000 rows will be sorted, while the actual number of rows to be sorted is 36. Of course the sort after the temp table quite accurately estimates 36 rows. So, by inserting the 36 rows into a temp table before sorting, the optimiser will know quite accurately how many records it will sort. To further support this hypothesis, I included a "top 36" in the query without a temp table, and low and behold, it estimated 36 rows in the sort and ran faster (in the same time as the temp table query).&lt;br /&gt;&lt;br /&gt;I feel that this performance effect is more of a curiosity than a valid justification to use temp tables in queries with Order By clauses. Also, it is very likely that in a future version of SQL Server the Optimiser team will enhance the product to dynamically determine what sort will be employed during query execution. Then the temp table query will be slower.&lt;br /&gt;&lt;br /&gt;The original query was from a customer database, but here is an equivalent query that I have written on my home Perfmon database. If you want to reproduce the effect, the key is to have a query that estimates a very large number of rows in the sort, while the actual number is very small.&lt;br /&gt;&lt;p&gt;The picture shows the execution plan for the two queries. The first query (no temp table) took 53% of the total resources, while the second and third queries (with temp table) only used 47% of the total resources. This was further evidenced from the temp table queries using 10% less cpu than the original query. 328 ms of cpu versus 297 ms for the temp table queries.&lt;/p&gt;&lt;span style="font-family:courier new;font-size:78%;color:#6600cc;"&gt;DECLARE @date DATETIME &lt;/span&gt;&lt;a href="http://3.bp.blogspot.com/_SHRRgijB18E/S1A6lMX8BOI/AAAAAAAAAVk/yd6LX5WqqMU/s1600-h/showplan.JPG"&gt;&lt;span style="font-family:courier new;font-size:78%;color:#6600cc;"&gt;&lt;img style="MARGIN: 0px 0px 10px 10px; WIDTH: 400px; FLOAT: right; HEIGHT: 261px; CURSOR: hand" id="BLOGGER_PHOTO_ID_5426901961632646370" border="0" alt="" src="http://3.bp.blogspot.com/_SHRRgijB18E/S1A6lMX8BOI/AAAAAAAAAVk/yd6LX5WqqMU/s400/showplan.JPG" /&gt;&lt;/span&gt;&lt;/a&gt;&lt;br /&gt;&lt;span style="font-family:courier new;font-size:78%;color:#6600cc;"&gt;&lt;span style="color:#6600cc;"&gt;DECLARE @value real&lt;br /&gt;select @date = '2010-01-15 15:50:00', @value = 700&lt;/span&gt;&lt;span style="color:#000099;"&gt;&lt;br /&gt;&lt;span style="color:#006600;"&gt;--select * from (&lt;br /&gt;&lt;/span&gt;select &lt;span style="color:#006600;"&gt;--top 53&lt;br /&gt;&lt;/span&gt;MAX(CounterDateTime) CounterDateTime,&lt;br /&gt;SUM(CounterID) CounterID,&lt;br /&gt;CounterValue CounterValue&lt;br /&gt;FROM CounterDatafacts with (nolock)&lt;br /&gt;WHERE CounterValue between @value and @value+2&lt;br /&gt;and CounterDateTime between @date and DATEADD(hh,1,@date)&lt;br /&gt;Group By CounterValue &lt;span style="color:#006600;"&gt;--) as x&lt;br /&gt;&lt;/span&gt;ORDER BY CounterDateTime&lt;br /&gt;&lt;/span&gt;&lt;span style="color:#993399;"&gt;--Same query query using temp table&lt;br /&gt;select MAX(CounterDateTime) CounterDateTime,&lt;br /&gt;SUM(CounterID) CounterID,&lt;br /&gt;CounterValue CounterValue&lt;br /&gt;INTO #TEMP&lt;br /&gt;FROM CounterDatafacts with (nolock)&lt;br /&gt;WHERE CounterValue between @value and @value+2&lt;br /&gt;and CounterDateTime between @date and DATEADD(hh,1,@date)&lt;br /&gt;Group By CounterValue&lt;br /&gt;SELECT * FROM #TEMP ORDER BY CounterDateTime &lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;font-size:78%;color:#993399;"&gt;DROP TABLE #Temp&lt;/span&gt;&lt;span style="color:#330033;"&gt; &lt;/span&gt;&lt;br /&gt;&lt;span style="color:#330033;"&gt;&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3689647052791307812-5962674377549611606?l=richardlees.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://richardlees.blogspot.com/feeds/5962674377549611606/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3689647052791307812&amp;postID=5962674377549611606' title='10 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3689647052791307812/posts/default/5962674377549611606'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3689647052791307812/posts/default/5962674377549611606'/><link rel='alternate' type='text/html' href='http://richardlees.blogspot.com/2010/01/are-temp-tables-bad.html' title='Are Temp Tables Bad?'/><author><name>Richard Lees</name><uri>http://www.blogger.com/profile/05671716466559973540</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='31' height='32' src='http://2.bp.blogspot.com/-AdeWScnQel8/Thj9ePsjpQI/AAAAAAAAAew/XnIn3xt4a0s/s220/RichardUni.png'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://3.bp.blogspot.com/_SHRRgijB18E/S1A6lMX8BOI/AAAAAAAAAVk/yd6LX5WqqMU/s72-c/showplan.JPG' height='72' width='72'/><thr:total>10</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3689647052791307812.post-4749260026552252384</id><published>2010-01-15T05:57:00.006+11:00</published><updated>2010-01-15T06:37:17.613+11:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='SSIS Packages'/><category scheme='http://www.blogger.com/atom/ns#' term='Transactions'/><title type='text'>The Transaction Manager is not available</title><content type='html'>&lt;a href="http://2.bp.blogspot.com/_SHRRgijB18E/S09wZQWjYUI/AAAAAAAAAVc/tNjOyk_X9Hk/s1600-h/TransactionRequired.JPG"&gt;&lt;img style="MARGIN: 0px 0px 10px 10px; WIDTH: 224px; FLOAT: right; HEIGHT: 320px; CURSOR: hand" id="BLOGGER_PHOTO_ID_5426679655193338178" border="0" alt="" src="http://2.bp.blogspot.com/_SHRRgijB18E/S09wZQWjYUI/AAAAAAAAAVc/tNjOyk_X9Hk/s320/TransactionRequired.JPG" /&gt;&lt;/a&gt; &lt;div&gt;A few things you need to know about SSIS TransactionOption "Required".&lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;&lt;div&gt;By default, SSIS packages do not require transactions, and will not coordinate transactions. That is because the default TransactionOption is "Supported". Supported means that if this package (or subordinate object) is called from another object that has TransactionOption "Required" then it will be part of the transaction. However, if you select "Required" as the package TransactionOption (or task that consumes multiple connections, such as a sequence container) then SSIS will use DTC to manage the transactions across multiple connections. This may not be immediately intuitive to you since you might only connect to a single SQL Server. However, with multiple connections, the only way SSIS can ensure they are all committed, or none are committed, is to use DTC (Distributed Transaction Coordinator).&lt;/div&gt;&lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;If the SSIS package is running on the same server as the only SQL Server database, then it is quite simple, you only need to have DTC service running. However, if the SSIS package is running on a different machine to SQL Server (this includes running SSIS in dev mode locally and attaching to another SQL machine) then you will need to ensure that DTC can communicate across machines. Otherwise, you might get a message like these&lt;/div&gt;&lt;br /&gt;&lt;div&gt;&lt;span style="font-size:85%;color:#6600cc;"&gt;Error: The SSIS Runtime has failed to start the distributed transaction due to error 0x8004D01B "The Transaction Manager is not available.". The DTC transaction failed to start. This could occur because the MSDTC Service is not running.&lt;/span&gt;&lt;/div&gt;&lt;br /&gt;&lt;div&gt;&lt;span style="color:#6633ff;"&gt;Error: The SSIS Runtime has failed to enlist the OLE DB connection in a distributed transaction with error 0x8004D00A "Unable to enlist in the transaction.".&lt;/span&gt;&lt;/div&gt;&lt;a href="http://2.bp.blogspot.com/_SHRRgijB18E/S09wRuwSeUI/AAAAAAAAAVU/3pp1rReMMUY/s1600-h/DTC.JPG"&gt;&lt;img style="MARGIN: 0px 0px 10px 10px; WIDTH: 320px; FLOAT: right; HEIGHT: 178px; CURSOR: hand" id="BLOGGER_PHOTO_ID_5426679525915392322" border="0" alt="" src="http://2.bp.blogspot.com/_SHRRgijB18E/S09wRuwSeUI/AAAAAAAAAVU/3pp1rReMMUY/s320/DTC.JPG" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;These error messages are, in essence, telling you that DTC cannot communicate with SQL Server. Here's what you need to check&lt;br /&gt;Ensure both computers can ping each other using netbios names. Netbios names must be used for DTC transactions.&lt;/li&gt;&lt;li&gt;Both computers must have MSDTC running&lt;/li&gt;&lt;li&gt;If there are firewalls on either machine, they must have exceptions for Port 135 or for the program msdtc.exe.&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;The dtcPing tool can be helpful in diagnosing DTC issues. For more information see &lt;a href="http://support.microsoft.com/kb/918331"&gt;http://support.microsoft.com/kb/918331&lt;/a&gt;&lt;/p&gt;&lt;p&gt;Now, whether or not you want to use large scope transactions is another question, and not a simple one to answer. A couple of points to think about when you are considering using transactions are&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Any update, insert etc to a rdmbs is either completed successfully or rolled back. Using TransactionOption "Required" as I discuss above is about combining many update/insert/delete statements so that they all succeed or none of them do.&lt;/li&gt;&lt;li&gt;Be mindful of how long the transaction runs for. This is the time that another transaction might have to wait if there is a locking issue.&lt;/li&gt;&lt;li&gt;There might be other ways to ensure your data integrity. For example, if you are thinking about transactions to enable simple re-running of the package in the event of an error, you might be able to structure the package so that it can re-run even after successful completion of some, or all, of the tasks.&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;Another point worth noting with SSIS transactions is that you can set TransactionOption of "Required" on the package, and have a task (such as Execute SQL) which has a TransactionOption of NotSupported. In this way, so long as the tasks executes, it will not be rolled back with the rest of the package in the event of a failure.&lt;/p&gt;&lt;p&gt;For a real-time demonstration of data that has SSIS packages loading/processing about 300 new records/second (all day, every day) see &lt;a href="http://richardlees.com.au/sites/Demonstrations"&gt;http://RichardLees.com.au/sites/Demonstrations&lt;/a&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/3689647052791307812-4749260026552252384?l=richardlees.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://richardlees.blogspot.com/feeds/4749260026552252384/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3689647052791307812&amp;postID=4749260026552252384' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3689647052791307812/posts/default/4749260026552252384'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3689647052791307812/posts/default/4749260026552252384'/><link rel='alternate' type='text/html' href='http://richardlees.blogspot.com/2010/01/transaction-manager-is-not-available.html' title='The Transaction Manager is not available'/><author><name>Richard Lees</name><uri>http://www.blogger.com/profile/05671716466559973540</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='31' height='32' src='http://2.bp.blogspot.com/-AdeWScnQel8/Thj9ePsjpQI/AAAAAAAAAew/XnIn3xt4a0s/s220/RichardUni.png'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://2.bp.blogspot.com/_SHRRgijB18E/S09wZQWjYUI/AAAAAAAAAVc/tNjOyk_X9Hk/s72-c/TransactionRequired.JPG' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3689647052791307812.post-6318936444446644434</id><published>2010-01-11T19:42:00.005+11:00</published><updated>2010-01-11T19:51:57.511+11:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='SQL'/><title type='text'>Circular Relationships</title><content type='html'>&lt;a href="http://2.bp.blogspot.com/_SHRRgijB18E/S0rl2oRb8lI/AAAAAAAAAVM/c5BdHxYQBV0/s1600-h/CircularRelationships.JPG"&gt;&lt;img style="MARGIN: 0px 0px 10px 10px; WIDTH: 145px; FLOAT: right; HEIGHT: 200px; CURSOR: hand" id="BLOGGER_PHOTO_ID_5425401427807433298" border="0" alt="" src="http://2.bp.blogspot.com/_SHRRgijB18E/S0rl2oRb8lI/AAAAAAAAAVM/c5BdHxYQBV0/s200/CircularRelationships.JPG" /&gt;&lt;/a&gt; I am occasionally reviewing database designs and one of the things I like to check is normalisation. One indication that a table structure isn't in third normal form is the existence of circular relationships. That is where you can navigate from one entity back to itself via circular relationships. I should stress that the existance of a circular relationship does not necessarily mean that we aren't in 3rd normal form, but it is a good clue.&lt;br /&gt;&lt;div&gt;Here is an example in the picture of a circular relationship. From the Products table you can get to ProductType directly or via ProductGroup.&lt;/div&gt;&lt;br /&gt;&lt;div&gt;To search for circular relationships that are declared by referential constraints, you can use the following SQL query. (Note, it is only looking for a three level relationship, and it could be enhanced to find deeper redundant relationships.)&lt;/div&gt;&lt;br /&gt;&lt;div&gt;&lt;span style="font-size:85%;color:#6600cc;"&gt;select&lt;br /&gt;OBJECT_NAME(fk.parent_object_id) Child&lt;br /&gt;,OBJECT_NAME(fk.referenced_object_id) Parent&lt;br /&gt;,OBJECT_NAME(fk1.referenced_object_id) Intermedidate&lt;br /&gt;,(select top 1 name from sys.foreign_key_columns fkc&lt;br /&gt;inner join sys.columns c on c.object_id=fkc.parent_object_id and c.column_id=fkc.parent_column_id&lt;br /&gt;where fkc.constraint_object_id=fk.object_id) FK_FirstColumn_FirstRelationship&lt;br /&gt;,(select top 1 name from sys.foreign_key_columns fkc&lt;br /&gt;inner join sys.columns c on c.object_id=fkc.referenced_object_id and c.column_id=fkc.referenced_column_id&lt;br /&gt;where fkc.constraint_object_id=fk.object_id) PK_FirstColumn_FirstRelationship&lt;br /&gt;,(select top 1 name from sys.foreign_key_columns fkc&lt;br /&gt;inner join sys.columns c on c.object_id=fkc.parent_object_id and c.column_id=fkc.parent_column_id&lt;br /&gt;where fkc.constraint_object_id=fk1.object_id) FK_FirstColumn_IntermediateRelationship1&lt;br /&gt;,(select top 1 name from sys.foreign_key_columns fkc&lt;br /&gt;inner join sys.columns c on c.object_id=fkc.referenced_object_id and c.column_id=fkc.referenced_column_id&lt;br /&gt;where fkc.constraint_object_id=fk1.object_id) PK_FirstColumn_IntermediateRelationship1&lt;br /&gt;,(select top 1 name from sys.foreign_key_columns fkc&lt;br /&gt;inner join sys.columns c on c.object_id=fkc.parent_object_id and c.column_id=fkc.parent_column_id&lt;br /&gt;where fkc.constraint_object_id=fk2.object_id) FK_FirstColumn_IntermediateRelationship2&lt;br /&gt;,(select top 1 name from sys.foreign_key_columns fkc&lt;br /&gt;inner join sys.columns c on c.object_id=fkc.referenced_object_id and c.column_id=fkc.referenced_column_id&lt;br /&gt;where fkc.constraint_object_id=fk2.object_id) PK_FirstColumn_IntermediateRelationship2&lt;br /&gt;from sys.foreign_keys fk&lt;br /&gt;inner join sys.foreign_keys fk1 on fk1.parent_object_id=fk.parent_object_id&lt;br /&gt;inner join sys.foreign_keys fk2 on fk2.parent_object_id=fk1.referenced_object_id&lt;br /&gt;and fk2.referenced_object_id=fk.referenced_object_id&lt;/span&gt; &lt;/div&gt;&lt;br /&gt;&lt;div&gt;Remember, there is a lot of information in the relational catalog. We should use when we want need information about our data structures.&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3689647052791307812-6318936444446644434?l=richardlees.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://richardlees.blogspot.com/feeds/6318936444446644434/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3689647052791307812&amp;postID=6318936444446644434' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3689647052791307812/posts/default/6318936444446644434'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3689647052791307812/posts/default/6318936444446644434'/><link rel='alternate' type='text/html' href='http://richardlees.blogspot.com/2010/01/circular-relationships.html' title='Circular Relationships'/><author><name>Richard Lees</name><uri>http://www.blogger.com/profile/05671716466559973540</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='31' height='32' src='http://2.bp.blogspot.com/-AdeWScnQel8/Thj9ePsjpQI/AAAAAAAAAew/XnIn3xt4a0s/s220/RichardUni.png'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://2.bp.blogspot.com/_SHRRgijB18E/S0rl2oRb8lI/AAAAAAAAAVM/c5BdHxYQBV0/s72-c/CircularRelationships.JPG' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3689647052791307812.post-2091925884698373186</id><published>2010-01-04T08:03:00.006+11:00</published><updated>2010-01-04T08:21:42.699+11:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='eat well'/><category scheme='http://www.blogger.com/atom/ns#' term='unicycle'/><title type='text'>Get Active and Eat Well</title><content type='html'>&lt;div&gt;&lt;div&gt;&lt;a href="http://3.bp.blogspot.com/_SHRRgijB18E/S0EIhFL77OI/AAAAAAAAAU0/WbMhMG3M_KE/s1600-h/Anna+and+Richard+unicycling.JPG"&gt;&lt;img style="MARGIN: 0px 0px 10px 10px; WIDTH: 320px; FLOAT: right; HEIGHT: 290px; CURSOR: hand" id="BLOGGER_PHOTO_ID_5422624790751735010" border="0" alt="" src="http://3.bp.blogspot.com/_SHRRgijB18E/S0EIhFL77OI/AAAAAAAAAU0/WbMhMG3M_KE/s320/Anna+and+Richard+unicycling.JPG" /&gt;&lt;/a&gt;It's summer holiday season in Australia and I have been spending more time with the family, beach and unicyle. That's why I haven't blogged for 3 weeks.&lt;br /&gt;&lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;My first blog for 2010 is to remind everyone that we need to keep active and eat well. I have had too many friends that have become software developers at the expense of social interaction, exercise and good eating. That was me for the first 5 years I worked at Microsoft. Long work hours combined with junk food and little activity. I have since learnt that you are a much more inovative developer if you keep a balance between work, family, exercise and social interaction.  The good news is that you can recover, I did.&lt;/div&gt;&lt;br /&gt;&lt;div&gt;Yesterday, my second son, Sven, helped me create a video of my typical unicycle jaunt around Sydney's North Head. Take a peak at &lt;a href="http://www.youtube.com/watch?v=FpNslE5J3tI"&gt;http://www.youtube.com/watch?v=FpNslE5J3tI&lt;/a&gt; &lt;/div&gt;&lt;img style="MARGIN: 0px 0px 10px 10px; WIDTH: 320px; FLOAT: right; HEIGHT: 304px; CURSOR: hand" id="BLOGGER_PHOTO_ID_5422626459859117618" border="0" alt="" src="http://1.bp.blogspot.com/_SHRRgijB18E/S0EKCPGJZjI/AAAAAAAAAVE/QYSng_bDatM/s320/Richard+going+down+the+Snake.JPG" /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;div&gt;Stay active and eat well.&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3689647052791307812-2091925884698373186?l=richardlees.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://richardlees.blogspot.com/feeds/2091925884698373186/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3689647052791307812&amp;postID=2091925884698373186' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3689647052791307812/posts/default/2091925884698373186'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3689647052791307812/posts/default/2091925884698373186'/><link rel='alternate' type='text/html' href='http://richardlees.blogspot.com/2010/01/get-active-and-eat-well.html' title='Get Active and Eat Well'/><author><name>Richard Lees</name><uri>http://www.blogger.com/profile/05671716466559973540</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='31' height='32' src='http://2.bp.blogspot.com/-AdeWScnQel8/Thj9ePsjpQI/AAAAAAAAAew/XnIn3xt4a0s/s220/RichardUni.png'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://3.bp.blogspot.com/_SHRRgijB18E/S0EIhFL77OI/AAAAAAAAAU0/WbMhMG3M_KE/s72-c/Anna+and+Richard+unicycling.JPG' height='72' width='72'/><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3689647052791307812.post-2293054190519399367</id><published>2009-11-26T19:25:00.003+11:00</published><updated>2009-11-29T08:13:28.937+11:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='SQL Server 2008'/><category scheme='http://www.blogger.com/atom/ns#' term='SQL Performance'/><category scheme='http://www.blogger.com/atom/ns#' term='SQL'/><title type='text'>When to use clustered indexes</title><content type='html'>I often found experienced database designers, new to SQL Server, will come with preconceived ideas about the optimiser and storage engine.  One of these ideas is that clustered indexes should be used sparingly, since they come at a cost with limited benefit.  With SQL Server, this is by and large untrue, and as a general rule, most tables will be better off with one of the indexes being clustered.&lt;br /&gt;&lt;br /&gt;SQL Server (and Sybase) have historically had a structurally different way of maintaining clustered indexes.  They actually use the index leaf pages to hold the data columns, which enables them to save space, reduce the number of pages touched in an index path, and use them to very efficiently sequence scan.&lt;br /&gt;&lt;br /&gt;There is a useful performance analysis of clustered versus non-clustered PK index on &lt;a href="http://technet.microsoft.com/en-us/library/cc917672.aspx"&gt;http://technet.microsoft.com/en-us/library/cc917672.aspx&lt;/a&gt;   This paper looks primarily at the cost and throughput of inserts, updates and deletes.   By and large, the clustered indexes performed better.  There is one significant exception, where there are high volume concurrent inserts, with keys in close proximity.  In this situation, concurrent can compete for locks and latches with other active transactions with similar keys.  By the way, this does not necessarily mean that you shouldn't use a clustered index for the table, rather, it might mean your clustered index should be on a key that won't create hot spots.  For example tbTransaction might have contention with TransactionID clustered, but a clustered index on AccountID would be less contentious and might even be better serving queries.&lt;br /&gt;&lt;br /&gt;One area that this paper doesn't look at, and in my view, is one of the most important reasons for clustering, is queries that join very large tables.  If you are joining two or more very large tables, the optimiser will like to have the opportunity to Merge Join the larger tables.  For example, a query that was to join tbTransaction with tbTransactionCharges and many other smaller tables is likely to benefit from having both large tables clustered in the same sequence.  This could be TransactionId, or, perhaps more likely, AccountID.  If queries on these two tables were often filtered by particular accounts (or tbAccount columns) then having all 3 tables clustered by AccountID (and also TransactionID or TransactionDate for the transaction tables) is likely to provide very good join performance.&lt;br /&gt;&lt;br /&gt;We are very fortunate with relational databases, in that we can change the index structure without having to rewrite any application queries.  However, I would encourage database designers to analyse in some detail the activity on the biggest tables, and design appropriate clustered indexes early on.  Clustereed indexes are much more difficult to change at a later date than non clustered indexes.  Also, for those smaller tables, make one of the indexes (typically the natural primary key) clustered.&lt;br /&gt;&lt;br /&gt;&lt;span style="color:#000099;"&gt;For a real-time demonstration of a database application continuously processing about 300 records/sec see &lt;/span&gt;&lt;a href="http://richardlees.com.au/Sites/Demonstrations"&gt;&lt;span style="color:#3333ff;"&gt;http://RichardLees.com.au/Sites/Demonstrations&lt;/span&gt;&lt;/a&gt;&lt;span style="color:#000099;"&gt;  The Perfmon application is close to real-time and is processing about 300 perfmon records all day, every day, on commodity 32 bit (yes I really should upgrade) hardware.&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3689647052791307812-2293054190519399367?l=richardlees.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://richardlees.blogspot.com/feeds/2293054190519399367/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3689647052791307812&amp;postID=2293054190519399367' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3689647052791307812/posts/default/2293054190519399367'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3689647052791307812/posts/default/2293054190519399367'/><link rel='alternate' type='text/html' href='http://richardlees.blogspot.com/2009/11/when-to-use-clustered-indexes.html' title='When to use clustered indexes'/><author><name>Richard Lees</name><uri>http://www.blogger.com/profile/05671716466559973540</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='31' height='32' src='http://2.bp.blogspot.com/-AdeWScnQel8/Thj9ePsjpQI/AAAAAAAAAew/XnIn3xt4a0s/s220/RichardUni.png'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3689647052791307812.post-3430187511430008538</id><published>2009-11-24T16:47:00.007+11:00</published><updated>2009-11-26T19:23:33.661+11:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='SQL Server 2008'/><category scheme='http://www.blogger.com/atom/ns#' term='SQL'/><title type='text'>How to Avoid SQL Reserved Words</title><content type='html'>Have you ever been querying a database and received a syntax error because one of your column, table or some other object name is clashing with an SQL reserved word? If so, here is an easy way to avoid reserved words during your development cycle. In simple terms it joins a list of reserved words with your catalog columns.&lt;br /&gt;&lt;br /&gt;&lt;ol&gt;&lt;li&gt;Simply download the ReservedWords.csv from &lt;a href="http://richardlees.com.au:8080/dropzone/SQLReservedWords.csv"&gt;http://richardlees.com.au:8080/dropzone/SQLReservedWords.csv&lt;/a&gt; Or if you prefer, copy the words from SQL Server books online and load them into a table. &lt;/li&gt;&lt;li&gt;Load the words into this table&lt;br /&gt;&lt;span style="color:#6600cc;"&gt;&lt;strong&gt;create table ReservedWords (ReservedWord varchar(128), ReserveWordList varchar(128))&lt;/strong&gt;&lt;/span&gt;&lt;/li&gt;&lt;li&gt;Run the following query &lt;/li&gt;&lt;/ol&gt;&lt;p&gt;&lt;span style="color:#6600cc;"&gt;&lt;strong&gt;select o.name [Object Name],c.name [Column Name], w.ReserveWordList&lt;br /&gt;from sys.columns c&lt;br /&gt;inner join coreddsdevtemp..ReservedWords w on w.ReservedWord=c.[name]&lt;br /&gt;inner join sys.objects o on c.object_id=o.object_id and o.type NOT IN ('S','IT')&lt;br /&gt;order by 1,2&lt;/span&gt;&lt;/strong&gt;&lt;/p&gt;You will get a list of all the objects that have column names on the SQL 2008, ODBC, and SQL future reserved lists. By the way, you might want to filter out the ODBC words if they are not appropriate for you.&lt;br /&gt;&lt;br /&gt;I should add that it isn't fatal to have reserved words in your columns. It just means that you might have to use delimiters around your column names.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3689647052791307812-3430187511430008538?l=richardlees.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://richardlees.blogspot.com/feeds/3430187511430008538/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3689647052791307812&amp;postID=3430187511430008538' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3689647052791307812/posts/default/3430187511430008538'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3689647052791307812/posts/default/3430187511430008538'/><link rel='alternate' type='text/html' href='http://richardlees.blogspot.com/2009/11/how-to-avoid-sql-reserved-words.html' title='How to Avoid SQL Reserved Words'/><author><name>Richard Lees</name><uri>http://www.blogger.com/profile/05671716466559973540</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='31' height='32' src='http://2.bp.blogspot.com/-AdeWScnQel8/Thj9ePsjpQI/AAAAAAAAAew/XnIn3xt4a0s/s220/RichardUni.png'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3689647052791307812.post-4885098416828316083</id><published>2009-11-21T08:02:00.009+11:00</published><updated>2009-11-21T08:31:39.915+11:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='MDX'/><category scheme='http://www.blogger.com/atom/ns#' term='OLAP Performance'/><title type='text'>MDX is a redundant language</title><content type='html'>It is often said that SQL is a redundant language, since all but the most simple queries can be written many different ways. Well, I find MDX much more redundant than SQL. There really are thousands of ways to express the same logical query. I like this, since it means that there are lots of opportunities to tune the query by writing it in a different way. Just like SQL. As an MDX writer, I encourage you to strengthen your ability to write queries in different (redundant) ways. This will make you more powerful in query writing and give you more performance opportunities.&lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;&lt;div&gt;Here is a simple example, which involves one of the most common "brick walls" hit by new MDX writers. You want to write a query that would be the logical equivalent of &lt;/div&gt;&lt;div&gt;&lt;span style="color:#663366;"&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span style="color:#663366;"&gt;columnx IN ('Australia','New Zealand')&lt;/span&gt;. Here is one way to write this in MDX&lt;a href="http://4.bp.blogspot.com/_SHRRgijB18E/SwcHWpFkLDI/AAAAAAAAAUk/BnN512pkL8w/s1600/aggregation.JPG"&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;div&gt;&lt;span style="color:#663366;"&gt;with member [Client Host].[Client Geography].ANZ as&lt;br /&gt;sum({[Client Host].[Client Geography].[Region].&amp;amp;[Asia].[AUSTRALIA],&lt;br /&gt;[Client Host].[Client Geography].[Region].&amp;amp;[Asia].[NEW ZEALAND]})&lt;br /&gt;select&lt;br /&gt;{measures.hits,measures.sessions,Measures.[bytes total]} on 0,&lt;br /&gt;tail([Date].[Year Month Day].[Month],12) on 1&lt;br /&gt;from [EasternMining]&lt;br /&gt;where [Client Host].[Client Geography].ANZ&lt;a href="http://2.bp.blogspot.com/_SHRRgijB18E/SwcKSewr-sI/AAAAAAAAAUs/8bdrp9vGYM8/s1600/aggregation.JPG"&gt;&lt;img style="MARGIN: 0px 0px 10px 10px; WIDTH: 320px; FLOAT: right; HEIGHT: 230px; CURSOR: hand" id="BLOGGER_PHOTO_ID_5406301190292765378" border="0" alt="" src="http://2.bp.blogspot.com/_SHRRgijB18E/SwcKSewr-sI/AAAAAAAAAUs/8bdrp9vGYM8/s320/aggregation.JPG" /&gt;&lt;/a&gt;&lt;/span&gt;&lt;/div&gt;&lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;Here is another way to write exactly the same query&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;span style="color:#663366;"&gt;select&lt;br /&gt;{measures.hits,measures.sessions,Measures.[bytes total]} on 0,&lt;br /&gt;tail([Date].[Year Month Day].[Month],12) on 1&lt;br /&gt;from&lt;br /&gt;(select {[Client Host].[Client Geography].[Region].&amp;amp;[Asia].[AUSTRALIA],&lt;br /&gt;[Client Host].[Client Geography].[Region].&amp;amp;[Asia].[NEW ZEALAND]} on 0&lt;br /&gt;from [EasternMining])&lt;/span&gt;&lt;/div&gt;&lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;Of course there are many other ways of expressing this same query. I would not suggest that you should use one technique over the other. It really depends on which one is easier to read, easier to maintain, and runs faster.&lt;a href="http://1.bp.blogspot.com/_SHRRgijB18E/SwcHOKjvnnI/AAAAAAAAAUc/V_UH-7BEwTQ/s1600/subquery.JPG"&gt;&lt;img style="MARGIN: 0px 0px 10px 10px; WIDTH: 320px; FLOAT: right; HEIGHT: 213px; CURSOR: hand" id="BLOGGER_PHOTO_ID_5406297817615408754" border="0" alt="" src="http://1.bp.blogspot.com/_SHRRgijB18E/SwcHOKjvnnI/AAAAAAAAAUc/V_UH-7BEwTQ/s320/subquery.JPG" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;For online real-time OLAP and data mining demonstrations goto &lt;a href="http://richardlees.com.au/Sites/Demonstrations"&gt;http://RichardLees.com.au/Sites/Demonstrations&lt;/a&gt; &lt;/div&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3689647052791307812-4885098416828316083?l=richardlees.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://richardlees.blogspot.com/feeds/4885098416828316083/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3689647052791307812&amp;postID=4885098416828316083' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3689647052791307812/posts/default/4885098416828316083'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3689647052791307812/posts/default/4885098416828316083'/><link rel='alternate' type='text/html' href='http://richardlees.blogspot.com/2009/11/mdx-is-redundant-language.html' title='MDX is a redundant language'/><author><name>Richard Lees</name><uri>http://www.blogger.com/profile/05671716466559973540</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='31' height='32' src='http://2.bp.blogspot.com/-AdeWScnQel8/Thj9ePsjpQI/AAAAAAAAAew/XnIn3xt4a0s/s220/RichardUni.png'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://2.bp.blogspot.com/_SHRRgijB18E/SwcKSewr-sI/AAAAAAAAAUs/8bdrp9vGYM8/s72-c/aggregation.JPG' height='72' width='72'/><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3689647052791307812.post-5111500174744959450</id><published>2009-11-07T16:09:00.011+11:00</published><updated>2009-11-14T07:01:30.290+11:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='OLAP Performance'/><category scheme='http://www.blogger.com/atom/ns#' term='Basket Analysis'/><title type='text'>Optimizing Basket Analysis</title><content type='html'>Basket Analysis is the analysis of sales by basket (or transaction). It can be useful in understanding questions such as&lt;br /&gt;&lt;ul&gt;&lt;li&gt;What is the total value of sales for transactions that include products x (and y, and z...)?&lt;/li&gt;&lt;li&gt;What is the average transaction value for transactions that include products x (and y, and z...)?&lt;/li&gt;&lt;li&gt;What proportion of transactions include products x (or y or z)? And what proportion of transactions that include products a (and b, and c...) also include products x (and y, and z...)?&lt;/li&gt;&lt;li&gt;How did the attach rate of product x change during the sales promotion series? And was there a lasting effect.&lt;/li&gt;&lt;li&gt;Any analysis that requires mix of products within transactions.&lt;/li&gt;&lt;/ul&gt;Traditionally basket analysis has been achieved by running SQL queries on very large, highly scalable, databases that dynamically summarize by transaction id. You can imagine that these queries involve huge numbers of transaction and item records and really enjoy massively parallel computers. However, it is now possible to run quite effective basket analysis systems using low cost commodity hardware and Microsoft OLAP cubes. Many people do this by creating a cube with a dimension on the transaction id. If you are considering creating such a solution, I have two major suggestions. This has helped me create basket analysis solutions that consume hundreds of millions of transactions in cubes that support relatively fast ad hoc queries (sub 30 seconds).&lt;br /&gt;&lt;ol&gt;&lt;li&gt;Rather than creating a dimension on transaction id, create a dimension on basket id. A basket is defined by the unique combination of items. So all the transactions that include items m, n, o and p, would be considered the same basket. Essentially, the basket id can be used as a dimension like the transaction id, but the basket it dimension is a fraction of the size. This makes the solution much more scalable and faster. A basket id dimension table can be created quite simply by creating a bit string with one bit representing each item. For example an 8000 byte string will support 64000 items. There is a little bit of math to create the bit string. but essentially each ItemID can create an exponential of 2 to get its position in the string. ie&lt;br /&gt;POWER(convert(bigint,2),ItemID). Of course, a bigint can only support 2 to the power of 64, so you will need to do this by a series of 8 byte strings. This would be very very tedious SQL to write, but I found that it is quite easy to write a little SQL to generate the full SQL. Also note, there is no need to keep the full 8000 bytes, it can be a varbinary to the right most byte with an item. See below.&lt;a href="http://4.bp.blogspot.com/_SHRRgijB18E/SvUOQgO2D6I/AAAAAAAAAUU/LZkcvIyRm-s/s1600-h/BasketSQL.JPG"&gt;&lt;img style="MARGIN: 0px 0px 10px 10px; WIDTH: 400px; FLOAT: right; HEIGHT: 300px; CURSOR: hand" id="BLOGGER_PHOTO_ID_5401239004793671586" border="0" alt="" src="http://4.bp.blogspot.com/_SHRRgijB18E/SvUOQgO2D6I/AAAAAAAAAUU/LZkcvIyRm-s/s400/BasketSQL.JPG" /&gt;&lt;/a&gt;&lt;/li&gt;&lt;br /&gt;&lt;li&gt;The second major suggestion I have, is to create a dimension using an identity column from the basket id table (there is no need to have the varbinary(8000) column in the cube, and write calculated measures for the basket analysis. ie. I would resist using the distinct count physical measures in the cube. You really need a Basket dimension, so that you can identify specific combinations of items.&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;The result is a relatively small cube, with a relatively large basket id dimension. The cube will have two measure groups, one for transactions with transaction count and sale value, while the item measure group will have item sales value and item count measures. Here is the short SQL query that will generate the full query for the basket ids. It is only a few lines of SQL, but it will generate over 1100 lines of SQL that will form the query for calculating the unique basket id. You will want to run this query incrementally, so just put a where clause at the bottom to ensure it is over the high water mark. I would also encourage you to put the query in an SSIS package with an OLEDB call to a procedure to look up the identity column on the basket table, so that can be stored with the transaction. The rest, including building the cube, should be easy.&lt;/p&gt;&lt;p&gt;A complementary application to this basket analysis cube is a &lt;a href="http://richardlees.blogspot.com/2009/05/data-mining-as-enhancement-to-basket.html"&gt;data mining model &lt;/a&gt;targeted at the relationships between sales items and useful for predicting sales items.&lt;/p&gt;&lt;p&gt;If you are having performance issues with your basket analysis solutions or want help with your application, don't hesitate to ask me to help. This is the type of BI activity I really enjoy.&lt;br /&gt;&lt;/p&gt;&lt;p&gt;For real-time demonstrations of OLAP, data mining and related BI technologies see &lt;a href="http://easternmining.com.au/Sites/Demonstrations"&gt;http://EasternMining.com.au/Sites/Demonstrations&lt;/a&gt; &lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3689647052791307812-5111500174744959450?l=richardlees.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://richardlees.blogspot.com/feeds/5111500174744959450/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3689647052791307812&amp;postID=5111500174744959450' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3689647052791307812/posts/default/5111500174744959450'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3689647052791307812/posts/default/5111500174744959450'/><link rel='alternate' type='text/html' href='http://richardlees.blogspot.com/2009/11/optimizing-basket-analysis.html' title='Optimizing Basket Analysis'/><author><name>Richard Lees</name><uri>http://www.blogger.com/profile/05671716466559973540</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='31' height='32' src='http://2.bp.blogspot.com/-AdeWScnQel8/Thj9ePsjpQI/AAAAAAAAAew/XnIn3xt4a0s/s220/RichardUni.png'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://4.bp.blogspot.com/_SHRRgijB18E/SvUOQgO2D6I/AAAAAAAAAUU/LZkcvIyRm-s/s72-c/BasketSQL.JPG' height='72' width='72'/><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3689647052791307812.post-8047025929592803200</id><published>2009-10-17T13:58:00.016+11:00</published><updated>2010-02-19T06:45:57.148+11:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Excel 2007'/><category scheme='http://www.blogger.com/atom/ns#' term='cube browsers'/><title type='text'>Excel 2007 OLAP PivotTable Tips</title><content type='html'>I am a great fan of Excel as an ad hoc cube browser, even though it lacks a few features I would like to see. Many people don't appreciate some of the functionality available within Excel. Here are a few of my favourite features.&lt;br /&gt;&lt;a href="http://2.bp.blogspot.com/_SHRRgijB18E/StlCXqvQEFI/AAAAAAAAAUM/3eikVMPKRFY/s1600-h/RefreshOnOpening.JPG"&gt;&lt;img style="MARGIN: 0px 0px 10px 10px; WIDTH: 196px; FLOAT: right; HEIGHT: 200px; CURSOR: hand" id="BLOGGER_PHOTO_ID_5393415003129778258" border="0" alt="" src="http://2.bp.blogspot.com/_SHRRgijB18E/StlCXqvQEFI/AAAAAAAAAUM/3eikVMPKRFY/s200/RefreshOnOpening.JPG" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;ol&gt;&lt;li&gt;Right click somewhere in the pivottable and select &lt;strong&gt;PivotTable Options&lt;/strong&gt;. Get familiar with all the options. My favorites are &lt;strong&gt;Refresh data when opening the file&lt;/strong&gt; and disabling &lt;strong&gt;row/column totals&lt;/strong&gt;.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Use conditional formatting within the cube. Excel, will honor these cell formatting options by default.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Use Excel's conditional formatting on top of number formatting. Excel will hold the conditional formatting until you change what's on rows.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;When you have a cube with multiple measure groups (don't they all now?) use the &lt;strong&gt;Show fields related to:&lt;/strong&gt; to filter measures and dimensions pertinent to your query.&lt;/li&gt;&lt;a href="http://4.bp.blogspot.com/_SHRRgijB18E/StlAYl8SZTI/AAAAAAAAAUE/vhziMQ07KrI/s1600-h/Formatting2.JPG"&gt;&lt;img style="MARGIN: 0px 0px 10px 10px; WIDTH: 102px; FLOAT: right; HEIGHT: 200px; CURSOR: hand" id="BLOGGER_PHOTO_ID_5393412819998893362" border="0" alt="" src="http://4.bp.blogspot.com/_SHRRgijB18E/StlAYl8SZTI/AAAAAAAAAUE/vhziMQ07KrI/s200/Formatting2.JPG" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;li&gt;Use the &lt;strong&gt;Design&lt;/strong&gt; tab to &lt;strong&gt;remove subtotals&lt;/strong&gt; by row or columns. Removing sub totals here, will be preserved when you continue pivoting, whereas removing sub totals from rows/columns will not persist.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Also use the &lt;strong&gt;Design&lt;/strong&gt; tab to insert &lt;strong&gt;banded rows&lt;/strong&gt; (or columns)&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Right click on the Row or Column header to request &lt;strong&gt;sort&lt;/strong&gt; by measure columns (ascending or descending) or even some other column value.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Right click on the Row or Column header to request &lt;strong&gt;Top 10&lt;/strong&gt; list. Note, this is really a Top n list, as you can determine how many rows/columns to display. Particularly useful when you have too many columns (rows) and you just want to show the top n.&lt;/li&gt;&lt;a href="http://1.bp.blogspot.com/_SHRRgijB18E/StlAYLH9nhI/AAAAAAAAAT8/krSZ_2EBd-I/s1600-h/MeasureFilter4.JPG"&gt;&lt;img style="MARGIN: 0px 0px 10px 10px; WIDTH: 112px; FLOAT: right; HEIGHT: 200px; CURSOR: hand" id="BLOGGER_PHOTO_ID_5393412812800106002" border="0" alt="" src="http://1.bp.blogspot.com/_SHRRgijB18E/StlAYLH9nhI/AAAAAAAAAT8/krSZ_2EBd-I/s200/MeasureFilter4.JPG" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;li&gt;Right click on the Row or Column header and use the &lt;strong&gt;Show/Hide Fields&lt;/strong&gt; option to remove hierarchy levels from the display. For example, you may want to show cities, without the higher levels of state, country and region.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Right click on the Row or Column header to display member properties. Particularly useful for properties such as phone number&lt;/li&gt;&lt;br /&gt;&lt;li&gt;If there are additional actions set up in the cube (a real drill through, for example) right click on the cell (or member) to trigger an &lt;strong&gt;Additional action&lt;/strong&gt;. I find this particularly useful when you want to drill through to a report that shows all the underlying facts, with all their associated details. It can also be useful to take you to an independent application that takes some information from where you are in the cube. For example, Google search (or Bing.com) for a member name.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Right click on a bunch of rows (or columns) to G&lt;strong&gt;roup&lt;/strong&gt; them together, as one member.&lt;a href="http://2.bp.blogspot.com/_SHRRgijB18E/StlAX3WvSzI/AAAAAAAAAT0/UE2FxlHery0/s1600-h/Subtotals5.JPG"&gt;&lt;img style="MARGIN: 0px 0px 10px 10px; WIDTH: 200px; FLOAT: right; HEIGHT: 77px; CURSOR: hand" id="BLOGGER_PHOTO_ID_5393412807493372722" border="0" alt="" src="http://2.bp.blogspot.com/_SHRRgijB18E/StlAX3WvSzI/AAAAAAAAAT0/UE2FxlHery0/s200/Subtotals5.JPG" /&gt;&lt;/a&gt;&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Get your cube designer to create useful &lt;strong&gt;sets&lt;/strong&gt; within appropriate dimensions. You can drag a set onto rows/columns. For example, Last 10 weeks, or New Customers etc.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;If you have a large cube (or poorly designed one) that is slow to query, then check the &lt;strong&gt;Defer Layout Update&lt;/strong&gt; box on the bottom of the field list. In this way, you can design your report without waiting for the data to display. Just click on the &lt;strong&gt;Update&lt;/strong&gt; button when you are ready.&lt;a href="http://2.bp.blogspot.com/_SHRRgijB18E/StlAXXxlfXI/AAAAAAAAATs/exihgrAUU4M/s1600-h/SortOrder6.JPG"&gt;&lt;img style="MARGIN: 0px 0px 10px 10px; WIDTH: 176px; FLOAT: right; HEIGHT: 200px; CURSOR: hand" id="BLOGGER_PHOTO_ID_5393412799016041842" border="0" alt="" src="http://2.bp.blogspot.com/_SHRRgijB18E/StlAXXxlfXI/AAAAAAAAATs/exihgrAUU4M/s200/SortOrder6.JPG" /&gt;&lt;/a&gt;&lt;/li&gt;&lt;br /&gt;&lt;li&gt;If your query is taking too long, just click on your &lt;strong&gt;Esc&lt;/strong&gt; button, and the query will be cancelled immediately.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;In the &lt;strong&gt;Options&lt;/strong&gt; tab, select&lt;strong&gt; Offline OLAP&lt;/strong&gt; under &lt;strong&gt;OLAP Tools&lt;/strong&gt;, to save a copy (or subset) of your cube locally. Note, this will only be available if you have been granted local copy permission. Can be really useful, if you want to take your laptop away and analyse the data.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;For the very advanced user, in the &lt;strong&gt;Options&lt;/strong&gt; tab, select&lt;strong&gt; Convert to formulas&lt;/strong&gt; from the&lt;strong&gt; OLAP tools&lt;/strong&gt; button. This will convert your entire pivottable to individual cell formulas of CUBEMEMBER and CUBEVALUE. From this point you will be able to create a report with full control on what is in every cell of the report. It really is quite powerful, although you lose the flexibility of a &lt;a href="http://3.bp.blogspot.com/_SHRRgijB18E/StlAXAmLbuI/AAAAAAAAATk/MhsrkXSUU5I/s1600-h/ShowHideFieldList8.JPG"&gt;&lt;img style="MARGIN: 0px 0px 10px 10px; WIDTH: 200px; FLOAT: right; HEIGHT: 160px; CURSOR: hand" id="BLOGGER_PHOTO_ID_5393412792794181346" border="0" alt="" src="http://3.bp.blogspot.com/_SHRRgijB18E/StlAXAmLbuI/AAAAAAAAATk/MhsrkXSUU5I/s200/ShowHideFieldList8.JPG" /&gt;&lt;/a&gt;pivottable. A hybrid option I like is to keep the filters from the pivottable and have a report using the CUBEMEMBER and CUBEVALUE formulas, which gives you a bit of the best from each.&lt;/li&gt;&lt;li&gt;To remove a row or column member, right click and select &lt;strong&gt;Filter&lt;/strong&gt;, then &lt;strong&gt;Hide Selected Items&lt;/strong&gt; (or &lt;strong&gt;Keep Selected Items&lt;/strong&gt;).  This is much easier than navigating through a selection list and checking the items you want (don't want).&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;Of course, there is a lot more functionality within Excel OLAP pivottable than I have highlighted above, but you can see that there is quite a lot of power hidden within the tool.&lt;/p&gt;&lt;p&gt;For online real-time OLAP, data mining, Excel Services, PerformancePoint demonstrations goto &lt;a href="http://richardlees.com.au/Sites/Demonstrations"&gt;http://RichardLees.com.au/Sites/Demonstrations&lt;/a&gt; &lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3689647052791307812-8047025929592803200?l=richardlees.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://richardlees.blogspot.com/feeds/8047025929592803200/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3689647052791307812&amp;postID=8047025929592803200' title='7 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3689647052791307812/posts/default/8047025929592803200'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3689647052791307812/posts/default/8047025929592803200'/><link rel='alternate' type='text/html' href='http://richardlees.blogspot.com/2009/10/excel-2007-olap-pivottable-tips.html' title='Excel 2007 OLAP PivotTable Tips'/><author><name>Richard Lees</name><uri>http://www.blogger.com/profile/05671716466559973540</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='31' height='32' src='http://2.bp.blogspot.com/-AdeWScnQel8/Thj9ePsjpQI/AAAAAAAAAew/XnIn3xt4a0s/s220/RichardUni.png'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://2.bp.blogspot.com/_SHRRgijB18E/StlCXqvQEFI/AAAAAAAAAUM/3eikVMPKRFY/s72-c/RefreshOnOpening.JPG' height='72' width='72'/><thr:total>7</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3689647052791307812.post-449045212570610985</id><published>2009-10-11T11:38:00.002+11:00</published><updated>2009-10-12T16:37:31.273+11:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Analysis Services'/><category scheme='http://www.blogger.com/atom/ns#' term='Excel 2007'/><title type='text'>What's preventing Excel from being the ubiquitous cube browser</title><content type='html'>I have said earlier that Microsoft’s Excel 2007 is not the ubiquitous cube browser that it should be. What’s wrong with it? Well nothing is really wrong with it, but there are several features, that I would expect from a product that is 10 years old. With OLAP services first release 1998, there was an update for Excel PivotTable. As I understand it that first version was largely the result of one great developer (TC). However, since that release, the upgrades have been unimpressive.&lt;a href="http://4.bp.blogspot.com/_SHRRgijB18E/StEnMpburcI/AAAAAAAAATU/dD1mhGve7nY/s1600-h/Excel1.JPG"&gt;&lt;img style="MARGIN: 0px 0px 10px 10px; WIDTH: 320px; FLOAT: right; HEIGHT: 215px; CURSOR: hand" id="BLOGGER_PHOTO_ID_5391133327173266882" border="0" alt="" src="http://4.bp.blogspot.com/_SHRRgijB18E/StEnMpburcI/AAAAAAAAATU/dD1mhGve7nY/s320/Excel1.JPG" /&gt;&lt;/a&gt; Excel 2007 is a great improvement on Excel 2003, but it still has a long way to catch up to the sophistication that is available in Analysis Services. Here are a few of the things I would like to see in Excel&lt;br /&gt;&lt;br /&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;&lt;ul&gt;&lt;li&gt;Full support for calculated members. They are partially supported, calculated measures are OK, but other calculated members are treated as second class citizens.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Ability to create calculated members that are stored within Excel, and available like any other calculated member for that user.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Support for 3 dimensional queries. Currently, a user can only ask for a two dimensional report. Excel, is inherently a 3 dimensional space when you include worksheets. So I would like to be able to create a PivotTable with a dimension placed on the worksheet axis.&lt;a href="http://3.bp.blogspot.com/_SHRRgijB18E/StEnM7WUeNI/AAAAAAAAATc/uZ-ivG38JYg/s1600-h/Excel0.JPG"&gt;&lt;img style="MARGIN: 0px 0px 10px 10px; WIDTH: 320px; FLOAT: right; HEIGHT: 250px; CURSOR: hand" id="BLOGGER_PHOTO_ID_5391133331982416082" border="0" alt="" src="http://3.bp.blogspot.com/_SHRRgijB18E/StEnM7WUeNI/AAAAAAAAATc/uZ-ivG38JYg/s320/Excel0.JPG" /&gt;&lt;/a&gt;&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Support for naive users who open up a dimension member list with a million members. For example, ask them if they really want the one million rows (columns) and perhaps defaulting to a top 100 list.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;More optimised MDX. Sometimes I despair with the performance of pivottable queries, and when I look at the MDX I see that it could run quickly with more efficient MDX.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Ability to see properties of ancestors&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Better integration into Excel spreadsheets. For example, ability to format measures and PivotTable to remember these, even after drill down, nesting etc.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Ability to define sets.&lt;/li&gt;&lt;a href="http://2.bp.blogspot.com/_SHRRgijB18E/StEmNq4svQI/AAAAAAAAATE/cwb1ZX5EjCM/s1600-h/Excel2.JPG"&gt;&lt;/a&gt;&lt;br /&gt;&lt;li&gt;Eposure to some set functionality in a gui fashion. For example, it might allow the user to select a crossjoin on rows with an exception function to exclude some members based on a filter criteria.&lt;/li&gt;&lt;a href="http://3.bp.blogspot.com/_SHRRgijB18E/StEnMAzbC9I/AAAAAAAAATM/CzX_83LaZGE/s1600-h/Excel2.JPG"&gt;&lt;img style="MARGIN: 0px 0px 10px 10px; WIDTH: 320px; FLOAT: right; HEIGHT: 250px; CURSOR: hand" id="BLOGGER_PHOTO_ID_5391133316266789842" border="0" alt="" src="http://3.bp.blogspot.com/_SHRRgijB18E/StEnMAzbC9I/AAAAAAAAATM/CzX_83LaZGE/s320/Excel2.JPG" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;li&gt;Ability to right click on members to “select only” or “deselect”&lt;/li&gt;&lt;br /&gt;&lt;li&gt;More control over charts. For example mixing the dimensionality of measures so that one measure is cumulative on series and the other is unrelated to the series.&lt;/li&gt;&lt;/ul&gt;Don’t get me wrong, even with this wish-list, I am a great fan of Excel PivotTable. As a rich client cube browser, it is still my favourite. It’s just that it isn’t keeping up to Analysis Services, which is truly a market leader in the OLAP field. In a later blog, I will write about my favourite features of Excel pivottable, many of which are not immediately obvious.&lt;br /&gt;&lt;br /&gt;&lt;div&gt;For real-time OLAP, Data Mining, Excel Services and related demonstrations goto &lt;a href="http://richardlees.com.au/Sites/Demonstrations"&gt;http://RichardLees.com.au/Sites/Demonstrations&lt;/a&gt; &lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3689647052791307812-449045212570610985?l=richardlees.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://richardlees.blogspot.com/feeds/449045212570610985/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3689647052791307812&amp;postID=449045212570610985' title='9 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3689647052791307812/posts/default/449045212570610985'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3689647052791307812/posts/default/449045212570610985'/><link rel='alternate' type='text/html' href='http://richardlees.blogspot.com/2009/10/whats-preventing-excel-from-being.html' title='What&apos;s preventing Excel from being the ubiquitous cube browser'/><author><name>Richard Lees</name><uri>http://www.blogger.com/profile/05671716466559973540</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='31' height='32' src='http://2.bp.blogspot.com/-AdeWScnQel8/Thj9ePsjpQI/AAAAAAAAAew/XnIn3xt4a0s/s220/RichardUni.png'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://4.bp.blogspot.com/_SHRRgijB18E/StEnMpburcI/AAAAAAAAATU/dD1mhGve7nY/s72-c/Excel1.JPG' height='72' width='72'/><thr:total>9</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3689647052791307812.post-7708283883392316580</id><published>2009-09-29T05:11:00.005+10:00</published><updated>2009-10-04T07:02:11.254+11:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='MDX'/><category scheme='http://www.blogger.com/atom/ns#' term='OLAP Performance'/><title type='text'>My Calculated Member is slow</title><content type='html'>&lt;a href="http://3.bp.blogspot.com/_SHRRgijB18E/SsetLssDWyI/AAAAAAAAASs/PBI9VkCk6aU/s1600-h/nonemptybehaviour.JPG"&gt;&lt;img style="float:right; margin:0 0 10px 10px;cursor:pointer; cursor:hand;width: 400px; height: 252px;" src="http://3.bp.blogspot.com/_SHRRgijB18E/SsetLssDWyI/AAAAAAAAASs/PBI9VkCk6aU/s400/nonemptybehaviour.JPG" border="0" alt=""id="BLOGGER_PHOTO_ID_5388465895658183458" /&gt;&lt;/a&gt;&lt;br /&gt;Have you written a calculated member that is performing very slowly, while the underlying physical measures are fast?  This could be a common cube query performance issue that has a very quick remedy.  &lt;br /&gt;&lt;br /&gt;Essentially, AS want to know when to treat this as an empty cell.  If you don't specify the non_empty_behaviour, AS will need to physically calculate all potential cells to determine which ones are actually emtpy.   With non_empty_behaviour specified, AS can avoid many calculations, since many blocks of cells are known to be empty.  The non_empty_behaviour attribute is appropriate for both SQL Server 2005 and SQL Server 2008.&lt;br /&gt;&lt;br /&gt;To remedy, all you need to do is specify the non_emtpy_behaviour for the calculated member.  &lt;br /&gt;&lt;br /&gt;Note, there are logic implications with non_empty_behaviour.  With the example in the picture (hits per web session) it is logical that I would want to return null when the sessions measure is null.  This would not be the case if I were calculating a YTD measure, since the measure might be null on the current day, but the calculation needs to add many other days that might not be null.&lt;br /&gt;&lt;br /&gt;For live OLAP and data mining demonstrations with real data see http://RichardLees.com.au/Sites/Demonstrations&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3689647052791307812-7708283883392316580?l=richardlees.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://richardlees.blogspot.com/feeds/7708283883392316580/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3689647052791307812&amp;postID=7708283883392316580' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3689647052791307812/posts/default/7708283883392316580'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3689647052791307812/posts/default/7708283883392316580'/><link rel='alternate' type='text/html' href='http://richardlees.blogspot.com/2009/09/my-calculated-member-is-slow.html' title='My Calculated Member is slow'/><author><name>Richard Lees</name><uri>http://www.blogger.com/profile/05671716466559973540</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='31' height='32' src='http://2.bp.blogspot.com/-AdeWScnQel8/Thj9ePsjpQI/AAAAAAAAAew/XnIn3xt4a0s/s220/RichardUni.png'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://3.bp.blogspot.com/_SHRRgijB18E/SsetLssDWyI/AAAAAAAAASs/PBI9VkCk6aU/s72-c/nonemptybehaviour.JPG' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3689647052791307812.post-4946802757724393589</id><published>2009-09-25T15:01:00.020+10:00</published><updated>2009-09-26T17:53:56.791+10:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Excel 2007'/><category scheme='http://www.blogger.com/atom/ns#' term='olap cubes'/><category scheme='http://www.blogger.com/atom/ns#' term='cube browsers'/><category scheme='http://www.blogger.com/atom/ns#' term='SSAS'/><category scheme='http://www.blogger.com/atom/ns#' term='PerformancePoint'/><title type='text'>Which cube browser for Microsoft OLAP</title><content type='html'>&lt;a href="http://EasternMining.com.au"&gt;&lt;img style="float:right; margin:0 0 10px 10px;cursor:pointer; cursor:hand;width: 200px; height: 188px;" src="http://3.bp.blogspot.com/_SHRRgijB18E/Sr2-63b0G2I/AAAAAAAAARk/oux50vSDE6M/s200/K1200S.jpg" border="0" alt=""id="BLOGGER_PHOTO_ID_5385670647927282530" /&gt;&lt;/a&gt;&lt;br /&gt;I am often asked which cube browser I recommend for Analysis Services cubes.  The answer is typically a few minutes long, since there isn't one ubiquitous cube browser that satisfies everyone.  It's a bit like asking someone which vehicle you recommend.  It really depends on what the purpose is and what you value.  A high performance sports car might be appropriate for some people, but would be inappropriate as a family car or to tow the boat to the coast.  For me, the best vehicle is a sports bike, but this would be inappropriate for most road users.  The same applies to cube browsers, some users need something very simple and easy, while others need sophistication and flexibility.  Having said that if Microsoft Excel had a few more features, it could quite easily be the ubiquitous browser.&lt;br /&gt;&lt;br /&gt;Two cube vehicles that complement each other well, unsurprisingly, are Microsoft Excel and SQL Server Reporting Services (RS).  Excel is very flexible, while RS can deliver tailored reports, with flexible parameters and easy links to drill down/across to other reports.  &lt;a href="http://3.bp.blogspot.com/_SHRRgijB18E/Sr3FYLOzcFI/AAAAAAAAASk/C63eBy8jluA/s1600-h/Excel.JPG"&gt;&lt;img style="float:right; margin:0 0 10px 10px;cursor:pointer; cursor:hand;width: 320px; height: 190px;" src="http://3.bp.blogspot.com/_SHRRgijB18E/Sr3FYLOzcFI/AAAAAAAAASk/C63eBy8jluA/s320/Excel.JPG" border="0" alt=""id="BLOGGER_PHOTO_ID_5385677748527394898" /&gt;&lt;/a&gt;&lt;br /&gt;In this way, RS is perfect for naive users that need easy to consume information with click-through for more detailed and associated information.  Note; RS does not support ad hoc cube browsing.   Microsoft also have PerformancePoint, which is a great tool for delivering visual data to a thin client.  It has functionality somewhere in-between Excel and RS and many features not available in either.  PerformancePoint supports (very rudimentary) ad hoc cube browsing, but it is very powerful at delivering visual and interactive data.  So PerformancePoint is ideal for presenting visual data to naive users, and for data savvy users there can be a great deal of “data flexibility”.  PerformancePoint can deliver this in a semi structured way, so that users don't drive a cube browser, rather, they drive a dashboard of their data.  There is a good example of rich PerformancePoint dashboard on &lt;a href="http://EasternMining.com.au/sites/Demonstrations/Shared%20Documents/SQL%20Server%20Database%20and%20OLAP%20Activity/SQL%20Server%20Activity.aspx"&gt;SQLS Live Dashboard&lt;/a&gt;.   Notice how you can click on scorecard items, and they act as filters on the charts.&lt;br /&gt;&lt;br /&gt;Another&lt;a href="http://easternmining.com.au/sites/Demonstrations/Pages/Perfmon%20Statistics%20Last%2060%20Minutes.aspx"&gt;&lt;img style="float:right; margin:0 0 10px 10px;cursor:pointer; cursor:hand;width: 320px; height: 242px;" src="http://2.bp.blogspot.com/_SHRRgijB18E/Sr3Bl_sD_hI/AAAAAAAAASM/gb6h8NBUy_8/s320/RS.JPG" border="0" alt=""id="BLOGGER_PHOTO_ID_5385673587900546578" /&gt;&lt;/a&gt; important cube browser (although not yet truly ad hoc) is Excel Services.  Excel Services is essentially an Excel spreadsheet accessed through a thin client, Internet Explorer.  Any spreadsheet can be published, even one that is attached to an OLAP cube, so it can be a useful vehicle to deliver live cube data.  There are a couple of good Excel Services examples on &lt;a href="http://EasternMining.com.au/sites/Demonstrations/Pages/NZCensus_Excel.aspx"&gt;NZ Census&lt;/a&gt;.  Notice how you can interact with the spreadsheet by drilling down hierarchies and applying filters.  Note too; how the data is live (you might want to switch to the weblogs spreadsheet).  However, in the current release of Excel Services you cannot change what is on rows, columns and filters.  I have many customers that love Excel Services, since it permits their Finance dept to design and publish reports entirely within Excel, and their report users always see live data.&lt;br /&gt;&lt;br /&gt;There are hundreds, if not thousands, of third party cube browsers.  They really aren't that hard to develop.&lt;a href="http://easternmining.com.au/sites/Demonstrations/Pages/NZCensus_Excel.aspx"&gt;&lt;img style="float:right; margin:0 0 10px 10px;cursor:pointer; cursor:hand;width: 320px; height: 246px;" src="http://1.bp.blogspot.com/_SHRRgijB18E/Sr3BOMutQWI/AAAAAAAAASE/sDC9f9LRVjg/s320/ExcelServices.JPG" border="0" alt=""id="BLOGGER_PHOTO_ID_5385673179084439906" /&gt;&lt;/a&gt;&lt;br /&gt;  I wrote the first thin client cube browser, ThinSlicer, which is still used by many organisations.  When I wrote the first version of ThinSlicer, in 1998, I thought that its shelf life would be short; surely Microsoft will offer a highly functional, low cost, thin client cube browser?  How wrong could I be?  It is now 10 years on, and the only ad hoc thin client cube browser we have from Microsoft is PerformancePoint, which while it has an ad hoc cube browser, it is not the reason anyone would buy it.  An example of a more functional thin client cube browser would be Report Portal.  This product supports quite strong ad hoc cube browsing and charting.   I must add that there are many other commercial cube browsers, and I really don’t want to list them, since the list and relative strengths are continually changing.&lt;br /&gt;&lt;br /&gt;If you are embarking on a data warehouse or business intelligence project, I would encourage you to leave the cube browser choice until quite late in the process.&lt;a href="http://easternmining.com.au/sites/Demonstrations/Shared%20Documents/SQL%20Server%20Database%20and%20OLAP%20Activity/SQL%20Server%20Activity.aspx"&gt;&lt;img style="float:right; margin:0 0 10px 10px;cursor:pointer; cursor:hand;width: 320px; height: 250px;" src="http://1.bp.blogspot.com/_SHRRgijB18E/Sr3B0wXuGiI/AAAAAAAAASU/KjHpfwsr06I/s320/PerformancePoint.JPG" border="0" alt=""id="BLOGGER_PHOTO_ID_5385673841486731810" /&gt;&lt;/a&gt;  (Just like you shouldn't purchase your production hardware until you are almost ready to deploy a solution.)  This is particularly true if you are not familiar with cubes and are not sure what you and your users will need.  Microsoft’s SQL Server is probably the best foundation for a data warehouse/business intelligence solution.  Particularly its OLAP cube and related technologies such as data mining.  I would encourage you to use Excel 2007 and Reporting Services (which is free with any version of SQL Server) as the initial cube delivery vehicles, and trial other products when, and if, you find the need.  If you are looking for better thin client visualisation, do try PerformancePoint and Excel Services before you engage with third party vendors.  These products are very useful, and they are likely to be greatly improved in future versions.  I should add that PerformancePoint dashboards will be incorporated into SharePoint MOSS in future, which makes a lot of sense to me.   One other consideration, if you are delivering to users outside of your Intranet/internal network is licensing.   SQL Server RS and Excel are quite easy to licence for use over the Internet.  Unfortunately, it’s not so easy with PerformancePoint and Excel Services.  If that is what you would like to do, I suggest you talk to your Microsoft reseller early on.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3689647052791307812-4946802757724393589?l=richardlees.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://richardlees.blogspot.com/feeds/4946802757724393589/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3689647052791307812&amp;postID=4946802757724393589' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3689647052791307812/posts/default/4946802757724393589'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3689647052791307812/posts/default/4946802757724393589'/><link rel='alternate' type='text/html' href='http://richardlees.blogspot.com/2009/09/which-cube-browser-for-microsoft-olap.html' title='Which cube browser for Microsoft OLAP'/><author><name>Richard Lees</name><uri>http://www.blogger.com/profile/05671716466559973540</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='31' height='32' src='http://2.bp.blogspot.com/-AdeWScnQel8/Thj9ePsjpQI/AAAAAAAAAew/XnIn3xt4a0s/s220/RichardUni.png'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://3.bp.blogspot.com/_SHRRgijB18E/Sr2-63b0G2I/AAAAAAAAARk/oux50vSDE6M/s72-c/K1200S.jpg' height='72' width='72'/><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3689647052791307812.post-5429033072809469746</id><published>2009-09-08T10:00:00.003+10:00</published><updated>2009-09-08T10:08:36.535+10:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='SQL Performance'/><category scheme='http://www.blogger.com/atom/ns#' term='DMV'/><title type='text'>Big Queries</title><content type='html'>&lt;a href="http://1.bp.blogspot.com/_SHRRgijB18E/SqWgAAUV7zI/AAAAAAAAARU/FXO3ot7lZuM/s1600-h/BigQueries.JPG"&gt;&lt;img style="float:right; margin:0 0 10px 10px;cursor:pointer; cursor:hand;width: 400px; height: 312px;" src="http://1.bp.blogspot.com/_SHRRgijB18E/SqWgAAUV7zI/AAAAAAAAARU/FXO3ot7lZuM/s400/BigQueries.JPG" border="0" alt=""id="BLOGGER_PHOTO_ID_5378881251909693234" /&gt;&lt;/a&gt;&lt;br /&gt;Have you ever wanted to know which queries are consuming the most resource on your server?  There is a quick and easy way to do this without starting up SQL Profiler.  Just execute the following SQL query.&lt;br /&gt;select top 50 &lt;br /&gt; execution_count,&lt;br /&gt; total_physical_reads,&lt;br /&gt; total_logical_reads,&lt;br /&gt; total_elapsed_time/1000 total_elapsed_time, &lt;br /&gt; total_elapsed_time/(1000*execution_count) Avg_Execution_Time,&lt;br /&gt; text&lt;br /&gt;--,* &lt;br /&gt;From Sys.dm_exec_query_stats qs&lt;br /&gt;CROSS APPLY sys.dm_exec_sql_text(QS.sql_handle)&lt;br /&gt;order by total_elapsed_time desc&lt;br /&gt;&lt;br /&gt;This query will list out the top 50 queries from you plan cache and show how much resource they have consumed since they have been in the cache.  Note, this is not an accurate way of measuring total resource consumption, since it depends on what and how long plans have been in the cache.  However, for a first cut tuning exercise, it is very helpful.  If you are looking at overall SQL Server (or machine) activity, they it doesn't matter if a small query is running inefficiently, if it executes infrequently.  The query above, provides total_elapsed_time, which is a good indication of which queries (in total) are consuming the most resource.&lt;br /&gt;&lt;br /&gt;What I would do in the first instance is copy the text from the query and run an explain, possibly with an execution, and look for tuning opportunities.  One of my mottos is that anything can have its performance improved, it's only a matter of how much effort is required.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3689647052791307812-5429033072809469746?l=richardlees.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://richardlees.blogspot.com/feeds/5429033072809469746/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3689647052791307812&amp;postID=5429033072809469746' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3689647052791307812/posts/default/5429033072809469746'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3689647052791307812/posts/default/5429033072809469746'/><link rel='alternate' type='text/html' href='http://richardlees.blogspot.com/2009/09/big-queries.html' title='Big Queries'/><author><name>Richard Lees</name><uri>http://www.blogger.com/profile/05671716466559973540</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='31' height='32' src='http://2.bp.blogspot.com/-AdeWScnQel8/Thj9ePsjpQI/AAAAAAAAAew/XnIn3xt4a0s/s220/RichardUni.png'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://1.bp.blogspot.com/_SHRRgijB18E/SqWgAAUV7zI/AAAAAAAAARU/FXO3ot7lZuM/s72-c/BigQueries.JPG' height='72' width='72'/><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3689647052791307812.post-5608158994490367946</id><published>2009-08-25T17:36:00.005+10:00</published><updated>2009-08-27T05:43:58.420+10:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='SSAS Performance'/><category scheme='http://www.blogger.com/atom/ns#' term='SSAS Partioning'/><title type='text'>SQL Server Analysis Services Partitions</title><content type='html'>Like relational partitions, SSAS partitions can be very useful in easing operations and, sometimes, improving performance.  Like any performance tuning, you really need to know the ramifications of the feature to get the best out of it.  &lt;br /&gt;&lt;br /&gt;Firstly, partitioning is an Enterprise version, so you will need to have SQL Server EE to make use of it.  Secondly, performance doesn't just improve by virtue of having partitions.  It depends on what is in the partitions, how many there are and how they are processed/queried.  SSAS knows the low and high dimension key attributes for each partition.  So if you have partitioned on date, and your query is looking for a particular date value (or range) SSAS will only query the partition (or partitions) relevant for your query.  Will your query run faster with partitioning in this instance?  Maybe, but not necessarily.  I would generally not anticipate an improvement in this scenario, unless there is some caching effect.  For example, if the current month (partition) is the "hottest" it will tend to stay in the data cache at the expense of the less queried months.  Note, this tends to happen in a large cube without partitions. &lt;br /&gt;&lt;br /&gt;On the other hand, if your query does not include a filter that aligns to the partitioning strategy, SSAS will break your query into multiple sub queries, with at least one sub-query in each partition.  This can allow the query to be executed in a parallel fashion, which might, but, again, not necessarily, improve performance.  If the parallel queries are essentially breaking the workload (as opposed to all performing the same workload for different slices) then there is likely to be a significant performance gain.  &lt;br /&gt;&lt;br /&gt;Just like a relational database, if there is only one query active at a time, parallelisation is more likely to achieve a performance gain than if there are many concurrent queries.  Many concurrent parallised queries can easily saturate a server and degrade overall performance.&lt;br /&gt;&lt;br /&gt;One of my favourite strategies for partitioning is not the classic one (which is to partition by date, say one partition per month/year) and it comes with a very high probability of performing well.  It is to partition into static and active partitions, with a large static partition(s) and a very small, but volatile, active partition.  If you want to keep your cube very close to real-time, evey time you incrementally update a partition the partition is washed out of the data cache.  It has to be washed out, since it is now out of date.  Unfortunately, if your cube is very large, the next few queries (including cache warmer) will be very slow, while they read the cube (and aggregations) off disk and into memory.  So a good potential strategy to to have all of the historical data in one partition and a second, very small, partition, with current data that is getting incrementally updated every minute or so.  That way, each minute, the large static partition will remain in memory.  Only the small dynamic partition will get washed out.  This is good, we are only washing out the small amount of data that we are changing.  This strategy requires an addition operational process, where the dynamic data is merged into the static partition.  There are a number of ways to do this, including merging partitions, or reprocessing the partitions during the quiet part of the day.   Also, this strategy does not negate the possibility of having a classic partitioning strategy also.  I.e your large partition may actually be a number of medium sized partitions, but the volatile data is isolated into one small partition.&lt;br /&gt;&lt;br /&gt;My second favourite reason for partitioning is, like relational partitions, it makes some operational procedures easier.  For example, with a classic partitioning strategy, to drop a month or year's worth of data, simply involves dropping one or more partitions.  No reprocessing require.&lt;br /&gt;&lt;br /&gt;My suggestion to you, if you are considering partitioning (and you should for large cubes) is to create a few test cubes and benchmark the performance with various partitioning strategies.  Monitor your IO, memory and cpu utilisation.  Ensure that your tests involve the caching (or washing out of cache) effect, and ensure that you are using data volumes (and hardware) that reflect what will be used in production.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3689647052791307812-5608158994490367946?l=richardlees.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://richardlees.blogspot.com/feeds/5608158994490367946/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3689647052791307812&amp;postID=5608158994490367946' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3689647052791307812/posts/default/5608158994490367946'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3689647052791307812/posts/default/5608158994490367946'/><link rel='alternate' type='text/html' href='http://richardlees.blogspot.com/2009/08/sql-server-analysis-services-partitions.html' title='SQL Server Analysis Services Partitions'/><author><name>Richard Lees</name><uri>http://www.blogger.com/profile/05671716466559973540</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='31' height='32' src='http://2.bp.blogspot.com/-AdeWScnQel8/Thj9ePsjpQI/AAAAAAAAAew/XnIn3xt4a0s/s220/RichardUni.png'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3689647052791307812.post-2177713740074897273</id><published>2009-08-24T06:34:00.007+10:00</published><updated>2009-08-24T19:04:30.429+10:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='MDX'/><category scheme='http://www.blogger.com/atom/ns#' term='Dynamic Reports'/><category scheme='http://www.blogger.com/atom/ns#' term='SSRS'/><title type='text'>Dynamic OLAP Reports</title><content type='html'>The specification from a customer's BI solution, that I just completed, included 3 reports.  (The project was really about designing a high performing cube with a hundreds of millions of facts, over 20 real dimensions and updates every 10 minutes.)  I love those sort of challenges.  Anyway, the 3 reports were essentially the same Profit and Loss Statement at different levels of the business hierarchy.  The customer expected 3 reports, and the early BI developer created 3 reports in the PoC, however I created one report that could navigate up and down the business hierarchy with hyperlinks.  Needless to say, the customer was delighted to have one report and the flexibility to click up and down the business.  This is similar to a blog I made some time ago.  I encourage BI developers to use SQL Server Reporting Services Reports (SSRS) on OLAP cubes to create fast and linked reports so that the reports appear like an application to the end user.  &lt;br /&gt;&lt;br /&gt;Adding hyperlinks to another report in SSRS is really simple, you just right click on the text box, asking for &lt;strong&gt;text box properties&lt;/strong&gt; and clicking on &lt;strong&gt;Action Go to Report&lt;/strong&gt;.  The parameters need to be configured.  In my case, the parameter for the business unit is created using the current member's unique name and its parent's unique name.  So a tiny bit of MDX is required.  That brings me onto another of my soapbox themes.  BI developers should know MDX.  If your BI developer doesn't know MDX, he/she is unlikely to be able to design high performing cubes and will not know how to exploit all the available functionality.  it's a bit like asking someone to design a relational data warehouse for you, who doesn't know SQL very well. Good MDX skills are a predicate for good cube designs.&lt;br /&gt;&lt;br /&gt;I can't provide the example of my customer's report, but you can see examples of dynamic and linked reports on &lt;a href="http://RichardLees.com.au/Sites/Demonstrations"&gt;http://RichardLees.com.au/Sites/Demonstrations&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3689647052791307812-2177713740074897273?l=richardlees.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://richardlees.blogspot.com/feeds/2177713740074897273/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3689647052791307812&amp;postID=2177713740074897273' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3689647052791307812/posts/default/2177713740074897273'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3689647052791307812/posts/default/2177713740074897273'/><link rel='alternate' type='text/html' href='http://richardlees.blogspot.com/2009/08/dynamic-olap-reports.html' title='Dynamic OLAP Reports'/><author><name>Richard Lees</name><uri>http://www.blogger.com/profile/05671716466559973540</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='31' height='32' src='http://2.bp.blogspot.com/-AdeWScnQel8/Thj9ePsjpQI/AAAAAAAAAew/XnIn3xt4a0s/s220/RichardUni.png'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3689647052791307812.post-2974487307890263089</id><published>2009-07-23T12:15:00.007+10:00</published><updated>2009-07-23T12:32:42.970+10:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='SQL Server Performance'/><category scheme='http://www.blogger.com/atom/ns#' term='IO Performance'/><title type='text'>Who's Using Resources on My Server?</title><content type='html'>&lt;a href="http://richardlees.com.au/sites/Demonstrations/Shared%20Documents/Windows%20Performance%20Monitor/Process.aspx"&gt;&lt;img style="float:right; margin:0 0 10px 10px;cursor:pointer; cursor:hand;width: 320px; height: 283px;" src="http://1.bp.blogspot.com/_SHRRgijB18E/SmfLTRBCBRI/AAAAAAAAARE/6cD7Z24Ngjs/s320/Perfmon2.JPG" border="0" alt=""id="BLOGGER_PHOTO_ID_5361477413253547282" /&gt;&lt;/a&gt;One of my favourite analytical pages is &lt;a href="http://richardlees.com.au/sites/Demonstrations/Shared%20Documents/Windows%20Performance%20Monitor/Process.aspx"&gt;Perfmon by Process&lt;/a&gt;.  This page shows me which Windows processes are using my critical resources, such as &lt;strong&gt;Processor, IO, page file and virtual bytes&lt;/strong&gt;.  When you click on this link, it will show you the critical resources and for each of the last three hours, it shows you which process has been the biggest consumer.  The two charts on the right show you the top 10 consumers for the critical resource you have selected on the left (scorecard).  By default, the selection is &lt;strong&gt;%Processor Time&lt;/strong&gt;, but you can click on others.  My favourite is &lt;strong&gt;IO Data Bytes/sec&lt;/strong&gt;, since this tends to be the most critical resource in a large database system.  Try clicking on &lt;strong&gt;IO Data Bytes/sec&lt;/strong&gt; and other counters.  See how the grraphs on the right change dynamically, and will show you real time analysis for the last 60 minutes and last 60 hours. &lt;br /&gt;&lt;a href="http://richardlees.com.au/sites/Demonstrations/Shared%20Documents/Windows%20Performance%20Monitor/Process.aspx"&gt;&lt;img style="float:right; margin:0 0 10px 10px;cursor:pointer; cursor:hand;width: 320px; height: 288px;" src="http://2.bp.blogspot.com/_SHRRgijB18E/SmfLcjwC3JI/AAAAAAAAARM/hjrI8vz08JU/s320/Perfmon1.JPG" border="0" alt=""id="BLOGGER_PHOTO_ID_5361477572901395602" /&gt;&lt;/a&gt;&lt;br /&gt;This is just a demonstration.   If you have a large production system, do you have such analytical tools available to you?  If you don't, I would suggest that you are operating blind, and that you should get some.  The toolset I have created uses nothing but SQL Server 2005/2008 and PerformancePoint (for visualisation).  In fact, it's so simple, I would like Microsoft to have something like it available as an optional install from SQL Server.&lt;br /&gt;&lt;br /&gt;There are lots more real-time demonstrations on &lt;a href="http://RichardLees.com.au/Sites/Demonstrations"&gt;http://RichardLees.com.au&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3689647052791307812-2974487307890263089?l=richardlees.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://richardlees.blogspot.com/feeds/2974487307890263089/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3689647052791307812&amp;postID=2974487307890263089' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3689647052791307812/posts/default/2974487307890263089'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3689647052791307812/posts/default/2974487307890263089'/><link rel='alternate' type='text/html' href='http://richardlees.blogspot.com/2009/07/whos-using-resources-on-my-server.html' title='Who&apos;s Using Resources on My Server?'/><author><name>Richard Lees</name><uri>http://www.blogger.com/profile/05671716466559973540</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='31' height='32' src='http://2.bp.blogspot.com/-AdeWScnQel8/Thj9ePsjpQI/AAAAAAAAAew/XnIn3xt4a0s/s220/RichardUni.png'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://1.bp.blogspot.com/_SHRRgijB18E/SmfLTRBCBRI/AAAAAAAAARE/6cD7Z24Ngjs/s72-c/Perfmon2.JPG' height='72' width='72'/><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3689647052791307812.post-783263758435286093</id><published>2009-07-22T19:06:00.006+10:00</published><updated>2009-07-22T20:18:04.005+10:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='SQL Server 2008'/><category scheme='http://www.blogger.com/atom/ns#' term='IO Performance'/><title type='text'>Instant File Initialization for SQL Server 2008 (and 2005)</title><content type='html'>&lt;a href="http://2.bp.blogspot.com/_SHRRgijB18E/Smbb4fR8EKI/AAAAAAAAAQs/2oNpX81JNRI/s1600-h/Perform+Volume+Management+Tasks.JPG"&gt;&lt;img style="float:right; margin:0 0 10px 10px;cursor:pointer; cursor:hand;width: 400px; height: 238px;" src="http://2.bp.blogspot.com/_SHRRgijB18E/Smbb4fR8EKI/AAAAAAAAAQs/2oNpX81JNRI/s400/Perform+Volume+Management+Tasks.JPG" border="0" alt=""id="BLOGGER_PHOTO_ID_5361214169947050146" /&gt;&lt;/a&gt;I will do anything to reduce the IO load on large databases, which is why I am a great fan of compression in SQL Server 2008.  However, another way of reducing IO is to ask SQL Server to format new data extents without writing zeroes out to all pages.  If your database takes a new extent (by default it is 10% of the file size, which typically isn't good) SQL Server will want to write binary zeroes to all the pages to ensure that some old data isn't hidden in your database.  SQL Server does not need the binary zeroes, it is done to ensure data security.  Someone else may have deleted a file and perhaps they don’t not want you seeing the data.&lt;br /&gt;&lt;br /&gt;Very often, the data security is not an issue and you would like to avoid the writing of binary zeroes.  This can be achieved (on NTFS drives) by ensuring that the account running SQL Server has SE_MANAGE_VOLUME_NAME privilege.  You can grant this privilege in User Rights Assignment on Windows 2003, XP or above.  To do this, grant the SQL Service account &lt;strong&gt;Perform volume maintenance tasks &lt;/strong&gt;local security rights.   Alternatively, if the SQL service account has administrator privileges, it will automatically have this privilege.  That's all you need to do to avoid having SQL Server write all those binary zeros.  By the way, SQL Server always needs to write binary zeroes to the log files, we are only avoiding binary zeroes on the data files.&lt;br /&gt;&lt;br /&gt;If you don't want to grant this privilege to the SQL service account, and you want to avoid the IO load during peak times, I suggest that you extend (manually or automatically) your datasets during an off peak times (i.e. before SQL does it for you).&lt;br /&gt;&lt;br /&gt;One of the most noticeable tasks affected by binary zeros is database restores.  If you watch the restore progress, it will typically not move off 0% until it has written out the entire data files with binary zeroes.   After writing every data page, it will then write the backup files over the data pages, during which time, you will see the progress percent increase.  So writing binary zeroes can approximately double full database restore times.&lt;br /&gt;&lt;br /&gt;Hope that helps you reduce IO load and increase SQL Server performance.&lt;br /&gt;&lt;br /&gt;For real-time SQL demonstrations, including Perfmon, which is writing over 300 records every second, see http://RichardLees.com.au/Sites/Demonstrations&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3689647052791307812-783263758435286093?l=richardlees.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://richardlees.blogspot.com/feeds/783263758435286093/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3689647052791307812&amp;postID=783263758435286093' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3689647052791307812/posts/default/783263758435286093'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3689647052791307812/posts/default/783263758435286093'/><link rel='alternate' type='text/html' href='http://richardlees.blogspot.com/2009/07/instant-file-initialization-for-sql.html' title='Instant File Initialization for SQL Server 2008 (and 2005)'/><author><name>Richard Lees</name><uri>http://www.blogger.com/profile/05671716466559973540</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='31' height='32' src='http://2.bp.blogspot.com/-AdeWScnQel8/Thj9ePsjpQI/AAAAAAAAAew/XnIn3xt4a0s/s220/RichardUni.png'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://2.bp.blogspot.com/_SHRRgijB18E/Smbb4fR8EKI/AAAAAAAAAQs/2oNpX81JNRI/s72-c/Perform+Volume+Management+Tasks.JPG' height='72' width='72'/><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3689647052791307812.post-6615374324947504204</id><published>2009-07-22T18:59:00.004+10:00</published><updated>2009-07-22T19:05:00.980+10:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Manly'/><category scheme='http://www.blogger.com/atom/ns#' term='unicycle'/><title type='text'>Spotted on SproutDaily.com</title><content type='html'>&lt;a href="http://1.bp.blogspot.com/_SHRRgijB18E/SmbV_5DJXAI/AAAAAAAAAQk/mblaLKN4oDI/s1600-h/Richard+Unicycling+Manly+Beach.JPG"&gt;&lt;img style="float:right; margin:0 0 10px 10px;cursor:pointer; cursor:hand;width: 400px; height: 267px;" src="http://1.bp.blogspot.com/_SHRRgijB18E/SmbV_5DJXAI/AAAAAAAAAQk/mblaLKN4oDI/s400/Richard+Unicycling+Manly+Beach.JPG" border="0" alt=""id="BLOGGER_PHOTO_ID_5361207700053646338" /&gt;&lt;/a&gt;&lt;br /&gt;Last week I was spotted by SproutDaily.com  Some of the best photographers in Sydney take pics around Manly Beach and post them out to everyone registered at SproutDaily.com.&lt;br /&gt;&lt;br /&gt;Just can't do anything in Manly without everyone knowing.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3689647052791307812-6615374324947504204?l=richardlees.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://richardlees.blogspot.com/feeds/6615374324947504204/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3689647052791307812&amp;postID=6615374324947504204' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3689647052791307812/posts/default/6615374324947504204'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3689647052791307812/posts/default/6615374324947504204'/><link rel='alternate' type='text/html' href='http://richardlees.blogspot.com/2009/07/spotted-on-sproutdailycom.html' title='Spotted on SproutDaily.com'/><author><name>Richard Lees</name><uri>http://www.blogger.com/profile/05671716466559973540</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='31' height='32' src='http://2.bp.blogspot.com/-AdeWScnQel8/Thj9ePsjpQI/AAAAAAAAAew/XnIn3xt4a0s/s220/RichardUni.png'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://1.bp.blogspot.com/_SHRRgijB18E/SmbV_5DJXAI/AAAAAAAAAQk/mblaLKN4oDI/s72-c/Richard+Unicycling+Manly+Beach.JPG' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3689647052791307812.post-6935713159487370335</id><published>2009-07-01T06:20:00.010+10:00</published><updated>2009-07-04T07:30:27.087+10:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='SSIS Packages'/><category scheme='http://www.blogger.com/atom/ns#' term='BI jobs'/><title type='text'>Restartable or Rerunnable SSIS Packages</title><content type='html'>&lt;a href="http://RichardLees.com.au/Sites/Demonstrations"&gt;&lt;img style="float:right; margin:0 0 10px 10px;cursor:pointer; cursor:hand;width: 200px; height: 131px;" src="http://1.bp.blogspot.com/_SHRRgijB18E/Sk53_u1ioPI/AAAAAAAAAQc/CeRVFL-CyZc/s200/Scorecard.JPG" border="0" alt=""id="BLOGGER_PHOTO_ID_5354348943778160882" /&gt;&lt;/a&gt;BI solutions need to be continually importing/updating data from external source systems.  I believe that a good BI solution will have automatically correcting load and process jobs.  What I mean by that is if a package fails (such as a power or system failure) the application will automatically correct itself next time it runs.  In the old days, we used to create "restartable" jobs, where, after a failure, the operator would manually intervene to restart the job with specific  parameters.  I think there is little excuse nowadays to have applications that require manual intervention after a failure.  They should be designed in such a way that they “know” where they are up to and continue, or reprocess.&lt;br /&gt;&lt;a href="http://1.bp.blogspot.com/_SHRRgijB18E/Sk50HSubQuI/AAAAAAAAAQM/fJdbmMSww3I/s1600-h/JobProperties.JPG"&gt;&lt;img style="float:right; margin:0 0 10px 10px;cursor:pointer; cursor:hand;width: 320px; height: 287px;" src="http://1.bp.blogspot.com/_SHRRgijB18E/Sk50HSubQuI/AAAAAAAAAQM/fJdbmMSww3I/s320/JobProperties.JPG" border="0" alt=""id="BLOGGER_PHOTO_ID_5354344675624567522" /&gt;&lt;/a&gt;&lt;br /&gt;For example, my Permon application (&lt;a href="http://RichardLees.com.au/sites/Demonstrations/Shared%20Documents/Windows%20Performance%20Monitor/Perfmon.aspx"&gt;Perfmon Dashboard&lt;/a&gt;) has a job that runs every 180 seconds to transform and load the new records, and incrementally process the OLAP cube.  Every now and again the machine is restarted for some reason and the Perfmon update process could be at any point when it is stopped.  However, the job will automatically run from the right place, next time it runs.  It will know whether there is 3 minutes or 10 minutes of data to process, by using high water marks in the database.  In this way, I have no operational overhead in running this application.  It just looks after itself.  It is the same for the weblogs BI solution on &lt;a href="http://RichardLees.com.au/Sites/Demonstrations"&gt;Weblogs Dashboard&lt;/a&gt;.   If your BI solution requires manual intervention after a failure or an error, get someone else in, like myself, to fix it up to run automatically without manual intervention.&lt;br /&gt;&lt;br /&gt;Now having said that, it is often useful to be able to pass parameters to an SSIS package.  &lt;em&gt;(I just needed to mention automatically correcting jobs above, as some developers make packages restartable,to help with manual correction, when it should be automatic.)&lt;/em&gt;   &lt;a href="http://4.bp.blogspot.com/_SHRRgijB18E/Sk53IajiCdI/AAAAAAAAAQU/FgUomYlRwwM/s1600-h/ssisPackage.JPG"&gt;&lt;img style="float:right; margin:0 0 10px 10px;cursor:pointer; cursor:hand;width: 200px; height: 156px;" src="http://4.bp.blogspot.com/_SHRRgijB18E/Sk53IajiCdI/AAAAAAAAAQU/FgUomYlRwwM/s200/ssisPackage.JPG" border="0" alt=""id="BLOGGER_PHOTO_ID_5354347993441110482" /&gt;&lt;/a&gt;For example, you might want to have the ability to ask the SSIS package to conditionally run one task.  This can be achieved quite easily by assigning the &lt;strong&gt;Disable &lt;/strong&gt;property on the task (container etc) using &lt;strong&gt;Expressions&lt;/strong&gt; on the task to set it to a parameter.  To pass the parameter it is simply a matter of adding &lt;strong&gt;/SET "\Package.Variables[DisableBackup]";False &lt;/strong&gt;to the cmd for the job in SQL Agent.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3689647052791307812-6935713159487370335?l=richardlees.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://richardlees.blogspot.com/feeds/6935713159487370335/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3689647052791307812&amp;postID=6935713159487370335' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3689647052791307812/posts/default/6935713159487370335'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3689647052791307812/posts/default/6935713159487370335'/><link rel='alternate' type='text/html' href='http://richardlees.blogspot.com/2009/07/restartable-or-rerunnable-ssis-packages.html' title='Restartable or Rerunnable SSIS Packages'/><author><name>Richard Lees</name><uri>http://www.blogger.com/profile/05671716466559973540</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='31' height='32' src='http://2.bp.blogspot.com/-AdeWScnQel8/Thj9ePsjpQI/AAAAAAAAAew/XnIn3xt4a0s/s220/RichardUni.png'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://1.bp.blogspot.com/_SHRRgijB18E/Sk53_u1ioPI/AAAAAAAAAQc/CeRVFL-CyZc/s72-c/Scorecard.JPG' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3689647052791307812.post-1681924106263017961</id><published>2009-06-26T08:24:00.013+10:00</published><updated>2009-06-27T20:11:11.668+10:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='MDX'/><category scheme='http://www.blogger.com/atom/ns#' term='Perfmon'/><category scheme='http://www.blogger.com/atom/ns#' term='olap cubes'/><category scheme='http://www.blogger.com/atom/ns#' term='SSRS'/><title type='text'>Reporting Services Tips for OLAP Cubes</title><content type='html'>&lt;a href="http://EasternMining.com.au/sites/Demonstrations/Pages/TopMemoryConsumers.aspx"&gt;&lt;img style="float:right; margin:0 0 10px 10px;cursor:pointer; cursor:hand;width: 320px; height: 230px;" src="http://1.bp.blogspot.com/_SHRRgijB18E/SkXJC8P-K0I/AAAAAAAAAQE/EZc-2MnW3eA/s320/TopMemoryConsumers.JPG" border="0" alt=""id="BLOGGER_PHOTO_ID_5351904784569740098" /&gt;&lt;/a&gt;Naturally I have an inclination to use Reporting Services on OLAP cubes.  My feeling is that reports should by and large be run from OLAP cube where they can be dynamic and fast.  SQL based reports are more ideally suited for "atomic data" reports (eg transaction lists) and some special reports requiring real time data.&lt;br /&gt;&lt;br /&gt;There are a few funnies when you get into OLAP reports with SSRS.  The SSRS wizard does a great job of starting the report and building the parameters you need.  However, before long you will need to edit the MDX that the wizard creates, to do things like taylor the parameter queries for cascading filters.  Here are a couple of tricks that I commonly use.&lt;br /&gt;&lt;br /&gt;In SSRS 2008, when you edit any MDX query, the designer will automatically rebuild (in other words wash out any MDX changes you have made) input parameter queries.  The simple work around for this frustrating behaviour is to remove the Dimension and Hierarchy values from the query's parameter list.&lt;br /&gt;&lt;a href="http://2.bp.blogspot.com/_SHRRgijB18E/SkXJCxJ7ssI/AAAAAAAAAP8/9HQPnlQiIhE/s1600-h/QueryEditor.JPG"&gt;&lt;img style="float:right; margin:0 0 10px 10px;cursor:pointer; cursor:hand;width: 320px; height: 208px;" src="http://2.bp.blogspot.com/_SHRRgijB18E/SkXJCxJ7ssI/AAAAAAAAAP8/9HQPnlQiIhE/s320/QueryEditor.JPG" border="0" alt=""id="BLOGGER_PHOTO_ID_5351904781591622338" /&gt;&lt;/a&gt;&lt;br /&gt;You might want to use the SSRS calendar control for easy date picking, which is used to filter an OLAP date dimension.  This isn't handled automatically, but can be achieved with the following.  Change the parameter property to be Date/Time datatype.  That will ensure that SSRS uses the date picker.  For most reports you will want to dynamically specify the default.  This can be simply achieved by a single row OLAP query (for example the last day with data) or from today's date using the now() function.  If you use the now() function you can strip off the time with =cdate(format(now(),"yyyy-MM-dd")).  The trick now is to use the datetime parameter in your MDX query.  What you need to do is construct a full member name using the known dimension-hierarchy names and contruct the member key using the date.  The construction of the key will depend on what you have defined as the key of the date hierarchy.  If the key is a date, then the fully qualified member name will look something like this. [Date].[Calendar].[TheDate].&amp;[2009-06-26T00:00:00].  So to include the date parameter in your query you can include something like the following in your query strtomember("[Date].[Calendar].[TheDate].&amp;["+format(now(),"yyyy-MM-dd")+"T00:00:00]")  Just replace [Date] [Calendar] and [TheDate] with the dimension, hierarchy and level name from your dimension.  The MDX query tool in SQL Enterprise Manager will help by providing a sample member.  The date parameter might simply be used in the WHERE clause to filter the query or it might be part of a range specification etc.  It's a member specification and as such could be used anywhere in your query.&lt;br /&gt;&lt;br /&gt;Not so much of a tip, but more of an apology.  The MDX query editor in SSRS is a very poor text editor.  When I am editing an MDX query, I invariably cut and paste the query into and out of SQL Enterprise Manager Query tool.&lt;br /&gt;&lt;br /&gt;Here is an easy way to get a report line number.  =RunningValue(1, SUM, "Invoice_Data")  The line number can also be used for alternate line shading.&lt;br /&gt;&lt;br /&gt;Check the performance of all the MDX queries.  If any of them are not running fast enough, see if there is anything you can do to speed them up.  One classic way to improve performance and improve report usability is to filter out empty members.  &lt;br /&gt;&lt;br /&gt;Another classic way to improve performance is to change the use of any single select parameters from StrToSet() to StrToMember() and placed on the WHERE clause.&lt;br /&gt;&lt;br /&gt;Just like SQL reports.  Or perhaps more so, since OLAP reports are more likely to be run dynamically, link your reports together so that users can easily navigate down and across reports.  Essentially, making it easier for users to drill through to focus the salient data.  With SSRS reports on OLAP this can be achieved in such a way that users do not need to know anything about databases or even that a cube is behind the report.  They simply click on headings or numbers to drill into and across the data that interests them.  For example, click on the "Top Memory Consumers" picture above for an OLAP report.  The report is dynamic and close to real time.  Notice how you can click on any of the processes and get an analysis of that process.  Likewise, you can click on any of the counters in that report for a breakdown of the processes using that resource.  A good set of dynamic SSRS reports will appear more like an application to end users than a traditional list of static reports.&lt;br /&gt;&lt;br /&gt;One last tip is to verify that all the queries in your report definition are actually used.  Sometimes SSRS will create new queries for you when you edit parameters.  Or you might modify a report and remove a parameter. Do remember to take out the unused queries.  All queries appear to fire even if they aren't used.&lt;br /&gt;&lt;br /&gt;For examples of real time OLAP reports see http://RichardLees.com.au/Sites/Demonstrations&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3689647052791307812-1681924106263017961?l=richardlees.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://richardlees.blogspot.com/feeds/1681924106263017961/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3689647052791307812&amp;postID=1681924106263017961' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3689647052791307812/posts/default/1681924106263017961'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3689647052791307812/posts/default/1681924106263017961'/><link rel='alternate' type='text/html' href='http://richardlees.blogspot.com/2009/06/reporting-services-tips-for-olap-cubes.html' title='Reporting Services Tips for OLAP Cubes'/><author><name>Richard Lees</name><uri>http://www.blogger.com/profile/05671716466559973540</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='31' height='32' src='http://2.bp.blogspot.com/-AdeWScnQel8/Thj9ePsjpQI/AAAAAAAAAew/XnIn3xt4a0s/s220/RichardUni.png'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://1.bp.blogspot.com/_SHRRgijB18E/SkXJC8P-K0I/AAAAAAAAAQE/EZc-2MnW3eA/s72-c/TopMemoryConsumers.JPG' height='72' width='72'/><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3689647052791307812.post-3426465906238057149</id><published>2009-06-22T07:29:00.010+10:00</published><updated>2009-06-22T07:44:57.885+10:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Search engines'/><category scheme='http://www.blogger.com/atom/ns#' term='Bing.com'/><category scheme='http://www.blogger.com/atom/ns#' term='Web Analytics'/><title type='text'>Bing.com - Microsoft's new search engine</title><content type='html'>&lt;a href="http://4.bp.blogspot.com/_SHRRgijB18E/Sj6nfjkJbaI/AAAAAAAAAPs/i7lD7qulG6k/s1600-h/BingSearch.JPG"&gt;&lt;img style="float:right; margin:0 0 10px 10px;cursor:pointer; cursor:hand;width: 200px; height: 128px;" src="http://4.bp.blogspot.com/_SHRRgijB18E/Sj6nfjkJbaI/AAAAAAAAAPs/i7lD7qulG6k/s200/BingSearch.JPG" border="0" alt=""id="BLOGGER_PHOTO_ID_5349897567927365026" /&gt;&lt;/a&gt;&lt;br /&gt;Have you checked out Microsoft's beta search engine, &lt;a href="www.Bing.com"&gt;Bing.com&lt;/a&gt;?  It has some nice features and now look much more like Google.com.&lt;br /&gt;&lt;br /&gt;I have noticed that it is rising on my Top Referrers List.  Currently it is in the top 20 and rising.  For an up to date list see http://RichardLees.com.au/Sites/Demonstrations &lt;br /&gt;&lt;br /&gt;By the way, I count 1 referrer as one person clicking on a link from Bing.com to somewhere on http://RichardLees.com.au  So far this month (June) Bing.com has made 45 referrals compared to Google, several hundred, or blogspot.com with 1679.&lt;a href="http://richardLees.com.au/Sites/Demonstrations"&gt;&lt;img style="float:right; margin:0 0 10px 10px;cursor:pointer; cursor:hand;width: 241px; height: 400px;" src="http://2.bp.blogspot.com/_SHRRgijB18E/Sj6nofgwb6I/AAAAAAAAAP0/QhDCXWCqx7s/s400/TopReferrers.JPG" border="0" alt=""id="BLOGGER_PHOTO_ID_5349897721458225058" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;For real time web analytics see &lt;a href="http://RichardLees.com.au/Sites/Demonstrations"&gt;RichardLees.com.au&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3689647052791307812-3426465906238057149?l=richardlees.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://richardlees.blogspot.com/feeds/3426465906238057149/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3689647052791307812&amp;postID=3426465906238057149' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3689647052791307812/posts/default/3426465906238057149'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3689647052791307812/posts/default/3426465906238057149'/><link rel='alternate' type='text/html' href='http://richardlees.blogspot.com/2009/06/bingcom-microsofts-new-search-engine.html' title='Bing.com - Microsoft&apos;s new search engine'/><author><name>Richard Lees</name><uri>http://www.blogger.com/profile/05671716466559973540</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='31' height='32' src='http://2.bp.blogspot.com/-AdeWScnQel8/Thj9ePsjpQI/AAAAAAAAAew/XnIn3xt4a0s/s220/RichardUni.png'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://4.bp.blogspot.com/_SHRRgijB18E/Sj6nfjkJbaI/AAAAAAAAAPs/i7lD7qulG6k/s72-c/BingSearch.JPG' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3689647052791307812.post-1803507192476670472</id><published>2009-06-18T19:52:00.005+10:00</published><updated>2009-06-18T20:09:05.488+10:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Performancepoint Planning'/><category scheme='http://www.blogger.com/atom/ns#' term='Microsoft Source Code'/><title type='text'>Microsoft Making PerformancePoint Planning Source Code Available</title><content type='html'>I have just heard that Microsoft will be making PerformancePoint Planning software available to partners (http://www.microsoft.com/bi/partners/default.aspx) who were actively involved in evaluating/using the product, at no cost.&lt;br /&gt;&lt;br /&gt;This is great news.  PerformancePoint Planning was a great V1 product and I think it would have had a great future had Microsoft continued to invest in it.  It's a loss to Microsoft, MS partners and customers that the product was discontinued.  Let's face it every organisation does planning, and if the organisation is large, you do need software to manage the process.  I did blog at the time of the discontinuance announcement http://richardlees.blogspot.com/2009/02/microsoft-is-discontinuing.html &lt;br /&gt;&lt;br /&gt;I don't know if there are any restrictions on what the partners can do with the code, but I imagine/hope some company will turn it into a V2 commercial product.  &lt;br /&gt;&lt;br /&gt;I really think the corporate planning market has room for maturation with none of the incumbent vendors (Hyperion, Peoplesoft, Cognos etc.) providing a ubiquitous product.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3689647052791307812-1803507192476670472?l=richardlees.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://richardlees.blogspot.com/feeds/1803507192476670472/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3689647052791307812&amp;postID=1803507192476670472' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3689647052791307812/posts/default/1803507192476670472'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3689647052791307812/posts/default/1803507192476670472'/><link rel='alternate' type='text/html' href='http://richardlees.blogspot.com/2009/06/microsoft-making-performancepoint.html' title='Microsoft Making PerformancePoint Planning Source Code Available'/><author><name>Richard Lees</name><uri>http://www.blogger.com/profile/05671716466559973540</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='31' height='32' src='http://2.bp.blogspot.com/-AdeWScnQel8/Thj9ePsjpQI/AAAAAAAAAew/XnIn3xt4a0s/s220/RichardUni.png'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3689647052791307812.post-5308440120132424888</id><published>2009-06-15T07:54:00.011+10:00</published><updated>2009-06-18T20:53:40.571+10:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Temp Tables'/><category scheme='http://www.blogger.com/atom/ns#' term='SQL Server Performance'/><category scheme='http://www.blogger.com/atom/ns#' term='Product Function'/><title type='text'>Learn SQL - SELECT Product(column)</title><content type='html'>I have on occasion been quoted as suggesting that SQL code with temp tables can indicate the author lacks strong SQL skills. Consequently, I have been challenged to write single SQL statements to replace multiple statements stuck together with temp tables. One of these occasions was with Ron Soukup over a bottle of whisky, but that story deserves its own blog.&lt;br /&gt;&lt;br /&gt;On this occasion (sometime in 1999) I was challenged to write a query to get the product of a set of numbers.  On another bottle of whiskey, as it happens.  My friend wanted something like the SUM() function, but he wanted the product of a set of numbers rather than the sum. The business application was quite simple. His client, a financial organisation, needed to calculate compound interes for money on overnight deposit.  Each day will have a different market interest rate.&lt;br /&gt;&lt;br /&gt;This problem had an interesting solution using a couple of maths functions - Sum(), Exp() and Log(). Here is something like the SQL that I wrote for them.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:85%;color:#000099;"&gt;&lt;strong&gt;select exp(sum(LOG(OvernightInterestRate+1))) as CompoundRate &lt;br /&gt;from OvernightDeposits&lt;br /&gt;where TheDate Between '1999-07-23' and '2009-06-18'&lt;/strong&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Now that's really quite easy, and is much more efficient than putting the data into a temp table and reprocessing it. Having said that, it would be quite useful (and I thought quite easy) to include a Product() function in SQL Server.&lt;br /&gt;&lt;br /&gt;For real-time SQL, OLAP and Data Mining demonstrations, goto http://RichardLees.com.au/Sites/Demonstrations&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3689647052791307812-5308440120132424888?l=richardlees.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://richardlees.blogspot.com/feeds/5308440120132424888/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3689647052791307812&amp;postID=5308440120132424888' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3689647052791307812/posts/default/5308440120132424888'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3689647052791307812/posts/default/5308440120132424888'/><link rel='alternate' type='text/html' href='http://richardlees.blogspot.com/2009/06/learn-sql-select-productcolumn.html' title='Learn SQL - SELECT Product(column)'/><author><name>Richard Lees</name><uri>http://www.blogger.com/profile/05671716466559973540</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='31' height='32' src='http://2.bp.blogspot.com/-AdeWScnQel8/Thj9ePsjpQI/AAAAAAAAAew/XnIn3xt4a0s/s220/RichardUni.png'/></author><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3689647052791307812.post-4131682426027067989</id><published>2009-06-14T14:13:00.013+10:00</published><updated>2009-06-14T14:34:22.972+10:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='SQL Performance'/><category scheme='http://www.blogger.com/atom/ns#' term='SQL Syntax'/><title type='text'>Know SQL - Predicates on the WHERE or OUTER JOIN Clauses</title><content type='html'>Writing good SQL is fundamental in a high performing application free of bugs.  SQL has been around as the standard database query language since it left QBE by the roadside in the 1980's, and the core syntax hasn't changed much.   Database developers (of any rdbms) should fully understand all ANSI aspects of the language.  &lt;br /&gt;&lt;br /&gt;I was with some developers recently who hadn't appreciated the difference between a filter predicate on a &lt;strong&gt;&lt;span style="font-size:85%;color:#000099;"&gt;WHERE &lt;/strong&gt;&lt;/span&gt;and a &lt;span style="font-size:85%;color:#000099;"&gt;&lt;strong&gt;LEFT OUTER JOIN &lt;/strong&gt;&lt;/span&gt;clause.  Essentially the difference is whether the predicate is working on the join or on the resultset.  If a predicate is on the right side table of a &lt;span style="font-size:85%;color:#000099;"&gt;&lt;strong&gt;LEFT OUTER JOIN&lt;/strong&gt;&lt;/span&gt; clause, then it will work on the right hand records only.  If the same predicate is on the &lt;span style="font-size:85%;color:#000099;"&gt;&lt;strong&gt;WHERE&lt;/strong&gt;&lt;/span&gt; clause, the entire record (left and right side) will be filtered out.  This can affect the number of records returned.&lt;br /&gt; &lt;br /&gt;Here is an example of the difference.  The first query has a filter on the &lt;span style="font-size:85%;color:#000099;"&gt;&lt;strong&gt;WHERE&lt;/strong&gt;&lt;/span&gt; clause, so that any objects joined with columns that have a &lt;span style="font-size:85%;color:#000099;"&gt;&lt;strong&gt;scale&lt;&gt;0&lt;/strong&gt;&lt;/span&gt; will be entirely filtered out.  That is, not all objects with a name like &lt;span style="font-size:85%;color:#000099;"&gt;&lt;strong&gt;'%sys%'&lt;/strong&gt;&lt;/span&gt; will be returned.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:85%;color:#000099;"&gt;&lt;strong&gt;   Select o.* from sys.objects o&lt;br /&gt;      left outer join sys.columns c on o.object_id=c.object_id &lt;br /&gt;      where o.name like '%sys%'&lt;br /&gt;        &lt;/span&gt;&lt;span style="font-size:85%;color:#ff0000;"&gt;and c.scale &lt;&gt; 0&lt;/strong&gt;&lt;/span&gt;&lt;br /&gt; &lt;br /&gt;This second query has had the columns predicate moved to the &lt;span style="font-size:85%;color:#000099;"&gt;&lt;strong&gt;LEFT OUTER JOIN &lt;/strong&gt;&lt;/span&gt;clause.  This is logically very different since the predicate &lt;span style="font-size:85%;color:#000099;"&gt;&lt;strong&gt;scale&lt;&gt;0&lt;/strong&gt;&lt;/span&gt; is working on the join.  In this query all objects with a name like '%sys%' will be returned.  It’s just a matter of whether they are joined with columns.&lt;br /&gt;&lt;span style="font-size:85%;color:#000099;"&gt;&lt;strong&gt;   Select o.* from sys.objects o&lt;br /&gt;     left outer join sys.columns c on o.object_id=c.object_id &lt;br /&gt;          &lt;/span&gt;&lt;span style="font-size:85%;color:#ff0000;"&gt;and c.scale &lt;&gt; 0&lt;/span&gt;&lt;span style="font-size:85%;color:#000099;"&gt;&lt;br /&gt;     where o.name like '%sys%'&lt;/strong&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;I have used &lt;span style="font-size:85%;color:#000099;"&gt;&lt;strong&gt;sys.objects &lt;/strong&gt;&lt;/span&gt;and &lt;span style="font-size:85%;color:#000099;"&gt;&lt;strong&gt;sys.columns&lt;/strong&gt;&lt;/span&gt;, since they are tables (well views actually, but that doesn't matter) in the &lt;span style="font-size:85%;color:#000099;"&gt;&lt;strong&gt;master &lt;/strong&gt;&lt;/span&gt;database (of either SQL Server 2005 or SQL Server 2008) so you can cut and paste the queries into your query editor and visualise the difference.&lt;br /&gt;&lt;br /&gt;This is just one subtlety of the SQL language.  If it is news to you and you are a database developer, I suggest that you spend some time learning the SQL language more thoroughly.  The good news is that any time you invest in the SQL language will be useful to you for a long time, since it does not appear to threatened by a new language anytime soon.  (As much as I love MDX, I can't see it replacing SQL in the rdbms.)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3689647052791307812-4131682426027067989?l=richardlees.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://richardlees.blogspot.com/feeds/4131682426027067989/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3689647052791307812&amp;postID=4131682426027067989' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3689647052791307812/posts/default/4131682426027067989'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3689647052791307812/posts/default/4131682426027067989'/><link rel='alternate' type='text/html' href='http://richardlees.blogspot.com/2009/06/know-sql-predicates-on-where-or-outer.html' title='Know SQL - Predicates on the WHERE or OUTER JOIN Clauses'/><author><name>Richard Lees</name><uri>http://www.blogger.com/profile/05671716466559973540</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='31' height='32' src='http://2.bp.blogspot.com/-AdeWScnQel8/Thj9ePsjpQI/AAAAAAAAAew/XnIn3xt4a0s/s220/RichardUni.png'/></author><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3689647052791307812.post-5641304960614520362</id><published>2009-06-13T13:36:00.005+10:00</published><updated>2009-06-13T14:24:35.202+10:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='web footprints'/><category scheme='http://www.blogger.com/atom/ns#' term='Web Analytics'/><category scheme='http://www.blogger.com/atom/ns#' term='browsing history'/><title type='text'>Leaving Footprints All Over the Internet</title><content type='html'>Someone recently asked me if my web analytics dashboard (&lt;a href="http://richardlees.com.au/Sites/Demonstrations"&gt;&lt;span style="font-size:85%;"&gt;http://RichardLees.com.au/Sites/Demonstrations&lt;/span&gt;&lt;/a&gt;) records information on where they had been on my site. The answer is an emphatic Yes. &lt;a href="http://1.bp.blogspot.com/_SHRRgijB18E/SjMnY3Sk1KI/AAAAAAAAAPM/FgC535x49ts/s1600-h/OneIP_browser.JPG"&gt;&lt;img style="MARGIN: 0px 0px 10px 10px; WIDTH: 400px; FLOAT: right; HEIGHT: 279px; CURSOR: hand" id="BLOGGER_PHOTO_ID_5346660490731115682" border="0" alt="" src="http://1.bp.blogspot.com/_SHRRgijB18E/SjMnY3Sk1KI/AAAAAAAAAPM/FgC535x49ts/s400/OneIP_browser.JPG" /&gt;&lt;/a&gt;Not that my site does anything special in the logging department. I am just using Windows IIS and by default it will log every http request. The only extra thing I do is that I put this data in a cube so that it is easy to query and visualise. You should remember that everytime you browse to a page on the internet, there is probably a log on that server recording your IP address, what you requested, date, time etc. And there will often be additional information such as your browser configuration, even the operating system and version number. If you are accessing the internet via a proxy server, then your IP address is shared amongst other proxy server users so you are somewhat camouflaged. But if you are using a proxy server, then you should consider the proxy server is logging every request you make over the internet, and possibly all the intranet browsing too.&lt;br /&gt;&lt;br /&gt;So, as I was saying, my site puts all the web logs into a cube so that you can visualise all the activity on my site, even what I do. I built it quite simply as a demonstration of OLAP technology.&lt;br /&gt;&lt;br /&gt;You can ad hoc query the activity on my site using ThinSlicer or PerformancePoint. Both of these tools are thin client, only requiring IE6 (or above) and there are no client controls. So if you have been to my site (anything on &lt;a href="http://richardlees.com.au/"&gt;http://richardlees.com.au/&lt;/a&gt;) then you will be able to browse the cube and see where you have been and what resources you used on my server at the time. Simply go to the &lt;strong&gt;Interactive Chart Grid&lt;/strong&gt; on &lt;a href="http://richardlees.com.au/Sites/Demonstrations"&gt;&lt;span style="font-size:85%;"&gt;http://RichardLees.com.au/Sites/Demonstrations&lt;/span&gt;&lt;/a&gt; and navigate to your IP address and display the requests you have made. This is using Microsoft's PerformancePoint dashboard technology, which takes a little getting used to, but it is a fully functional cube browser. For example, the picture above is a query I made, drilling into IP address 84.151.222.71, which happens to be in Munich, Germany.  I then drilled into .jpg files to see what pictures had been downloaded.  If you don't know your IP address, you can drill down to your geographic location and select the IP address active at the time you were on the site. You could even browse to my site requesting a resource that does not exist, such as HelenClark.jpg, since you will be the only person requesting this resource, you will be able to drill into this resource and list out your own IP address etc. You could then pivot on the IP address and see what else you requested. The cube is available for continuous querying, even though it is being updated with new logs every 15 minutes or so.&lt;br /&gt;&lt;br /&gt;Please note, I do not use these web logs for anything other than providing a demonstration of OLAP and data mining technologies. There is no reason to feel paranoid about what I store on my server, but it might make you think about what information other web sites have about your activity.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3689647052791307812-5641304960614520362?l=richardlees.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://richardlees.blogspot.com/feeds/5641304960614520362/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3689647052791307812&amp;postID=5641304960614520362' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3689647052791307812/posts/default/5641304960614520362'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3689647052791307812/posts/default/5641304960614520362'/><link rel='alternate' type='text/html' href='http://richardlees.blogspot.com/2009/06/leaving-your-footprints-on-internet.html' title='Leaving Footprints All Over the Internet'/><author><name>Richard Lees</name><uri>http://www.blogger.com/profile/05671716466559973540</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='31' height='32' src='http://2.bp.blogspot.com/-AdeWScnQel8/Thj9ePsjpQI/AAAAAAAAAew/XnIn3xt4a0s/s220/RichardUni.png'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://1.bp.blogspot.com/_SHRRgijB18E/SjMnY3Sk1KI/AAAAAAAAAPM/FgC535x49ts/s72-c/OneIP_browser.JPG' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3689647052791307812.post-8174723806663633746</id><published>2009-06-08T11:27:00.008+10:00</published><updated>2009-06-08T11:57:22.778+10:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Search engines'/><category scheme='http://www.blogger.com/atom/ns#' term='Altavista'/><category scheme='http://www.blogger.com/atom/ns#' term='Referrals'/><category scheme='http://www.blogger.com/atom/ns#' term='real-time analytics'/><title type='text'>Altavista is Back in Action</title><content type='html'>&lt;a href="http://easternmining.com.au/sites/Demonstrations/Pages/TopReferrers.aspx"&gt;&lt;img style="MARGIN: 0px 0px 10px 10px; WIDTH: 400px; FLOAT: right; HEIGHT: 220px; CURSOR: hand" id="BLOGGER_PHOTO_ID_5344769579719986146" border="0" alt="" src="http://2.bp.blogspot.com/_SHRRgijB18E/SixvnW2GC-I/AAAAAAAAAPE/w0n49pnqbLE/s400/AltavistaReferrals1.JPG" /&gt;&lt;/a&gt;Anyone who was using the internet in the 1990s, like me, will fondly remember Altavista.com, the best search engine of that era.&lt;br /&gt;&lt;br /&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;I have just noticed that it is back in action and referring people to my site! It seems to have replaced referrals that have come from Yahoo. I guess that shouldn't be so surprising since Yahoo acquired Altavista in a roundabout way via Overture Services.&lt;br /&gt;&lt;br /&gt;What is interesting to me is that altavista.com has come back and that almost all of the Altavista referrals (on my site) have come from Chicargo, Illinois. Anyone know why might be?&lt;/div&gt;&lt;br /&gt;&lt;div&gt;I have attached a couple of reports, one is simply the Top Referrers Report from &lt;a href="http://richardlees.com.au/Sites/Demonstrations"&gt;&lt;span style="font-size:85%;"&gt;http://RichardLees.com.au/Sites/Demonstrations&lt;/span&gt;&lt;/a&gt; which shows altavista suddenly appearing in Maya 2009. The other I have created using ThinSlicer showing the geographic location of the altavista users. You can do the same remotely on my site. &lt;a href="http://4.bp.blogspot.com/_SHRRgijB18E/SixsFcPgi3I/AAAAAAAAAO0/-bY9OMd2U68/s1600-h/AltavistaReferrals.JPG"&gt;&lt;img style="MARGIN: 0px 0px 10px 10px; WIDTH: 400px; FLOAT: right; HEIGHT: 224px; CURSOR: hand" id="BLOGGER_PHOTO_ID_5344765698518322034" border="0" alt="" src="http://4.bp.blogspot.com/_SHRRgijB18E/SixsFcPgi3I/AAAAAAAAAO0/-bY9OMd2U68/s400/AltavistaReferrals.JPG" /&gt;&lt;/a&gt; Or, if you prefer, the default dashboard on &lt;a href="http://richardlees.com.au/Sites/Demonstrations"&gt;&lt;span style="font-size:85%;"&gt;http://RichardLees.com.au/Sites/Demonstrations&lt;/span&gt;&lt;/a&gt; has a top referrers grid at the bottom right of the dashboard, which is interactive, so you can drill down on Altavista or drill across to any other dimension.&lt;/div&gt;&lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;I find it interesting participating and watching the competition between the big search engines. From the stats on my site, Google.com is by far the largest, with live.com enjoying a steady workload. By the way, the home page on the dashboard above shows the top referrers, but only for the last 5 months. If you want to see a longer range, simply drill up on the time dimension to All, then drill back down to month and you will see all months.&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3689647052791307812-8174723806663633746?l=richardlees.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://richardlees.blogspot.com/feeds/8174723806663633746/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3689647052791307812&amp;postID=8174723806663633746' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3689647052791307812/posts/default/8174723806663633746'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3689647052791307812/posts/default/8174723806663633746'/><link rel='alternate' type='text/html' href='http://richardlees.blogspot.com/2009/06/altavista-is-back-in-action.html' title='Altavista is Back in Action'/><author><name>Richard Lees</name><uri>http://www.blogger.com/profile/05671716466559973540</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='31' height='32' src='http://2.bp.blogspot.com/-AdeWScnQel8/Thj9ePsjpQI/AAAAAAAAAew/XnIn3xt4a0s/s220/RichardUni.png'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://2.bp.blogspot.com/_SHRRgijB18E/SixvnW2GC-I/AAAAAAAAAPE/w0n49pnqbLE/s72-c/AltavistaReferrals1.JPG' height='72' width='72'/><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3689647052791307812.post-7515356944286695969</id><published>2009-06-06T16:46:00.007+10:00</published><updated>2009-06-14T13:50:09.812+10:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='olap cubes'/><category scheme='http://www.blogger.com/atom/ns#' term='web performance'/><category scheme='http://www.blogger.com/atom/ns#' term='sharepoint server 2007'/><category scheme='http://www.blogger.com/atom/ns#' term='jpg images'/><title type='text'>Resizing .jpg files for SharePoint Server 2007</title><content type='html'>&lt;div&gt;My internet site (&lt;a href="http://richardlees.com.au/"&gt;http://richardlees.com.au/&lt;/a&gt;) is hosted on Sharepoint Server 2007. &lt;a href="http://2.bp.blogspot.com/_SHRRgijB18E/SioVMYoQDAI/AAAAAAAAAOk/4aXlJfCDiWw/s1600-h/jpgResponseTimes.JPG"&gt;&lt;img style="MARGIN: 0px 0px 10px 10px; WIDTH: 400px; FLOAT: right; HEIGHT: 265px; CURSOR: hand" id="BLOGGER_PHOTO_ID_5344107210342403074" border="0" alt="" src="http://2.bp.blogspot.com/_SHRRgijB18E/SioVMYoQDAI/AAAAAAAAAOk/4aXlJfCDiWw/s400/jpgResponseTimes.JPG" /&gt;&lt;/a&gt;There are many photos on my site, particularly, but not exclusively, family snaps. In SharePoint I have resized the photos for easier display. I knew that SharePoint did not physically resize the images, but didn't appreciate the full impact it had on people browsing my site over a low speed link. Since SharePoint did not physically resize the photo (.jpg file) a 2MB file might be downloaded to show a little photo whose image only needs to be a few kilobytes. I am a bit disappointed that Sharepoint doesn't resize images automatically. I kind of feel that since it knows how big the display images are, it could resize them accordingly, and even though it didn't do it now, I thought it must be fixed in a service &lt;a href="http://richardlees.com.au/Sites/Demonstrations"&gt;&lt;img style="MARGIN: 0px 0px 10px 10px; WIDTH: 400px; FLOAT: right; HEIGHT: 156px; CURSOR: hand" id="BLOGGER_PHOTO_ID_5347024679213638018" border="0" alt="" src="http://2.bp.blogspot.com/_SHRRgijB18E/SjRynbHGoYI/AAAAAAAAAPU/aW2LwSMOLmA/s400/JPG_resize.JPG" /&gt;&lt;/a&gt;pack or subsequent release.&lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;&lt;div&gt;Anyway a couple of weeks ago a partner colleague mentioned to me his frustration with these images, so I spent a few minutes and resized all the .jpg images in SharePoint. I was only using about 30 images, so it only took about 30 minutes. It is interesting how much difference this has made to the performance of my web site. The home page on my web dashboard shows average response times for the major file types (.aspx, .axd, .css, and .jpg) over the last 60 days. If you look at this graph (top right on &lt;a href="http://richardlees.com.au/Sites/Demonstrations"&gt;&lt;span style="font-size:85%;"&gt;http://RichardLees.com.au/Sites/Demonstrations&lt;/span&gt;&lt;/a&gt;&lt;span style="font-size:85%;"&gt;)&lt;/span&gt; you will see .jpg has dropped in response time around May 21. Althought there is quite a lot of daily variations since it is an average of many different size images. I haven't had such a large performance change since I talked to my son about his download activity (&lt;a href="http://richardlees.blogspot.com/2009/01/is-my-web-site-getting-slower.html"&gt;&lt;span style="font-size:85%;"&gt;http://richardlees.blogspot.com/2009/01/is-my-web-site-getting-slower.html&lt;/span&gt;&lt;/a&gt;).&lt;/div&gt;&lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;You are able to drill into the response time graph, and even take it to a new window if you know how to drive PerformancePoint (see &lt;a href="http://richardlees.blogspot.com/2009/04/try-out-microsofts-zero-footprint-cube.html"&gt;&lt;span style="font-size:85%;"&gt;http://richardlees.blogspot.com/2009/04/try-out-microsofts-zero-footprint-cube.html&lt;/span&gt;&lt;/a&gt; for some quick instructions). I have done this, and drilled down to the photo of Anna and myself on unicycles (one of the main MB culprits). The graph is attached. You can achieve the same, although you will see a more up to date chart since the cube is updating every few minutes.&lt;/div&gt;&lt;br /&gt;&lt;div&gt;Two takeaways from this initiative. &lt;/div&gt;&lt;ol&gt;&lt;li&gt;If you are using SharePoint 2007, it pays to resize your images to the display size.&lt;/li&gt;&lt;li&gt;It always pays to monitor and log performance of any production application so that you can see user performance and the impact of your tuning exercises&lt;/li&gt;&lt;/ol&gt;&lt;div&gt;For more close to real-time performance information of my internet site and the two servers behind the site goto &lt;a href="http://richardlees.com.au/Sites/Demonstrations"&gt;&lt;span style="font-size:85%;"&gt;http://RichardLees.com.au/Sites/Demonstrations&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3689647052791307812-7515356944286695969?l=richardlees.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://richardlees.blogspot.com/feeds/7515356944286695969/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3689647052791307812&amp;postID=7515356944286695969' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3689647052791307812/posts/default/7515356944286695969'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3689647052791307812/posts/default/7515356944286695969'/><link rel='alternate' type='text/html' href='http://richardlees.blogspot.com/2009/06/resizing-jpg-files-for-sharepoint.html' title='Resizing .jpg files for SharePoint Server 2007'/><author><name>Richard Lees</name><uri>http://www.blogger.com/profile/05671716466559973540</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='31' height='32' src='http://2.bp.blogspot.com/-AdeWScnQel8/Thj9ePsjpQI/AAAAAAAAAew/XnIn3xt4a0s/s220/RichardUni.png'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://2.bp.blogspot.com/_SHRRgijB18E/SioVMYoQDAI/AAAAAAAAAOk/4aXlJfCDiWw/s72-c/jpgResponseTimes.JPG' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3689647052791307812.post-8691289033258974478</id><published>2009-06-06T06:38:00.003+10:00</published><updated>2009-06-06T16:19:31.898+10:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Database name'/><category scheme='http://www.blogger.com/atom/ns#' term='Analysis Services'/><category scheme='http://www.blogger.com/atom/ns#' term='DatabaseID'/><category scheme='http://www.blogger.com/atom/ns#' term='rename'/><title type='text'>Analysis Services Database Name and DatabaseID</title><content type='html'>&lt;a href="http://1.bp.blogspot.com/_SHRRgijB18E/SimGUOSF8mI/AAAAAAAAAOc/T4W9RbZOvNI/s1600-h/RestoreAS_error.JPG"&gt;&lt;img style="MARGIN: 0px 0px 10px 10px; WIDTH: 400px; FLOAT: right; HEIGHT: 98px; CURSOR: hand" id="BLOGGER_PHOTO_ID_5343950114841293410" border="0" alt="" src="http://1.bp.blogspot.com/_SHRRgijB18E/SimGUOSF8mI/AAAAAAAAAOc/T4W9RbZOvNI/s400/RestoreAS_error.JPG" /&gt;&lt;/a&gt;Typically an Analysis Services database has a DatabaseID the same as the database name. However, if you rename a database, the DatabaseID will stay the same. This can be a source of confusion and it can lead to useful operational techniques.&lt;br /&gt;&lt;br /&gt;&lt;div&gt;Whenever anyone connects to an Analysis Services database, they do so by selecting a particular database name. So when you rename a database, users will see and use the new name. However, SSIS packages that perform Analysis Services processing tasks select the database by the DatabaseID.  Also, Analysis Services keeps the DatabaseID as a unique identifier. So when you rename an Analysis Services database, existing SSIS packages will be looking for the old DatabaseID.&lt;br /&gt;&lt;/div&gt;&lt;div&gt;So if you have an Analysis Services database, say &lt;strong&gt;&lt;span style="color:#000099;"&gt;EasternMining&lt;/span&gt;&lt;/strong&gt;, and you rename it to &lt;span style="color:#000099;"&gt;&lt;strong&gt;Eastern&lt;/strong&gt; &lt;strong&gt;Mining&lt;/strong&gt;&lt;/span&gt;, then users will only connect to it by asking for &lt;span style="color:#000099;"&gt;&lt;strong&gt;Eastern Mining&lt;/strong&gt;&lt;/span&gt;&lt;span style="color:#000000;"&gt;, which is fine&lt;/span&gt;. However if you try to restore a database called &lt;strong&gt;&lt;span style="color:#000099;"&gt;EasternMining&lt;/span&gt;&lt;/strong&gt; (which doesn't exist anymore) it will fail, even though you have enabled &lt;strong&gt;&lt;span style="color:#000099;"&gt;Allow Database Overwrite&lt;/span&gt;&lt;/strong&gt;. The message will be something like Backup and restore errors: &lt;strong&gt;&lt;span style="font-size:85%;color:#ff0000;"&gt;An object with the same 'databaseid' ID but different 'databasename' Name already exists on the instance. (The AllowOverwrite flag only applies to the Object ID, not to the object name.)&lt;/span&gt;&lt;/strong&gt;  I find the message a bit misleading, but what it is saying is that a database with that DatabaseID exists already.&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;&lt;div&gt;Unfortunately, you cannot change the databaseID, without doing something like backing up and restoring the database.  Interestingly, an xmla create database statement will overwrite an existing database and DatabaseID.  If you want to know the DatabaseID for an Analysis Services database, just right click on the database and select properties.&lt;/div&gt;&lt;div&gt; &lt;/div&gt;&lt;div&gt;I have previously blogged on how you might use this to maintain close to continuous availability when you need to deploy a new Analysis Services cube structure. &lt;a href="http://richardlees.blogspot.com/2009/05/new-analysis-services-database-version.html"&gt;http://richardlees.blogspot.com/2009/05/new-analysis-services-database-version.html&lt;/a&gt; &lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3689647052791307812-8691289033258974478?l=richardlees.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://richardlees.blogspot.com/feeds/8691289033258974478/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3689647052791307812&amp;postID=8691289033258974478' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3689647052791307812/posts/default/8691289033258974478'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3689647052791307812/posts/default/8691289033258974478'/><link rel='alternate' type='text/html' href='http://richardlees.blogspot.com/2009/06/analysis-services-database-name-and.html' title='Analysis Services Database Name and DatabaseID'/><author><name>Richard Lees</name><uri>http://www.blogger.com/profile/05671716466559973540</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='31' height='32' src='http://2.bp.blogspot.com/-AdeWScnQel8/Thj9ePsjpQI/AAAAAAAAAew/XnIn3xt4a0s/s220/RichardUni.png'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://1.bp.blogspot.com/_SHRRgijB18E/SimGUOSF8mI/AAAAAAAAAOc/T4W9RbZOvNI/s72-c/RestoreAS_error.JPG' height='72' width='72'/><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3689647052791307812.post-4917038633511040025</id><published>2009-05-31T07:24:00.010+10:00</published><updated>2009-05-31T13:32:18.690+10:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Data Mining DMX basket analysis SQL Server 2008 Analysis Services'/><title type='text'>Data Mining as an Enhancement to Basket Analysis</title><content type='html'>&lt;a href="http://2.bp.blogspot.com/_SHRRgijB18E/SiH2r39bbfI/AAAAAAAAAOU/0Z8JZMcc5D8/s1600-h/BookDependency.JPG"&gt;&lt;img style="MARGIN: 0px 0px 10px 10px; WIDTH: 400px; FLOAT: right; HEIGHT: 231px; CURSOR: hand" id="BLOGGER_PHOTO_ID_5341821866654526962" border="0" alt="" src="http://2.bp.blogspot.com/_SHRRgijB18E/SiH2r39bbfI/AAAAAAAAAOU/0Z8JZMcc5D8/s400/BookDependency.JPG" /&gt;&lt;/a&gt;Big retailers all want to perform some form of basket analysis, where they analyse the mix of products that customers purchase in one basket. This tends to be an extremely hardware intensive exercise. Using OLAP technologies can help in this exercise, but even with OLAP hardware can be stressed since you really need a dimension on TransactionID. (OK, there are some optimisations you can make to minimise this dimension, but it is still very large and expensive.) However, if you want to analyse which products are bought together (along with other variables such as time of day, customer gender etc) then data mining (DM) can be a great tool and offer its analysis at a very low cost (ie you only need a small commodity server to perform the analysis).  DM algorithms have been available as part of the standard edition of SQL Server since 2000.&lt;br /&gt;&lt;div&gt;&lt;br /&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;Essentially, you ask the data mining to create a model to predict products, based on other products (and other variables such as time of day, store, customer gender etc). The data mining algorithms can scan billions of transactions and produce a data mining model in a relatively short time. The data mining algorithms search through many combinations of products/attributes and discover which products/attributes have strong associations. Essentially, the DM model will only stores relevant associations and their predictive strength. The data mining model tends to be very small since it only contains the associations, even though the work it has done to find them can be resource consuming. The data mining model does not need to store the billions of baskets, or the associations between irrelevant products. You are able to very easily browse the model to visualise products (and potentially other attributes such as gender and time of day) and their associations. Also, you can use the data mining model to predict which products might be bought in a particular basket. This data mining prediction query is extremely fast, typically a few milliseconds, so it is quite plausible that online transactions are probing the data mining model for suggested products at real time.  For example, your billions of transactions might be terabytes of data, while the resultant DM model could be a couple of hundred megabytes. of data.&lt;br /&gt;&lt;/div&gt;&lt;div&gt;I have an online demonstration of a model that does something like this on &lt;a href="http://easternmining.com.au/sites/Demonstrations/Pages/LibrariesSuggestions.aspx"&gt;&lt;span style="font-size:85%;"&gt;http://EasternMining.com.au/sites/Demonstrations/Pages/LibrariesSuggestions.aspx&lt;/span&gt;&lt;/a&gt; I don't have a retailer's transaction history.  Most companies privacy policies prevent them from me displaying them on the internet, however, if yours doesn't let me know and I will host the db on my site.  What I do have is the borrowing history of a real municipal library (with names and personal identifiers fudged), which is very similar in structure to retailer transactions. The book is like a product and a borrower's loaned books are a transaction or basket. In my demonstration, you don't need to enter the books and gender, just select any borrower, and the demonstration will select the books that they have borrowed and their sex. This is a relational query and is actually embedded in the data mining (DMX) query. Here is the "product suggestion" DMX query. It looks a bit wordy, but it is not complicated, and the SQL Server wizard creates most of the query for you.&lt;a href="http://easternmining.com.au/sites/Demonstrations/Pages/LibrariesSuggestions.aspx"&gt;&lt;img style="MARGIN: 0px 0px 10px 10px; WIDTH: 400px; FLOAT: right; HEIGHT: 280px; CURSOR: hand" id="BLOGGER_PHOTO_ID_5341821458013478562" border="0" alt="" src="http://3.bp.blogspot.com/_SHRRgijB18E/SiH2UFpz3qI/AAAAAAAAAN8/T2KLLjclAEw/s400/BookSuggestion.JPG" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;div&gt;&lt;span style="font-size:85%;color:#6600cc;"&gt;SELECT flattened&lt;br /&gt;t.[BorrowerNo],t.sex,t.[decadeofbirth],&lt;br /&gt;TopCount(predict([Loans n Titles],include_statistics),$probability,12)&lt;br /&gt;From BookSuggestionFullDT&lt;br /&gt;PREDICTION JOIN&lt;br /&gt;SHAPE {OPENQUERY([Libraries],&lt;br /&gt;'SELECT&lt;br /&gt;BorrowerNo,&lt;br /&gt;Sex,&lt;br /&gt;DecadeOfBirth&lt;br /&gt;FROM Borrowers_Interesting')}&lt;br /&gt;APPEND&lt;br /&gt;({OPENQUERY([Libraries],&lt;br /&gt;'SELECT&lt;br /&gt;Title,&lt;br /&gt;BorrowerNo&lt;br /&gt;FROM LoansInterestingFull&lt;br /&gt;WHERE Borrower = ''Damen Roxburgh'' ')}&lt;br /&gt;RELATE BorrowerNo TO BorrowerNo)&lt;br /&gt;AS Loans_interesting AS t&lt;br /&gt;ON BookSuggestionFullDT.Sex = t.Sex AND&lt;br /&gt;BookSuggestionFullDT.[Decade Of Birth] = t.DecadeOfBirth AND&lt;br /&gt;BookSuggestionFullDT.[Loans n Titles].Title = t.Loans_interesting.Title&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;&lt;div&gt;Here is a similar query with the values coded into the query. As you can see, it is much easier to read. I have found the DMX language easier to learn than MDX, since the tasks you are requesting tend to be simpler. I.e. you are generally only asking for a prediction based on some input variables.&lt;/div&gt;&lt;br /&gt;&lt;div&gt;&lt;span style="font-size:85%;color:#6600cc;"&gt;SELECT flattened&lt;br /&gt;MyCustomer.Customer,MyCustomer.Sex,MyCustomer.DecadeOfBirth,&lt;br /&gt;TopCount(predict([Loans n Titles],include_statistics),$probability,12)&lt;br /&gt;From BookSuggestionFullDT&lt;br /&gt;PREDICTION JOIN&lt;br /&gt;&lt;/span&gt;&lt;span style="font-size:85%;color:#6600cc;"&gt;(SELECT "Richard Lees" as Customer,&lt;br /&gt;"Male" as [Sex] ,&lt;br /&gt;"1960s" as DecadeOfBirth,&lt;br /&gt;(select 'Marie Claire' as [Title] union&lt;br /&gt;select 'Cleo' as Title) as books) as MyCustomer&lt;br /&gt;ON BookSuggestionFullDT.Sex = MyCustomer.Sex AND&lt;br /&gt;BookSuggestionFullDT.[Decade Of Birth] = MyCustomer.[DecadeOfBirth] AND&lt;br /&gt;BookSuggestionFullDT.[Loans n Titles].Title = MyCustomer.Books.Title&lt;/span&gt;&lt;/div&gt;&lt;br /&gt;&lt;div&gt;So what I am suggesting here is that if you are performing basket analysis, or have a desire to, then you should also consider creating a data mining model.  The DM model will help find the associations and optionally predict potential products. You could even have online shopping sites use DM models to make predictions based on the basket content so far.  The query tends to be a few milliseconds, certainly sub-second.&lt;/div&gt;&lt;br /&gt;&lt;div&gt;If you require any assistance in creating or exploiting your data mining models, please don't hesitate to ask me for assistance.&lt;/div&gt;&lt;br /&gt;&lt;div&gt;For more online real-time business intelligence demonstrations see &lt;a href="http://richardlees.com.au/Sites/Demonstrations"&gt;&lt;span style="font-size:85%;"&gt;http://RichardLees.com.au/Sites/Demonstrations&lt;/span&gt;&lt;/a&gt; &lt;/div&gt;&lt;br /&gt;&lt;br /&gt;&lt;div&gt;Richard&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3689647052791307812-4917038633511040025?l=richardlees.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://richardlees.blogspot.com/feeds/4917038633511040025/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3689647052791307812&amp;postID=4917038633511040025' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3689647052791307812/posts/default/4917038633511040025'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3689647052791307812/posts/default/4917038633511040025'/><link rel='alternate' type='text/html' href='http://richardlees.blogspot.com/2009/05/data-mining-as-enhancement-to-basket.html' title='Data Mining as an Enhancement to Basket Analysis'/><author><name>Richard Lees</name><uri>http://www.blogger.com/profile/05671716466559973540</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='31' height='32' src='http://2.bp.blogspot.com/-AdeWScnQel8/Thj9ePsjpQI/AAAAAAAAAew/XnIn3xt4a0s/s220/RichardUni.png'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://2.bp.blogspot.com/_SHRRgijB18E/SiH2r39bbfI/AAAAAAAAAOU/0Z8JZMcc5D8/s72-c/BookDependency.JPG' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3689647052791307812.post-1008876958145999655</id><published>2009-05-29T07:12:00.005+10:00</published><updated>2009-05-29T07:40:02.400+10:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='real data Data Mining tutorial SQL Server 2008'/><title type='text'>Creating your first data mining model</title><content type='html'>&lt;a href="http://technet.microsoft.com/en-us/library/dd883232.aspx"&gt;&lt;img style="MARGIN: 0px 0px 10px 10px; WIDTH: 320px; FLOAT: right; HEIGHT: 256px; CURSOR: hand" id="BLOGGER_PHOTO_ID_5340987487160779826" border="0" alt="" src="http://1.bp.blogspot.com/_SHRRgijB18E/Sh7_0mIGRDI/AAAAAAAAAN0/-NpVI7XfYfY/s320/DataMiningTutorial.JPG" /&gt;&lt;/a&gt;Data Mining is a entirely new exercise using new technology for most developers. It can be awkward creating your very first model. To help people create their very first data mining model using real data, I have written a whitepaper on msdn. So if you want to create your first model, visualise it, query it, and create a Reporting Services report from it go to &lt;a href="http://technet.microsoft.com/en-us/library/dd883232.aspx"&gt;&lt;span style="font-size:85%;"&gt;http://technet.microsoft.com/en-us/library/dd883232.aspx&lt;/span&gt;&lt;/a&gt;&lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;&lt;div&gt;You can see a real-time demonstration of data mining on &lt;a href="http://easternmining/sites/Demonstrations/Pages/PredictingLatenciesusingDataMining.aspx"&gt;&lt;span style="font-size:85%;"&gt;http://EasternMining/sites/Demonstrations/Pages/PredictingLatenciesusingDataMining.aspx&lt;/span&gt;&lt;/a&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;&lt;div&gt;Of course, if you require any assistance with more advanced features of data mining, or applying it to your business strategy, just ask me.&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3689647052791307812-1008876958145999655?l=richardlees.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://richardlees.blogspot.com/feeds/1008876958145999655/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3689647052791307812&amp;postID=1008876958145999655' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3689647052791307812/posts/default/1008876958145999655'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3689647052791307812/posts/default/1008876958145999655'/><link rel='alternate' type='text/html' href='http://richardlees.blogspot.com/2009/05/creating-your-first-data-mining-model.html' title='Creating your first data mining model'/><author><name>Richard Lees</name><uri>http://www.blogger.com/profile/05671716466559973540</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='31' height='32' src='http://2.bp.blogspot.com/-AdeWScnQel8/Thj9ePsjpQI/AAAAAAAAAew/XnIn3xt4a0s/s220/RichardUni.png'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://1.bp.blogspot.com/_SHRRgijB18E/Sh7_0mIGRDI/AAAAAAAAAN0/-NpVI7XfYfY/s72-c/DataMiningTutorial.JPG' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3689647052791307812.post-5371445649866377654</id><published>2009-05-28T15:58:00.004+10:00</published><updated>2009-05-28T16:32:11.169+10:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='DMX Data Mining models predicting response times'/><title type='text'>Don't be afraid of Data Mining models and DMX</title><content type='html'>&lt;a href="http://3.bp.blogspot.com/_SHRRgijB18E/Sh4u8q-cfxI/AAAAAAAAANs/-rFBXTaDSTk/s1600-h/ResponseTimePredictions.JPG"&gt;&lt;img style="MARGIN: 0px 0px 10px 10px; WIDTH: 400px; FLOAT: right; HEIGHT: 294px; CURSOR: hand" id="BLOGGER_PHOTO_ID_5340757827971219218" border="0" alt="" src="http://3.bp.blogspot.com/_SHRRgijB18E/Sh4u8q-cfxI/AAAAAAAAANs/-rFBXTaDSTk/s400/ResponseTimePredictions.JPG" /&gt;&lt;/a&gt;Since SQL Server 2000, Microsoft have included data mining algorithms in the standard edition. It continues to astound me that people are still not taking advantage of this technology. OK, you will have to learn something new, but it is a lot of fun and can have profound impact on an organisation. Sometimes all you want, or need, to do is build the model and visualise it.&lt;br /&gt;&lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;However, it is often desirable to use the model to make predictions. To do this, you really need to write DMX (Data Mining version of MDX) although the wizard does a good job of helping you. I have some real time data mining demonstrations on my internet site. For example, &lt;a href="http://easternmining.com.au/sites/Demonstrations/Pages/PredictingLatenciesusingDataMining.aspx"&gt;&lt;span style="font-size:85%;"&gt;http://EasternMining.com.au/sites/Demonstrations/Pages/PredictingLatenciesusingDataMining.aspx&lt;/span&gt;&lt;/a&gt; will query a data mining model with the last 50 hits to the site and predict what the response time will be. Of course there is no benefit in such a model, as we already know the outcome, but it is an interesting demonstration of data mining. The predictions could be for delivery time, probability of fraud, customer profitability, new hire longevity, medical prognosis etc. Here is the DMX query behind the report.&lt;/div&gt;&lt;br /&gt;&lt;div&gt;&lt;span style="font-family:courier new;font-size:85%;color:#6600cc;"&gt;SELECT&lt;br /&gt;Last50.[rid],&lt;br /&gt;Last50.[LogTime],&lt;br /&gt;Predict(ProcessingTimeDT.[Processing Time]) as [Prediction],&lt;br /&gt;Last50.ProcessingTime,&lt;br /&gt;Last50.BytesIn,&lt;br /&gt;Last50.BytesOut,&lt;br /&gt;Last50.Country,&lt;br /&gt;Last50.Resource,&lt;br /&gt;Last50.ClientAgnt as [ClientAgent],&lt;br /&gt;Last50.http_status,&lt;br /&gt;Last50.Operation,&lt;br /&gt;Last50.ReferringServer,&lt;br /&gt;Last50.os,&lt;br /&gt;Last50.City&lt;br /&gt;From&lt;br /&gt;ProcessingTimeDT&lt;br /&gt;PREDICTION JOIN&lt;br /&gt;OPENQUERY([Weblogs],&lt;br /&gt;'SELECT&lt;br /&gt;[rid],&lt;br /&gt;[LogTime],&lt;br /&gt;ProcessingTime,&lt;br /&gt;BytesIn,&lt;br /&gt;BytesOut,&lt;br /&gt;Country,&lt;br /&gt;Resource,&lt;br /&gt;ClientAgnt,&lt;br /&gt;http_status,&lt;br /&gt;Operation,&lt;br /&gt;ReferringServer,&lt;br /&gt;ResourceType,&lt;br /&gt;OK,&lt;br /&gt;OS,&lt;br /&gt;City&lt;br /&gt;FROM&lt;br /&gt;(SELECT TOP (50) rid, LogTime, ProcessingTime, BytesIn, BytesOut, Country, State, City, resource, resourcetype, clientagnt, OK, http_status, operation,&lt;br /&gt;referringServer, calyear, Calmonth,OS&lt;br /&gt;FROM InternetLog_Wide&lt;br /&gt;ORDER BY RID DESC) as Last50Hits') AS Last50&lt;br /&gt;ON ProcessingTimeDT.[Bytes In] = Last50.BytesIn&lt;br /&gt;AND ProcessingTimeDT.[Bytes Out] = Last50.BytesOut&lt;br /&gt;AND ProcessingTimeDT.Country = Last50.Country&lt;br /&gt;AND ProcessingTimeDT.Resource = Last50.Resource&lt;br /&gt;AND ProcessingTimeDT.ResourceType = Last50.ResourceType&lt;br /&gt;AND ProcessingTimeDT.ClientAgnt = Last50.ClientAgnt&lt;br /&gt;AND ProcessingTimeDT.OK = Last50.OK&lt;br /&gt;AND ProcessingTimeDT.[Http Status] = Last50.http_status&lt;br /&gt;AND ProcessingTimeDT.Operation = Last50.Operation&lt;br /&gt;AND ProcessingTimeDT.[Referring Server] = Last50.ReferringServer&lt;br /&gt;AND ProcessingTimeDT.OS = Last50.OS&lt;br /&gt;AND ProcessingTimeDT.City = Last50.City&lt;/span&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;div&gt;The DMX query is actually a hybrid query, with the inner query (for the last 50 hits) coming from the SQL relational database. If we take out the hybrid query and just enter some data manually, the query looks a lot cleaner. For example here is a DMX query where I type in a couple of the attributes (country, city, resource) and get a prediction based on this input. &lt;/div&gt;&lt;br /&gt;&lt;div&gt;&lt;span style="font-family:courier new;font-size:85%;color:#6600cc;"&gt;SELECT&lt;br /&gt;Predict(ProcessingTimeDT.[Processing Time]) as [Prediction],&lt;br /&gt;TypedData.*&lt;br /&gt;From&lt;br /&gt;ProcessingTimeDT&lt;br /&gt;PREDICTION JOIN&lt;br /&gt;(SELECT&lt;br /&gt;'UNITED STATES' as Country,&lt;br /&gt;'RichardsWelcomePage.aspx' as Resource,&lt;br /&gt;'MSIE 8.0' as ClientAgnt,&lt;br /&gt;'New York' as City )&lt;br /&gt;AS TypedData&lt;br /&gt;ON ProcessingTimeDT.Country = TypedData.Country&lt;br /&gt;AND ProcessingTimeDT.Resource = TypedData.Resource&lt;br /&gt;AND ProcessingTimeDT.ClientAgnt = TypedData.ClientAgnt&lt;br /&gt;AND ProcessingTimeDT.City = TypedData.City&lt;/span&gt;&lt;/div&gt;&lt;br /&gt;&lt;div&gt;Notice how the data mining model does not need to have all the attributes. The more you give the query, the more accurate the query will be.&lt;/div&gt;&lt;br /&gt;&lt;div&gt;If you need any assistance designing or querying your data mining models, don't hesitate to ask for my assistance. Data mining is one of my favourite activities.&lt;/div&gt;&lt;br /&gt;&lt;div&gt;Richard&lt;/div&gt;&lt;br /&gt;&lt;div&gt;For more real-time data mining and OLAP demonstrations goto &lt;a href="http://easternmining.com.au/Sites/Demonstrations"&gt;&lt;span style="font-size:85%;"&gt;http://EasternMining.com.au/Sites/Demonstrations&lt;/span&gt;&lt;/a&gt; &lt;/div&gt;&lt;br /&gt;&lt;div&gt;For more about Richard Lees goto&lt;span style="font-size:85%;"&gt; &lt;/span&gt;&lt;a href="http://richardlees.com.au/"&gt;&lt;span style="font-size:85%;"&gt;http://richardlees.com.au/&lt;/span&gt;&lt;/a&gt; &lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3689647052791307812-5371445649866377654?l=richardlees.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://richardlees.blogspot.com/feeds/5371445649866377654/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3689647052791307812&amp;postID=5371445649866377654' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3689647052791307812/posts/default/5371445649866377654'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3689647052791307812/posts/default/5371445649866377654'/><link rel='alternate' type='text/html' href='http://richardlees.blogspot.com/2009/05/dont-be-afraid-of-data-mining-models.html' title='Don&apos;t be afraid of Data Mining models and DMX'/><author><name>Richard Lees</name><uri>http://www.blogger.com/profile/05671716466559973540</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='31' height='32' src='http://2.bp.blogspot.com/-AdeWScnQel8/Thj9ePsjpQI/AAAAAAAAAew/XnIn3xt4a0s/s220/RichardUni.png'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://3.bp.blogspot.com/_SHRRgijB18E/Sh4u8q-cfxI/AAAAAAAAANs/-rFBXTaDSTk/s72-c/ResponseTimePredictions.JPG' height='72' width='72'/><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3689647052791307812.post-386020410490289241</id><published>2009-05-27T14:22:00.009+10:00</published><updated>2009-05-27T16:29:06.192+10:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='MDX Analysis Services text in a calculated member'/><title type='text'>MDX for Business - Text as a calculated measure</title><content type='html'>&lt;a href="http://easternmining.com.au/Sites/Demonstrations"&gt;&lt;img style="MARGIN: 0px 0px 10px 10px; WIDTH: 304px; FLOAT: right; HEIGHT: 400px; CURSOR: hand" id="BLOGGER_PHOTO_ID_5340358365711867458" border="0" alt="" src="http://4.bp.blogspot.com/_SHRRgijB18E/ShzDo6GKnkI/AAAAAAAAANc/b3zR6bRv_88/s400/TopCitiesByHour.JPG" /&gt;&lt;/a&gt;It surprises many developers that you can have a text value as a calculated measure, since a measure usually contains an aggregated number. However, a text value is very simple and very useful in a calculated measure. It could be as simple as getting a property of a member such as a customer's mobile number -&lt;br /&gt;&lt;br /&gt;&lt;div&gt;&lt;span style="color:#6600cc;"&gt;Member Measures.MobileNumber as Customers.Customer.properties("Mobile Number")&lt;/span&gt;&lt;br /&gt;&lt;div&gt;I have a real example, in a report that shows hours on rows and the last three days on columns, and for each cell it shows the top City (customer, product, whatever) by virtue of the number of web requests in that hour. This is shown on the default page of my web dashboard on &lt;a href="http://easternmining.com.au/Sites/Demonstrations"&gt;http://EasternMining.com.au/Sites/Demonstrations&lt;/a&gt; See down near the bottom is a table called &lt;strong&gt;Top Cities by Hour - Last 3 Days&lt;/strong&gt;. It shows the city with the most activity on my site for that hour. It might look ahead of time to you, if you are in America or Europe. It is actually very close to real-time in Sydney Australia.&lt;/div&gt;&lt;br /&gt;&lt;div&gt;Here is the mdx&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;span style="color:#993399;"&gt;With Member Measures.TopCity as&lt;br /&gt;Topcount([Client Host].[Client Geography].[City]&lt;br /&gt;,1,measures.[request count]).item(0).member_caption&lt;br /&gt;Select&lt;br /&gt;tail([Date].[Year Month Day].[Day],3) on Columns,&lt;br /&gt;[HoursOfDay].[HoursOfDay].[Hour Of Day] on Rows&lt;br /&gt;From EasternMining&lt;br /&gt;Where Measures.TopCity&lt;/span&gt;&lt;/div&gt;&lt;a href="http://1.bp.blogspot.com/_SHRRgijB18E/ShzDsno8NsI/AAAAAAAAANk/hzMoU8CpMyY/s1600-h/TopRegionsByHour.JPG"&gt;&lt;img style="MARGIN: 0px 0px 10px 10px; WIDTH: 210px; FLOAT: right; HEIGHT: 400px; CURSOR: hand" id="BLOGGER_PHOTO_ID_5340358429476927170" border="0" alt="" src="http://1.bp.blogspot.com/_SHRRgijB18E/ShzDsno8NsI/AAAAAAAAANk/hzMoU8CpMyY/s400/TopRegionsByHour.JPG" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;div&gt;See how simple the mdx is. Again, imagine having to write the same query in SQL. Also, if I want to change something in the report, such as last 5 days, last 3 months etc, it is very simple. In fact, If I chage [City] to [Region] I get the following report. This report tends to show the time zones of Asia, Europe and Americas. See how Americas tend to be early morning (Sydney time) while Europe is late at night and Asia is visiting during the online day.&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;&lt;div&gt;It's so very easy with MDX. If the answer is in the data, then one MDX expression should be able to ask the question. Let me know if you need any assistance in writing or learning mdx.&lt;/div&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3689647052791307812-386020410490289241?l=richardlees.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://richardlees.blogspot.com/feeds/386020410490289241/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3689647052791307812&amp;postID=386020410490289241' title='7 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3689647052791307812/posts/default/386020410490289241'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3689647052791307812/posts/default/386020410490289241'/><link rel='alternate' type='text/html' href='http://richardlees.blogspot.com/2009/05/mdx-for-business-text-as-calculated.html' title='MDX for Business - Text as a calculated measure'/><author><name>Richard Lees</name><uri>http://www.blogger.com/profile/05671716466559973540</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='31' height='32' src='http://2.bp.blogspot.com/-AdeWScnQel8/Thj9ePsjpQI/AAAAAAAAAew/XnIn3xt4a0s/s220/RichardUni.png'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://4.bp.blogspot.com/_SHRRgijB18E/ShzDo6GKnkI/AAAAAAAAANc/b3zR6bRv_88/s72-c/TopCitiesByHour.JPG' height='72' width='72'/><thr:total>7</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3689647052791307812.post-2008754518555272323</id><published>2009-05-22T12:36:00.009+10:00</published><updated>2009-05-25T19:38:22.764+10:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Analysis Services MDX Ranking Customers Top cities'/><title type='text'>MDX for Business - Ranking Customers/Cities</title><content type='html'>&lt;a href="http://richardlees.com.au/Sites/Demonstrations"&gt;&lt;img style="MARGIN: 0px 0px 10px 10px; WIDTH: 381px; FLOAT: right; HEIGHT: 400px; CURSOR: hand" id="BLOGGER_PHOTO_ID_5339338409266611522" border="0" alt="" src="http://1.bp.blogspot.com/_SHRRgijB18E/Shkj_oPvhUI/AAAAAAAAANU/-IVzIsIWWEg/s400/TopCities.JPG" /&gt;&lt;/a&gt;I was just asked to help with an MDX query to list out the top 50 Customers and provide ranks for the last 3 periods. This is a relatively simple MDX query, which someone with moderate MDX skills should be able to write. It might be interesting to you, so here it is.&lt;br /&gt;&lt;br /&gt;&lt;div&gt;&lt;span style="font-size:85%;color:#6600cc;"&gt;with&lt;br /&gt;Set Last3Months as tail([Date].[Year Month Day].[Month],3)&lt;br /&gt;Member Measures.Last3MonthsHits as Sum(Last3Months,Measures.Hits)&lt;br /&gt;Set Top50Cities as topcount(filter([Client Host].[Client Geography].[City],[Client Host].[Client Geography].member_caption&lt;&gt;"-"),50,Measures.Last3MonthsHits)&lt;br /&gt;Member Measures.RankThisMonth as&lt;br /&gt;rank([Client Host].[Client Geography].currentmember,order(Top50Cities,Measures.Hits,BDESC))&lt;br /&gt;Member Measures.Movement as Measures.RankThisMonth-(Measures.RankThisMonth,[Date].[Year Month Day].prevmember)&lt;br /&gt;&lt;br /&gt;Select Last3Months&lt;br /&gt;*{Measures.Hits,Measures.RankThisMonth,Measures.Movement} on 0,&lt;br /&gt;Top50Cities on 1&lt;br /&gt;From EasternMining&lt;/span&gt;&lt;/div&gt;&lt;br /&gt;&lt;div&gt;&lt;a href="http://4.bp.blogspot.com/_SHRRgijB18E/ShYQrY1WssI/AAAAAAAAAM8/nDVZ4q5ZXYc/s1600-h/Top20Cities.JPG"&gt;&lt;/a&gt;It is a real query and I have executed it on my Weblogs cube (all hits and sessions from my web server).  The results are formatted in Excel, which I have found interesting. Sydney (my home town) naturally is top of the list, but then comes New York and London. I don't do any business in these cities, which is why I find it interesting. Perhaps I should have offices there? Redmond is quite high. I guess my old Microsoft colleagues are still finding the real time demonstrations interesting.&lt;/div&gt;&lt;br /&gt;&lt;div&gt;Anyway, you can read the MDX and see what it is doing. Notice the elegance of the MDX query. It is very succinct, and easy to read.  For those of you who know SQL, can you imagine writing this query in SQL?  It wouldn't look anywhere near as elegant, nor would it perform as well.&lt;/div&gt;&lt;br /&gt;&lt;div&gt;If you would like assistance with your OLAP cube design or MDX query writing, please don't hesitate to ask. &lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3689647052791307812-2008754518555272323?l=richardlees.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://richardlees.blogspot.com/feeds/2008754518555272323/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3689647052791307812&amp;postID=2008754518555272323' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3689647052791307812/posts/default/2008754518555272323'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3689647052791307812/posts/default/2008754518555272323'/><link rel='alternate' type='text/html' href='http://richardlees.blogspot.com/2009/05/mdx-for-business-ranking.html' title='MDX for Business - Ranking Customers/Cities'/><author><name>Richard Lees</name><uri>http://www.blogger.com/profile/05671716466559973540</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='31' height='32' src='http://2.bp.blogspot.com/-AdeWScnQel8/Thj9ePsjpQI/AAAAAAAAAew/XnIn3xt4a0s/s220/RichardUni.png'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://1.bp.blogspot.com/_SHRRgijB18E/Shkj_oPvhUI/AAAAAAAAANU/-IVzIsIWWEg/s72-c/TopCities.JPG' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3689647052791307812.post-2295840078854820093</id><published>2009-05-22T11:29:00.007+10:00</published><updated>2009-05-23T08:53:12.252+10:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Windows Performance Monitor Continuous monitoring real time analysis'/><title type='text'>Perfmon Has Everything</title><content type='html'>&lt;a href="http://easternmining.com.au/sites/Demonstrations/Shared%20Documents/Windows%20Performance%20Monitor/Disk.aspx"&gt;&lt;img style="MARGIN: 0px 0px 10px 10px; WIDTH: 400px; FLOAT: right; HEIGHT: 272px; CURSOR: hand" id="BLOGGER_PHOTO_ID_5338783912486957090" border="0" alt="" src="http://2.bp.blogspot.com/_SHRRgijB18E/Shcrrqxa8CI/AAAAAAAAANE/EHKoQrHwMPE/s400/WriteBytesPerSec.JPG" /&gt;&lt;/a&gt;I think I have blogged on the Perfmon cube before, but I am blogging again since many people still do not appreciate how valuable and comprehensive the information from Perfmon can be. When you have a production performance issue, it is so much easier to diagnose if you can do so without adding further stress on the server and by looking at trend information. For example, if your server is busy and going slow, wouldn't it be good to see what cpu/memory/io metrics are now, and compare them to how they were before the performance issue? If you don't store historical information, trend analysis is not possible. And without historical information you will be less likely to preemptively act to avoid a performance problem.&lt;br /&gt;&lt;div&gt;&lt;div&gt;&lt;br /&gt;&lt;div&gt;My dashboard &lt;a href="http://easternmining.com.au/sites/Demonstrations/Shared%20Documents/Windows%20Performance%20Monitor/Perfmon.aspx"&gt;http://EasternMining.com.au/sites/Demonstrations/Shared%20Documents/Windows%20Performance%20Monitor/Perfmon.aspx&lt;/a&gt; shows many perfmon metrics, but the dashboard conceals how rich the cube is. Notice the headline information in the dashboard above. Now let's assume that there might be an IO bottleneck (very common in database servers nowadays). Click on the &lt;strong&gt;Disk&lt;/strong&gt; tab at the top of the dashboard. This will show you the &lt;strong&gt;Physical Disk&lt;/strong&gt; counters from perfmon. You can filter it by a particular machine. Now click on &lt;strong&gt;Disk Bytes/sec&lt;/strong&gt; in the main display table. &lt;a href="http://1.bp.blogspot.com/_SHRRgijB18E/ShYFHXtDqTI/AAAAAAAAAM0/p9b8iW2w3uw/s1600-h/ProcessWriteBytesPerSec.JPG"&gt;&lt;/a&gt;Notice how the two graphs on the right dynamically chart this counter for the last 60 minutes and last 60 hours. &lt;a href="http://easternmining.com.au/sites/Demonstrations/Shared%20Documents/Windows%20Performance%20Monitor/Process.aspx"&gt;&lt;img style="MARGIN: 0px 0px 10px 10px; WIDTH: 400px; FLOAT: right; HEIGHT: 309px; CURSOR: hand" id="BLOGGER_PHOTO_ID_5338784153072749490" border="0" alt="" src="http://2.bp.blogspot.com/_SHRRgijB18E/Shcr5rBiy7I/AAAAAAAAANM/YbbIdEzc36E/s400/ProcessWriteBytesPerSec.JPG" /&gt;&lt;/a&gt;We can see that there is a large amount of IO on the B: drive during the last couple of days. Now we want to know which program (windows process) is consuming this IO. Perfmon has all the information. Click on &lt;strong&gt;Process&lt;/strong&gt; tab at the top of the dashboard. Then click on &lt;a style="COLOR: #000000; CURSOR: hand" class="bsm-Link" onclick="PPSMA.DashboardController.get_instance().updateParameter('4f016b33-8473-48f1-8568-b5451d811a29','543aebcb-95ad-4ee8-ae69-4a188271de53',['[Counters].[Counter Hierarchy].[Group].&amp;amp;[Process].&amp;amp;[Process].&amp;amp;[IO Data Bytes/sec]7e3b6586-5c3f-4b51-affe-9e6d1040a00e']);UpdateSelectedCellStyle();return false;" val="bsmval"&gt;&lt;strong&gt;IO Data Bytes/sec&lt;/strong&gt;&lt;/a&gt; in the main table. Notice how the graphs on the right chart the top processes of this counter. Also try clicking on &lt;a style="COLOR: #000000; CURSOR: hand" class="bsm-Link" onclick="PPSMA.DashboardController.get_instance().updateParameter('4f016b33-8473-48f1-8568-b5451d811a29','543aebcb-95ad-4ee8-ae69-4a188271de53',['[Counters].[Counter Hierarchy].[Group].&amp;amp;[Process].&amp;amp;[Process].&amp;amp;[IO Data Bytes/sec]7e3b6586-5c3f-4b51-affe-9e6d1040a00e']);UpdateSelectedCellStyle();return false;" val="bsmval"&gt;&lt;strong&gt;IO Write Bytes/sec&lt;/strong&gt;&lt;/a&gt; and see that most of the IO activity is write IO. The chart tells us which process is consuming the IO. In my case it is msmdsrv.exe but it might be different by the time you look at it. All this analysis can be performed without increasing load on the target servers.&lt;/div&gt;&lt;br /&gt;&lt;div&gt;If you are interested in Windows performance, try clicking around the Perfmon dashboard tabs on my demonstration site above. The Perfmon metrics are very close to real time (&lt;=180 second lag). You can also use ThinSlicer for ad hoc queries of the Perfmon cube from &lt;a href="http://richardlees.com.au:8080/iislog/tsPerfmon.aspx?Column=[DateTime].[Year%20Month%20Day%20Hour]&amp;amp;Row=[Counters].[Counter%20Hierarchy]&amp;amp;Measure=Measures.[ValueAverage"&gt;http://richardlees.com.au:8080/iislog/tsPerfmon.aspx?Column=[DateTime].[Year%20Month%20Day%20Hour]&amp;amp;Row=[Counters].[Counter%20Hierarchy]&amp;amp;Measure=Measures.[ValueAverage&lt;/a&gt;] &lt;/div&gt;&lt;br /&gt;&lt;div&gt;Also if you would like a Perfmon cube in your organisation, I would be happy to help you. The only technologies used to create the real time dashboard above is Microsoft SQL Server and Microsoft PerformancePoint (PerformancePoint is optional for an application like this, since it is just the vehicle for displaying the information from the cube). &lt;/div&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3689647052791307812-2295840078854820093?l=richardlees.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://richardlees.blogspot.com/feeds/2295840078854820093/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3689647052791307812&amp;postID=2295840078854820093' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3689647052791307812/posts/default/2295840078854820093'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3689647052791307812/posts/default/2295840078854820093'/><link rel='alternate' type='text/html' href='http://richardlees.blogspot.com/2009/05/perfmon-has-everything.html' title='Perfmon Has Everything'/><author><name>Richard Lees</name><uri>http://www.blogger.com/profile/05671716466559973540</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='31' height='32' src='http://2.bp.blogspot.com/-AdeWScnQel8/Thj9ePsjpQI/AAAAAAAAAew/XnIn3xt4a0s/s220/RichardUni.png'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://2.bp.blogspot.com/_SHRRgijB18E/Shcrrqxa8CI/AAAAAAAAANE/EHKoQrHwMPE/s72-c/WriteBytesPerSec.JPG' height='72' width='72'/><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3689647052791307812.post-4934275770096704185</id><published>2009-05-17T20:11:00.008+10:00</published><updated>2009-05-17T20:39:53.578+10:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='SQL Server 2008 deploying new database versions very large OLAP databases with slow processing'/><title type='text'>New Analysis Services database version with minimal downtime</title><content type='html'>&lt;a href="http://richardlees.com.au/"&gt;&lt;img id="BLOGGER_PHOTO_ID_5336739339571828578" style="FLOAT: right; MARGIN: 0px 0px 10px 10px; WIDTH: 400px; CURSOR: hand; HEIGHT: 198px" alt="" src="http://1.bp.blogspot.com/_SHRRgijB18E/Sg_oJ3IIF2I/AAAAAAAAAMU/BBhTSFheaqg/s400/ASProperties_v2.JPG" border="0" /&gt;&lt;/a&gt;It can be operationally challenging to deploy a new AS database version without cube downtime. Typically, if you deploy a new version of an AS database, the database will be offline until the database has completed a full process. This might be a long time in an environment where the large measure groups are very large and are only incrementally processed (on a daily, hourly, minute etc. schedule), since the full process may take many hours. This can be a problem in SQL Server OLAP as we have been operationally used to a cube being available 24 by 7, since SQL Server 7.0.&lt;br /&gt;&lt;br /&gt;One workaround is to deploy a new version of the database with a slightly different name, for example the existing database might be Perfmon and the new version might be Perfmon_V2. In this way, &lt;a href="http://3.bp.blogspot.com/_SHRRgijB18E/Sg_o6XN02kI/AAAAAAAAAMc/kanplZyRSAc/s1600-h/ASDatabases.JPG"&gt;&lt;img id="BLOGGER_PHOTO_ID_5336740172819388994" style="FLOAT: right; MARGIN: 0px 0px 10px 10px; WIDTH: 325px; CURSOR: hand; HEIGHT: 265px" alt="" src="http://3.bp.blogspot.com/_SHRRgijB18E/Sg_o6XN02kI/AAAAAAAAAMc/kanplZyRSAc/s400/ASDatabases.JPG" border="0" /&gt;&lt;/a&gt;the new database can be deployed and processing while the existing database is online and available. The trick comes when the new database has completed processing. The old database could be deleted and the new database renamed to the original name. This has a slight snag, since when you rename a database, the databaseID remains the same, and the DatbaseID is used by SSIS in AS processing tasks. So you would need to change all the SSIS packages that refer to the database. This would be confusing to manage since the database names would be out of sync with the databaseIDs. The extra trick is to back up the new AS database after processing and restore it over the original database. When you restore an AS database, the database name and databaseID are in sync.&lt;br /&gt;&lt;br /&gt;Hence, for an AS database that takes a long time to fully process, to deploy a new database version follow these steps.&lt;br /&gt;&lt;ol&gt;&lt;li&gt;Deploy the new AS database with a suffix of _V2&lt;/li&gt;&lt;li&gt;Fully process the new AS database &lt;/li&gt;&lt;li&gt;Backup the new AS database &lt;/li&gt;&lt;li&gt;Restore the AS database over the original database (Allow Database Overwrite) &lt;/li&gt;&lt;/ol&gt;&lt;p&gt;The downtime is limited to the duration of the restore operation. &lt;/p&gt;&lt;p&gt;Note, the original AS database could continue with incremental processing during the new database full process, although some consideration needs to be given to the way you manage high water marks in the measure groups. This workaround is suitable for SQL Server 2005 and SQL Server 2008. &lt;/p&gt;&lt;p&gt;For realtime online OLAP and data mining demonstrations see &lt;a href="http://richardlees.com.au/Sites/Demonstrations"&gt;http://RichardLees.com.au/Sites/Demonstrations&lt;/a&gt; &lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3689647052791307812-4934275770096704185?l=richardlees.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://richardlees.blogspot.com/feeds/4934275770096704185/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3689647052791307812&amp;postID=4934275770096704185' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3689647052791307812/posts/default/4934275770096704185'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3689647052791307812/posts/default/4934275770096704185'/><link rel='alternate' type='text/html' href='http://richardlees.blogspot.com/2009/05/new-analysis-services-database-version.html' title='New Analysis Services database version with minimal downtime'/><author><name>Richard Lees</name><uri>http://www.blogger.com/profile/05671716466559973540</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='31' height='32' src='http://2.bp.blogspot.com/-AdeWScnQel8/Thj9ePsjpQI/AAAAAAAAAew/XnIn3xt4a0s/s220/RichardUni.png'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://1.bp.blogspot.com/_SHRRgijB18E/Sg_oJ3IIF2I/AAAAAAAAAMU/BBhTSFheaqg/s72-c/ASProperties_v2.JPG' height='72' width='72'/><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3689647052791307812.post-3683567435624181333</id><published>2009-05-04T18:36:00.012+10:00</published><updated>2009-05-04T19:07:17.700+10:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='SQL Server Performance File Fragmentation'/><title type='text'>SQL Server Fragmentation</title><content type='html'>&lt;div&gt;&lt;a href="http://3.bp.blogspot.com/_SHRRgijB18E/Sf6tfu5U2xI/AAAAAAAAALM/bbaifxV-bZI/s1600-h/Frag.JPG"&gt;&lt;span style="font-family:arial;"&gt;&lt;img id="BLOGGER_PHOTO_ID_5331889769529269010" style="FLOAT: right; MARGIN: 0px 0px 10px 10px; WIDTH: 320px; CURSOR: hand; HEIGHT: 182px" alt="" src="http://3.bp.blogspot.com/_SHRRgijB18E/Sf6tfu5U2xI/AAAAAAAAALM/bbaifxV-bZI/s320/Frag.JPG" border="0" /&gt;&lt;/span&gt;&lt;/a&gt;&lt;span style="font-family:arial;"&gt; &lt;span style="font-size:130%;color:#000099;"&gt;There are two types of fragmentation&lt;/span&gt; &lt;/span&gt;&lt;span style="color:#000099;"&gt;&lt;br /&gt;&lt;div&gt;&lt;div&gt;&lt;br /&gt;&lt;div&gt;&lt;span style="font-family:arial;"&gt;SQL Server Fragmentation &lt;/span&gt;&lt;/div&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;span style="font-family:arial;"&gt;You can analyse SQL fragmentation with DBCC SHOWCONTIG, and use SQL Server utilities to reorganise, if necessary. Most DBAs know about this type of fragmentation and SQL Server utilities can help you reoganise your databases online.&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;&lt;div&gt;&lt;span style="color:#000099;"&gt;&lt;span style="font-family:arial;"&gt;OS Level Fragmentation&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span style="font-family:arial;"&gt;SQL Server knows nothing about this fragmentation since it is managed by the OS. &lt;a href="http://3.bp.blogspot.com/_SHRRgijB18E/Sf6wG0TfwOI/AAAAAAAAALk/NpX90_2-cbU/s1600-h/FragReport.JPG"&gt;&lt;img id="BLOGGER_PHOTO_ID_5331892640019366114" style="FLOAT: right; MARGIN: 0px 0px 10px 10px; WIDTH: 175px; CURSOR: hand; HEIGHT: 200px" alt="" src="http://3.bp.blogspot.com/_SHRRgijB18E/Sf6wG0TfwOI/AAAAAAAAALk/NpX90_2-cbU/s200/FragReport.JPG" border="0" /&gt;&lt;/a&gt;You need to use the system defragmentation tool (or file copy) to defrag, which is not easy while SQL Server has the file open. A much better strategy is to avoid OS fragmentation. To do this simply ask SQL Server to grow files at a reasonable size. 1 MB is not a good increment for a terabye database. Also the default of 10% is not very good, since an active transaction that requires additional space will have to wait while the entire 10% is formatted before it can continue. Not a good thing to happen in a high OLTP environment. Notice how the Defrag utility is showing the number of fragments per file. You should be a little bit concerned about database files with over about 100 fragments, and anything over 1000 fragments may well be causing you performance issues.&lt;/span&gt;&lt;/div&gt;&lt;br /&gt;&lt;div&gt;&lt;span style="color:#000099;"&gt;&lt;span style="font-family:arial;"&gt;The best strategy is to preallocat&lt;/span&gt;&lt;span style="font-family:arial;"&gt;e the space in large chunks, during quiet times, so that no transaction has to wait and you avoid OS &lt;a href="http://3.bp.blogspot.com/_SHRRgijB18E/Sf6uWdZtS1I/AAAAAAAAALc/Me-wtgiqdzo/s1600-h/SQLFiles.JPG"&gt;&lt;span style="font-family:arial;"&gt;&lt;img id="BLOGGER_PHOTO_ID_5331890709726055250" style="FLOAT: right; MARGIN: 0px 0px 10px 10px; WIDTH: 320px; CURSOR: hand; HEIGHT: 103px" alt="" src="http://3.bp.blogspot.com/_SHRRgijB18E/Sf6uWdZtS1I/AAAAAAAAALc/Me-wtgiqdzo/s320/SQLFiles.JPG" border="0" /&gt;&lt;/span&gt;&lt;/a&gt;fragmentation&lt;/span&gt;&lt;/span&gt;&lt;span style="font-family:arial;"&gt;&lt;span style="color:#000099;"&gt;.&lt;/span&gt; However, just in case the db file is out of space give it a smallish allocation size of something like 200MB. This is a compromise between not having the file too fragmented and not having online transactions wait for several minutes for formatting. &lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3689647052791307812-3683567435624181333?l=richardlees.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://richardlees.blogspot.com/feeds/3683567435624181333/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3689647052791307812&amp;postID=3683567435624181333' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3689647052791307812/posts/default/3683567435624181333'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3689647052791307812/posts/default/3683567435624181333'/><link rel='alternate' type='text/html' href='http://richardlees.blogspot.com/2009/05/sql-server-fragmentation.html' title='SQL Server Fragmentation'/><author><name>Richard Lees</name><uri>http://www.blogger.com/profile/05671716466559973540</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='31' height='32' src='http://2.bp.blogspot.com/-AdeWScnQel8/Thj9ePsjpQI/AAAAAAAAAew/XnIn3xt4a0s/s220/RichardUni.png'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://3.bp.blogspot.com/_SHRRgijB18E/Sf6tfu5U2xI/AAAAAAAAALM/bbaifxV-bZI/s72-c/Frag.JPG' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3689647052791307812.post-3155405035447048610</id><published>2009-04-21T06:15:00.009+10:00</published><updated>2009-04-21T06:40:41.064+10:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Web Analytics using Microsoft Analysis Services'/><title type='text'>Photo Switcheroo</title><content type='html'>&lt;a href="http://richardlees.com.au/Pages/AboutRichardLees.aspx"&gt;&lt;img id="BLOGGER_PHOTO_ID_5326875919761020850" style="FLOAT: left; MARGIN: 0px 10px 10px 0px; WIDTH: 320px; CURSOR: hand; HEIGHT: 202px" alt="" src="http://4.bp.blogspot.com/_SHRRgijB18E/SezdbQ_wF7I/AAAAAAAAALE/0iD8YpC3HDY/s320/SurfingOriginal.jpg" border="0" /&gt;&lt;/a&gt;&lt;a href="http://richardlees.com.au/Pages/AboutRichardLees.aspx"&gt;&lt;img id="BLOGGER_PHOTO_ID_5326875830396334386" style="DISPLAY: block; MARGIN: 0px auto 10px; WIDTH: 320px; CURSOR: hand; HEIGHT: 246px; TEXT-ALIGN: center" alt="" src="http://2.bp.blogspot.com/_SHRRgijB18E/SezdWEFjMTI/AAAAAAAAAK8/aTT9ARC8N9g/s320/surfing.JPG" border="0" /&gt;&lt;/a&gt;Everyone knows that my web logs go into a cube and provide a demonstration of how powerful OLAP cubes are (&lt;a href="http://richardlees.com.au/Sites/Demonstrations"&gt;http://RichardLees.com.au/Sites/Demonstrations&lt;/a&gt;) Well I have noticed an increasing number of hits from personal sites that have links to photos on my site. One in particular - the surfing photo on at top left. I am not sure if they do it for copyright or web performance reasons. Anyway, I have switched the top left photo for one of myself - top right. Is that an awful thing to do? If my conscience gets the better of me, I will switch them back.&lt;br /&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;&lt;br /&gt;&lt;div&gt;Here is a list of a few of the sites that are using the photo and now have my photo. No doubt they will notice the old guy on the surf ski, at some stage, and update the photo.&lt;/div&gt;&lt;br /&gt;&lt;div&gt;&lt;a href="http://www4.ncsu.edu/~btcapo2/"&gt;http://www4.ncsu.edu/~btcapo2/&lt;/a&gt;&lt;br /&gt;&lt;a href="http://forum.fok.nl/topic/1222710/5/50"&gt;http://forum.fok.nl/topic/1222710/5/50&lt;/a&gt;&lt;br /&gt;&lt;a href="http://www.myspace.com/apinos"&gt;http://www.myspace.com/apinos&lt;/a&gt;&lt;br /&gt;&lt;a href="http://www.myspace.com/szandrathequeen"&gt;http://www.myspace.com/szandrathequeen&lt;/a&gt; &lt;/div&gt;&lt;br /&gt;&lt;div&gt;There are more sites using the surfing photo. If you are interested, you can query my weblogs cube and look for Referrers to Surfing.jpg It is one 0f the top referring targets. You can also see which sites are still using it. Just look for hits after April 20.&lt;/div&gt;&lt;br /&gt;&lt;div&gt;Richard&lt;br /&gt;&lt;/div&gt;&lt;div&gt;For real time OLAP and data mining demonstrations go to &lt;a href="http://richardlees.com.au/Sites/Demonstrations"&gt;http://RichardLees.com.au/Sites/Demonstrations&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 class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3689647052791307812-3155405035447048610?l=richardlees.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://richardlees.blogspot.com/feeds/3155405035447048610/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3689647052791307812&amp;postID=3155405035447048610' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3689647052791307812/posts/default/3155405035447048610'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3689647052791307812/posts/default/3155405035447048610'/><link rel='alternate' type='text/html' href='http://richardlees.blogspot.com/2009/04/photo-switcheroo.html' title='Photo Switcheroo'/><author><name>Richard Lees</name><uri>http://www.blogger.com/profile/05671716466559973540</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='31' height='32' src='http://2.bp.blogspot.com/-AdeWScnQel8/Thj9ePsjpQI/AAAAAAAAAew/XnIn3xt4a0s/s220/RichardUni.png'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://4.bp.blogspot.com/_SHRRgijB18E/SezdbQ_wF7I/AAAAAAAAALE/0iD8YpC3HDY/s72-c/SurfingOriginal.jpg' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3689647052791307812.post-8998687671330164599</id><published>2009-04-09T09:44:00.012+10:00</published><updated>2009-04-09T10:28:00.131+10:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='PerformancePoint thin client cube browsing'/><title type='text'>Try out Microsoft's zero footprint cube browser</title><content type='html'>Many people are not aware that Microsoft has a thin client cube browser. It is part of the current version of PerformancePoint Server, (although that will change in future and become part of SharePoint).&lt;br /&gt;&lt;a href="http://3.bp.blogspot.com/_SHRRgijB18E/Sd04tHUPpFI/AAAAAAAAAJ0/vZsm4KxnoFo/s1600-h/pp1.JPG"&gt;&lt;img id="BLOGGER_PHOTO_ID_5322472682331022418" style="FLOAT: right; MARGIN: 0px 0px 10px 10px; WIDTH: 400px; CURSOR: hand; HEIGHT: 264px" alt="" src="http://3.bp.blogspot.com/_SHRRgijB18E/Sd04tHUPpFI/AAAAAAAAAJ0/vZsm4KxnoFo/s400/pp1.JPG" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;I have PerformancePoint on my demonstration site, so you are able to try out the cube browser. All you need is Internet Explorer (version 6 or above). It doesn't appear to be supported by other cube browsers as yet. To try out the cube browser, simply go to my demonstration site, &lt;a href="http://richardlees.com.au/Sites/Demonstrations"&gt;http://RichardLees.com.au/Sites/Demonstrations&lt;/a&gt;. The home page is a PerformancePoint page with several SharePoint webparts. All the charts on this home page are from a real time database of web activity on RichardLees.com.au. Some of these web parts, I have written MDX to get exactly the chart I want. These charts are not interactive. However many of the charts I have created by simply slicing and filtering the data, which means that you can continue to slice and dice. For example, on the demonstrations home page try right clicking on the chart &lt;span style="color:#000099;"&gt;&lt;strong&gt;Top Referrers Last Few Months&lt;/strong&gt;&lt;/span&gt; column heading &lt;span style="color:#000099;"&gt;&lt;strong&gt;March&lt;/strong&gt;&lt;/span&gt; and select &lt;span style="color:#000099;"&gt;&lt;strong&gt;Drill Up&lt;/strong&gt;&lt;/span&gt;. That &lt;a href="http://2.bp.blogspot.com/_SHRRgijB18E/Sd06VzgrCPI/AAAAAAAAAJ8/sASZZG7y1yM/s1600-h/pp2.JPG"&gt;&lt;img id="BLOGGER_PHOTO_ID_5322474480900704498" style="FLOAT: right; MARGIN: 0px 0px 10px 10px; WIDTH: 266px; CURSOR: hand; HEIGHT: 288px" alt="" src="http://2.bp.blogspot.com/_SHRRgijB18E/Sd06VzgrCPI/AAAAAAAAAJ8/sASZZG7y1yM/s400/pp2.JPG" border="0" /&gt;&lt;/a&gt;will take you to the higher level on the Date dimension. Then right click on &lt;span style="color:#000099;"&gt;&lt;strong&gt;2009&lt;/strong&gt;&lt;/span&gt; and select &lt;span style="color:#000099;"&gt;drill up&lt;/span&gt; again, which will take you to the &lt;span style="color:#000099;"&gt;&lt;strong&gt;All&lt;/strong&gt;&lt;/span&gt; member of the &lt;span style="color:#000099;"&gt;&lt;strong&gt;date&lt;/strong&gt;&lt;/span&gt; dimension. Now right click on one of the row headings (these are referring sites to RichardLees.com.au) and select &lt;span style="color:#000099;"&gt;&lt;strong&gt;Drill Up&lt;/strong&gt;&lt;/span&gt;. Right click on the row heading again and select &lt;span style="color:#000099;"&gt;&lt;strong&gt;Drill Up&lt;/strong&gt;&lt;/span&gt;. Now you will be at the All level of the entire cube, and able to drill to any part of the cube. Note, you will also find a useful button on the right hand side of the webpart, which has an option to &lt;span style="color:#000099;"&gt;&lt;strong&gt;Open in new window&lt;/strong&gt;&lt;/span&gt;. This will give you much more real estate to work with. You can right click on any of the row/column heading and select &lt;span style="color:#000099;"&gt;&lt;strong&gt;Drill Down to&lt;/strong&gt;&lt;/span&gt; and select any dimension hierarchy such as &lt;span style="color:#000099;"&gt;&lt;strong&gt;Client Agent, Client Host, Date, Entry Exit, Hour, Machine, Operation, Port, Status HTTP, Status Win32&lt;/strong&gt;&lt;/span&gt; etc. Actually, there are more dimensions than the webpart can display. As you hover over each dimension the available hierarchies are displayed.&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;Some of the useful thin client functions you have are &lt;span style="color:#000099;"&gt;&lt;strong&gt;Show only&lt;/strong&gt;&lt;/span&gt;, &lt;strong&gt;&lt;span style="color:#000099;"&gt;Remove&lt;/span&gt;, &lt;span style="color:#000099;"&gt;Sort&lt;/span&gt;&lt;/strong&gt; (rows and/or columns), &lt;span style="color:#000099;"&gt;&lt;strong&gt;Report Type&lt;/strong&gt;&lt;/span&gt; (grid, column, line, stacked, stacke 100%), &lt;span style="color:#000099;"&gt;&lt;strong&gt;Pivot&lt;/strong&gt;&lt;/span&gt;, &lt;span style="color:#000099;"&gt;&lt;strong&gt;Filter&lt;/strong&gt;&lt;/span&gt;. &lt;a href="http://2.bp.blogspot.com/_SHRRgijB18E/Sd08MNPqVFI/AAAAAAAAAKE/wj57sl3hbYU/s1600-h/pp3.JPG"&gt;&lt;/a&gt;You really can slice and dice to any part of the cube you desire. &lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;Try it out on &lt;a href="http://2.bp.blogspot.com/_SHRRgijB18E/Sd08cNDeY5I/AAAAAAAAAKM/VQhtvKF9BM4/s1600-h/pp3.JPG"&gt;&lt;img id="BLOGGER_PHOTO_ID_5322476789860033426" style="FLOAT: right; MARGIN: 0px 0px 10px 10px; WIDTH: 393px; CURSOR: hand; HEIGHT: 348px" alt="" src="http://2.bp.blogspot.com/_SHRRgijB18E/Sd08cNDeY5I/AAAAAAAAAKM/VQhtvKF9BM4/s400/pp3.JPG" border="0" /&gt;&lt;/a&gt;&lt;a href="http://richardlees.com.au/Sites/Demonstrations"&gt;http://RichardLees.com.au/Sites/Demonstrations&lt;/a&gt; If you are good, you should be able to navigate to filter out your own activity on this site and see where you have been. There are other cubes on this site that you can also navigate such as the &lt;strong&gt;&lt;span style="color:#000099;"&gt;&lt;span style="color:#000099;"&gt;New&lt;/span&gt; Zealand census&lt;/span&gt;, &lt;span style="color:#000099;"&gt;Perfmon&lt;/span&gt;&lt;/strong&gt; stats from my demonstration machines and &lt;span style="color:#000099;"&gt;&lt;strong&gt;Foodmart&lt;/strong&gt;&lt;/span&gt;. If you are having trouble understanding how to use the functionality and want a simpler cube, use the New Zealand census cube, since it has a much simpler structure and is easy to consume.&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 class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3689647052791307812-8998687671330164599?l=richardlees.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://richardlees.blogspot.com/feeds/8998687671330164599/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3689647052791307812&amp;postID=8998687671330164599' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3689647052791307812/posts/default/8998687671330164599'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3689647052791307812/posts/default/8998687671330164599'/><link rel='alternate' type='text/html' href='http://richardlees.blogspot.com/2009/04/try-out-microsofts-zero-footprint-cube.html' title='Try out Microsoft&apos;s zero footprint cube browser'/><author><name>Richard Lees</name><uri>http://www.blogger.com/profile/05671716466559973540</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='31' height='32' src='http://2.bp.blogspot.com/-AdeWScnQel8/Thj9ePsjpQI/AAAAAAAAAew/XnIn3xt4a0s/s220/RichardUni.png'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://3.bp.blogspot.com/_SHRRgijB18E/Sd04tHUPpFI/AAAAAAAAAJ0/vZsm4KxnoFo/s72-c/pp1.JPG' height='72' width='72'/><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3689647052791307812.post-5168834444507146259</id><published>2009-03-31T07:52:00.004+11:00</published><updated>2009-03-31T08:07:03.422+11:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Internet Explorer 8 market share real time analytics'/><title type='text'>Internet Explorer 8 is Now Available</title><content type='html'>&lt;a href="http://richardlees.com.au/Sites/Demonstrations"&gt;&lt;img id="BLOGGER_PHOTO_ID_5319087159230168802" style="FLOAT: right; MARGIN: 0px 0px 10px 10px; WIDTH: 400px; CURSOR: hand; HEIGHT: 250px" alt="" src="http://3.bp.blogspot.com/_SHRRgijB18E/SdExlu9BfuI/AAAAAAAAAJs/3DJtvBOKAN8/s400/ClientAgentShare.JPG" border="0" /&gt;&lt;/a&gt;There has been a jump in Internet Explorer 8 activity on my site since it was release a couple of weeks ago. It jumped from about 5% of site activity to about 15%. Not surprisingly this increase has come from IE6 and IE7. However, what really intrigues me is how much Internet Explorer 6 is still being used. It still represents a little over 20% of total activity. See chart on right, or for a live graph see &lt;a href="http://richardlees.com.au/Sites/Demonstrations"&gt;http://RichardLees.com.au/Sites/Demonstrations&lt;/a&gt; The data is kept up to within 15 minutes of real time. You are able to slice and dice the web activity via the PerformancePoint graphs or "ThinSlicer on Weblogs". Note, these thin client cube browsers work best with IE.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3689647052791307812-5168834444507146259?l=richardlees.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://richardlees.blogspot.com/feeds/5168834444507146259/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3689647052791307812&amp;postID=5168834444507146259' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3689647052791307812/posts/default/5168834444507146259'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3689647052791307812/posts/default/5168834444507146259'/><link rel='alternate' type='text/html' href='http://richardlees.blogspot.com/2009/03/internet-explorer-8-is-now-available.html' title='Internet Explorer 8 is Now Available'/><author><name>Richard Lees</name><uri>http://www.blogger.com/profile/05671716466559973540</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='31' height='32' src='http://2.bp.blogspot.com/-AdeWScnQel8/Thj9ePsjpQI/AAAAAAAAAew/XnIn3xt4a0s/s220/RichardUni.png'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://3.bp.blogspot.com/_SHRRgijB18E/SdExlu9BfuI/AAAAAAAAAJs/3DJtvBOKAN8/s72-c/ClientAgentShare.JPG' height='72' width='72'/><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3689647052791307812.post-7803914986919095638</id><published>2009-03-29T08:14:00.003+11:00</published><updated>2009-03-29T08:55:12.655+11:00</updated><title type='text'>Nigel Pendse' BI Survey 8</title><content type='html'>&lt;a href="http://4.bp.blogspot.com/_SHRRgijB18E/Sc6buiGJ9TI/AAAAAAAAAJk/2qzoFkXHe2A/s1600-h/bisurvey8.JPG"&gt;&lt;img id="BLOGGER_PHOTO_ID_5318359433700046130" style="FLOAT: right; MARGIN: 0px 0px 10px 10px; WIDTH: 200px; CURSOR: hand; HEIGHT: 102px" alt="" src="http://4.bp.blogspot.com/_SHRRgijB18E/Sc6buiGJ9TI/AAAAAAAAAJk/2qzoFkXHe2A/s200/bisurvey8.JPG" border="0" /&gt;&lt;/a&gt;As a participant, I have just received a complimentary copy of BI Survey 8.  I have been reading Nigel's surveys since 1999.  While I often disagreed with some of his findings, I have always considered him the best analyst in the Business Intelligence space.&lt;br /&gt;&lt;br /&gt;Unfortunately, I am not able to quote anything from the BI Survey.  Nigel Pendse is remunerated by subscribers (not vendors) which makes his surveys refreshingly independent.&lt;br /&gt;&lt;br /&gt;I would encourage any organization, about to embark on a data warehouse/business intelligence project, to subscribe to this analysis.   Unless you are a seasoned BI developer, you will benefit from many insights and be much better armed before being caressed by a dapper BI sales team.&lt;br /&gt;&lt;br /&gt;The current survey includes analyses on the selection process, who succeeded by vendor, biggest outstanding issues, size of deployment etc. etc.   Those who are familiar with Nigel’s reports will know that he is very candid when talking about vendors and products.  He really doesn’t hold back, understandably since this is what his subscribers are paying for.&lt;br /&gt;&lt;br /&gt;I encourage openness in our BI industry.  My hope is that the more informed organizations are, the better the decisions they will make, improving the effectiveness and reputation of our BI industry.&lt;br /&gt;&lt;br /&gt;You can download a free chapter or subscribe to the full survey on &lt;a href="http://www.bi-survey.com/"&gt;http://www.bi-survey.com&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Richard&lt;br /&gt;For real-time online BI demonstrations goto &lt;a href="http://richardlees.com.au/Sites/Demonstrations"&gt;http://RichardLees.com.au/Sites/Demonstrations&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3689647052791307812-7803914986919095638?l=richardlees.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://richardlees.blogspot.com/feeds/7803914986919095638/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3689647052791307812&amp;postID=7803914986919095638' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3689647052791307812/posts/default/7803914986919095638'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3689647052791307812/posts/default/7803914986919095638'/><link rel='alternate' type='text/html' href='http://richardlees.blogspot.com/2009/03/nigel-pendse-bi-survey-8.html' title='Nigel Pendse&apos; BI Survey 8'/><author><name>Richard Lees</name><uri>http://www.blogger.com/profile/05671716466559973540</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='31' height='32' src='http://2.bp.blogspot.com/-AdeWScnQel8/Thj9ePsjpQI/AAAAAAAAAew/XnIn3xt4a0s/s220/RichardUni.png'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://4.bp.blogspot.com/_SHRRgijB18E/Sc6buiGJ9TI/AAAAAAAAAJk/2qzoFkXHe2A/s72-c/bisurvey8.JPG' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3689647052791307812.post-8732356984752284986</id><published>2009-03-06T15:59:00.004+11:00</published><updated>2009-03-06T16:08:12.328+11:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Error 44 The following system error occurred:  No mapping between account names and security IDs was done.'/><title type='text'>Error 44. No mapping between account names and security IDs was done</title><content type='html'>&lt;a href="http://3.bp.blogspot.com/_SHRRgijB18E/SbCvGYSXqAI/AAAAAAAAAJU/VPj3IE5Td-I/s1600-h/error44.JPG"&gt;&lt;img id="BLOGGER_PHOTO_ID_5309936484803389442" style="FLOAT: right; MARGIN: 0px 0px 10px 10px; WIDTH: 400px; CURSOR: hand; HEIGHT: 60px" alt="" src="http://3.bp.blogspot.com/_SHRRgijB18E/SbCvGYSXqAI/AAAAAAAAAJU/VPj3IE5Td-I/s400/error44.JPG" border="0" /&gt;&lt;/a&gt;Have you ever received this error?&lt;br /&gt;&lt;div&gt;&lt;span style="color:#ff0000;"&gt;Error 44 The following system error occurred: No mapping between account names and security IDs was done.&lt;/span&gt;&lt;/div&gt;&lt;br /&gt;&lt;div&gt;You will receive this error when you have windows accounts or groups in an AS project that you are deploying to a server in a different domain. Ie. the server does not recognise these accounts.&lt;/div&gt;&lt;br /&gt;&lt;div&gt;A simple work around is to leave the windows users and groups out of the roles and add them in from SQL Server Management Studio. Another option, if it is a common deployment is to use AMO and add the appropriate accounts automatically.  Also, if you use the AS Deployment wizard there are options to omit role deployment.&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;&lt;div&gt;Richard&lt;/div&gt;&lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3689647052791307812-8732356984752284986?l=richardlees.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://richardlees.blogspot.com/feeds/8732356984752284986/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3689647052791307812&amp;postID=8732356984752284986' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3689647052791307812/posts/default/8732356984752284986'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3689647052791307812/posts/default/8732356984752284986'/><link rel='alternate' type='text/html' href='http://richardlees.blogspot.com/2009/03/error-44-no-mapping-between-account.html' title='Error 44. No mapping between account names and security IDs was done'/><author><name>Richard Lees</name><uri>http://www.blogger.com/profile/05671716466559973540</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='31' height='32' src='http://2.bp.blogspot.com/-AdeWScnQel8/Thj9ePsjpQI/AAAAAAAAAew/XnIn3xt4a0s/s220/RichardUni.png'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://3.bp.blogspot.com/_SHRRgijB18E/SbCvGYSXqAI/AAAAAAAAAJU/VPj3IE5Td-I/s72-c/error44.JPG' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3689647052791307812.post-8404612301151390599</id><published>2009-02-18T19:32:00.003+11:00</published><updated>2009-02-18T20:13:47.579+11:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='data types'/><category scheme='http://www.blogger.com/atom/ns#' term='Analysis Services'/><category scheme='http://www.blogger.com/atom/ns#' term='Fact Table OLAP RDBMS Performance'/><category scheme='http://www.blogger.com/atom/ns#' term='cube design'/><title type='text'>Space Considerations for Various OLAP Data Types</title><content type='html'>&lt;a href="http://2.bp.blogspot.com/_SHRRgijB18E/SZvPgL-4rMI/AAAAAAAAAJM/qtHNRLY_CN4/s1600-h/DataTypes.JPG"&gt;&lt;img id="BLOGGER_PHOTO_ID_5304061138038205634" style="FLOAT: right; MARGIN: 0px 0px 10px 10px; WIDTH: 367px; CURSOR: hand; HEIGHT: 400px" alt="" src="http://2.bp.blogspot.com/_SHRRgijB18E/SZvPgL-4rMI/AAAAAAAAAJM/qtHNRLY_CN4/s400/DataTypes.JPG" border="0" /&gt;&lt;/a&gt; &lt;div&gt;Data types can have huge space and performance implications on large SQL tables, and there are similar implications for large OLAP cubes. We all know that double word numbers take up more space than single word numbers, but what exactly is the difference? &lt;/div&gt;&lt;br /&gt;&lt;div&gt;I have performed a simple benchmark to compare the space used of the various data types. My tests involved a &lt;/div&gt;&lt;ul&gt;&lt;li&gt;44 Million row fact table&lt;/li&gt;&lt;li&gt;3 measures containing values between -99 and 1039.22&lt;/li&gt;&lt;li&gt;5 dimensions&lt;/li&gt;&lt;li&gt;SQL Server 2008&lt;/li&gt;&lt;li&gt;Zero aggregations&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;I used 6 datatypes in SQL Server tables. The SQL data types I employed were&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Money&lt;/li&gt;&lt;li&gt;Float&lt;/li&gt;&lt;li&gt;Decimal&lt;/li&gt;&lt;li&gt;Bigint&lt;/li&gt;&lt;li&gt;Integer&lt;/li&gt;&lt;li&gt;Real&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;For the Money data type, I created two cubes, since Analysis Services defaulted the Money data type to Double. I created one cube with the default Double data type, and another with the cube data type as Currency.&lt;/p&gt;&lt;p&gt;The results show that the best performing (in terms of size) were&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Bigint and Integer (41% smaller than Double)&lt;/li&gt;&lt;li&gt;Currency (25% smaller than Double)&lt;/li&gt;&lt;li&gt;Single (3% smaller than Double)&lt;/li&gt;&lt;li&gt;Double&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;Note, Sometimes the data type you choose is determined (or constrained) by the domain of the numbers you are aggregating. For example, if your numbers contain significant digits after the decimal point, then Bigint and Integer are inappropriate, unless you are happy to round them. Likewise, for imprecise numbers, Double gives you the most precision, which you might need. But just because the underlying SQL table uses a single or double byte precision data type, if the number is always an integer, you might as well tell Analysis Services to use a Bigint and save on space.&lt;/p&gt;&lt;p&gt;It shouldn't surprise you that the binary numbers (Bigint and Integer) are the smallest on disk, nor that they took the same space as each other. Analysis Services compresses all the cells before writing to disk, and the binary numbers will tend to have lots of leading zeroes, which compress very well. Similarly, but to a lessor extent, Currency (really a packed decimal) compresses next best.&lt;/p&gt;&lt;p&gt;Just in case you are wondering why bother fiddling with data types when the cube is only a gigabyte or two in size, and disk space is plentiful. The reason is that if you can save 40% in your cube size, processing and querying will be noticeably faster. With a smaller cube, your cache will warm faster, and if the entire cube doesn't fit in RAM, you will have a greator proportion of your cube in RAM with a smaller cube.  Ie. your queries will run much faster.&lt;/p&gt;&lt;p&gt;Richard&lt;/p&gt;&lt;p&gt;for real-time online OLAP demonstrations go to &lt;a href="http://richardlees.com.au/Sites/Demonstrations"&gt;http://RichardLees.com.au/Sites/Demonstrations&lt;/a&gt; &lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3689647052791307812-8404612301151390599?l=richardlees.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://richardlees.blogspot.com/feeds/8404612301151390599/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3689647052791307812&amp;postID=8404612301151390599' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3689647052791307812/posts/default/8404612301151390599'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3689647052791307812/posts/default/8404612301151390599'/><link rel='alternate' type='text/html' href='http://richardlees.blogspot.com/2009/02/space-considerations-for-various-olap.html' title='Space Considerations for Various OLAP Data Types'/><author><name>Richard Lees</name><uri>http://www.blogger.com/profile/05671716466559973540</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='31' height='32' src='http://2.bp.blogspot.com/-AdeWScnQel8/Thj9ePsjpQI/AAAAAAAAAew/XnIn3xt4a0s/s220/RichardUni.png'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://2.bp.blogspot.com/_SHRRgijB18E/SZvPgL-4rMI/AAAAAAAAAJM/qtHNRLY_CN4/s72-c/DataTypes.JPG' height='72' width='72'/><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3689647052791307812.post-6491321654188095540</id><published>2009-02-09T19:45:00.004+11:00</published><updated>2009-02-09T19:57:58.790+11:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Row count fast high performing SQL count(*)'/><title type='text'>How many rows in a table</title><content type='html'>&lt;a href="http://3.bp.blogspot.com/_SHRRgijB18E/SY_vxdzEzII/AAAAAAAAAJE/qaDWDpbX-pQ/s1600-h/HowManyRows.JPG"&gt;&lt;img id="BLOGGER_PHOTO_ID_5300718919529843842" style="FLOAT: right; MARGIN: 0px 0px 10px 10px; WIDTH: 400px; CURSOR: hand; HEIGHT: 364px" alt="" src="http://3.bp.blogspot.com/_SHRRgijB18E/SY_vxdzEzII/AAAAAAAAAJE/qaDWDpbX-pQ/s400/HowManyRows.JPG" border="0" /&gt;&lt;/a&gt;A very common DBA/developer task is to query how many rows there are in a table. Of course the natural way to do this is to write a query such as&lt;br /&gt;&lt;br /&gt;&lt;span style="color:#cc66cc;"&gt;SELECT COUNT(*) FROM MyTable&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;However, this query will be slow for a very large table, particularly if it only has a clustered index. Why not just ask SQL Server for the stats on the table? If your SQL Server database has the default settings, it will be continually sampling the table and will generally have a very good idea of how many records there are. A very simple way of asking the optimiser for these stats is to write the following query&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="color:#cc66cc;"&gt;SELECT * FROM MyTable&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;and click on the &lt;strong&gt;Display Estimated Execution Plan&lt;/strong&gt; button, which looks a bit like 3 green boxes and a caption. SQL Server will tell you, amongst other things, the number of records it expects to read as you hover over the scan object.&lt;br /&gt;&lt;br /&gt;I encourage developers to click on this button, not just to get a quick count, but so that they start to use the showplan more often. In my view, the more often you use showplan and the more familiar you get with the optimiser, the higher performing SQL you will write.&lt;br /&gt;&lt;br /&gt;Richard&lt;br /&gt;&lt;br /&gt;for real time SQL, OLAP, Data Mining, PerformancePoint, Excel Services see &lt;a href="http://richardlees.com.au/Sites/Demonstrations"&gt;http://RichardLees.com.au/Sites/Demonstrations&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3689647052791307812-6491321654188095540?l=richardlees.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://richardlees.blogspot.com/feeds/6491321654188095540/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3689647052791307812&amp;postID=6491321654188095540' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3689647052791307812/posts/default/6491321654188095540'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3689647052791307812/posts/default/6491321654188095540'/><link rel='alternate' type='text/html' href='http://richardlees.blogspot.com/2009/02/how-many-rows-in-table.html' title='How many rows in a table'/><author><name>Richard Lees</name><uri>http://www.blogger.com/profile/05671716466559973540</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='31' height='32' src='http://2.bp.blogspot.com/-AdeWScnQel8/Thj9ePsjpQI/AAAAAAAAAew/XnIn3xt4a0s/s220/RichardUni.png'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://3.bp.blogspot.com/_SHRRgijB18E/SY_vxdzEzII/AAAAAAAAAJE/qaDWDpbX-pQ/s72-c/HowManyRows.JPG' height='72' width='72'/><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3689647052791307812.post-4244601851953910780</id><published>2009-02-02T13:06:00.003+11:00</published><updated>2009-02-02T13:16:21.153+11:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Microsoft discontinuing PerformancePoint Server 2007'/><title type='text'>Microsoft is discontinuing PerformancePoint Server</title><content type='html'>This is hard for me to fathom, let alone beleive.  &lt;a href="http://www.informationweek.com/news/business_intelligence/analytics/showArticle.jhtml?articleID=212902915"&gt;http://www.informationweek.com/news/business_intelligence/analytics/showArticle.jhtml?articleID=212902915&lt;/a&gt; &lt;br /&gt;&lt;br /&gt;Microsoft was a new entrant to the Financial Planning software arena with PPS back in 2007.  However it's V1 product received positive reviews (see &lt;a href="http://www.olapreport.com/"&gt;Nigel Pendse's report&lt;/a&gt;).   However, it appears that they won't be doing any more work on the planning component after the release of PPS 2007 sp3, and will roll the dashboard software into SharePoint Server.&lt;br /&gt;&lt;br /&gt;I am pleased to hear that they are continuing the dashboard software, since that's the component that I often use.   See my demonstration site on &lt;a href="http://richardlees.com.au/Sites/Demonstrations"&gt;http://RichardLees.com.au/Sites/Demonstrations&lt;/a&gt; and it makes sense to roll it into SharePoint.  Hopefully this will mean that it will be easy to license it for Internet use.&lt;br /&gt;&lt;br /&gt;But I can't help but feel that they are making a mistake in dropping the planning product that could have a huge future.&lt;br /&gt;&lt;br /&gt;Richard&lt;br /&gt;&lt;a href="http://richardlees.com.au/"&gt;http://RichardLees.com.au&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3689647052791307812-4244601851953910780?l=richardlees.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://richardlees.blogspot.com/feeds/4244601851953910780/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3689647052791307812&amp;postID=4244601851953910780' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3689647052791307812/posts/default/4244601851953910780'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3689647052791307812/posts/default/4244601851953910780'/><link rel='alternate' type='text/html' href='http://richardlees.blogspot.com/2009/02/microsoft-is-discontinuing.html' title='Microsoft is discontinuing PerformancePoint Server'/><author><name>Richard Lees</name><uri>http://www.blogger.com/profile/05671716466559973540</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='31' height='32' src='http://2.bp.blogspot.com/-AdeWScnQel8/Thj9ePsjpQI/AAAAAAAAAew/XnIn3xt4a0s/s220/RichardUni.png'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3689647052791307812.post-4249611415125349977</id><published>2009-01-30T10:10:00.004+11:00</published><updated>2009-01-30T10:22:10.395+11:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Web Analytics Response Time'/><title type='text'>Is my web site getting slower</title><content type='html'>&lt;a href="http://richardlees.com.au/Sites/Demonstrations"&gt;&lt;img id="BLOGGER_PHOTO_ID_5296857323034376386" style="FLOAT: right; MARGIN: 0px 0px 10px 10px; WIDTH: 400px; CURSOR: hand; HEIGHT: 158px" alt="" src="http://3.bp.blogspot.com/_SHRRgijB18E/SYI3q91j-MI/AAAAAAAAAI0/4sfTwqdC33w/s400/WebReponseTime.JPG" border="0" /&gt;&lt;/a&gt;Strange things have been happening in our household over the Christmas holidays. My web dashboard tells me that as from January 1st, my web server's average respone times have deteriorated badly.  A few days ago my ISP informed me that I had exceeded my monthly limit, and one of my sons has been watching a lot of movies on his laptop lately.  &lt;div&gt; &lt;/div&gt;&lt;div&gt;The chart above (you can see the latest from &lt;a href="http://richardlees.com.au/Sites/Demonstrations"&gt;http://RichardLees.com.au/Sites/Demonstrations&lt;/a&gt;) shows the dramatic slow down of the response times since the downloads were happening.  Has anyone using my site noticed the slow down?   &lt;/div&gt;&lt;div&gt; &lt;/div&gt;&lt;div&gt;My son confesses that he &lt;em&gt;might &lt;/em&gt;have used up a little of our bandwidth downloading these movies and from now on he will limit it to one at a time and mostly after hours.  Also, I think that since school has started he won't have so much time to watch movies on his laptop.  At least I am hoping.  &lt;/div&gt;&lt;div&gt; &lt;/div&gt;&lt;div&gt;What would be really nice is an ability to give a lower priority to these downloads (happening in the background) over online http requests to my server.  I am not much of a network engineer.  Does anyone know if there is a simple way of prioritising this adsl2 traffic? &lt;/div&gt;&lt;div&gt; &lt;/div&gt;&lt;div&gt;Richard&lt;/div&gt;&lt;div&gt;&lt;a href="http://richadlees.com.au/"&gt;http://RichadLees.com.au&lt;/a&gt; &lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3689647052791307812-4249611415125349977?l=richardlees.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://richardlees.blogspot.com/feeds/4249611415125349977/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3689647052791307812&amp;postID=4249611415125349977' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3689647052791307812/posts/default/4249611415125349977'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3689647052791307812/posts/default/4249611415125349977'/><link rel='alternate' type='text/html' href='http://richardlees.blogspot.com/2009/01/is-my-web-site-getting-slower.html' title='Is my web site getting slower'/><author><name>Richard Lees</name><uri>http://www.blogger.com/profile/05671716466559973540</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='31' height='32' src='http://2.bp.blogspot.com/-AdeWScnQel8/Thj9ePsjpQI/AAAAAAAAAew/XnIn3xt4a0s/s220/RichardUni.png'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://3.bp.blogspot.com/_SHRRgijB18E/SYI3q91j-MI/AAAAAAAAAI0/4sfTwqdC33w/s72-c/WebReponseTime.JPG' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3689647052791307812.post-664608253466711639</id><published>2009-01-10T10:36:00.008+11:00</published><updated>2009-01-10T11:06:10.036+11:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='drllthrough Analysis Services OLAP drill through'/><title type='text'>Drill Through in Analysis Services</title><content type='html'>Drill through functionality was not included in the first version of OLAP technology from Microsoft (SQL Server 7). I found it such sought after requirement that I created my own drill through for ThinSlicer (the first thin client cube browser). It would simply generate an SQL statement based on the dimension members and assumed that the level names were the same as the column names. Then in 2000, drill though functionality was included in Analysis Services, so I removed my custom code. &lt;div&gt;&lt;br /&gt;&lt;div&gt;However, things changed in 2005 (SQL Server 2005) when drill through architecture was changed so that a drill through request did not go to the relational database (for the atomic records) rather, it returned the most detailed cell data from the cube. This, was a huge change in functionality, and some would argue that is not "drill through" but "drill bottom". As you can imagine, there are security implications with drilling through to relational data. In the cube, you can include quite complex security functionality, which would be difficult to honor in a drill through. This might be the reason drill through was dramatically changed in SQL Server 2005.&lt;a href="http://2.bp.blogspot.com/_SHRRgijB18E/SWfizCw0FVI/AAAAAAAAAIc/FZQ2fqozBms/s1600-h/DrillThroughFrom.JPG"&gt;&lt;img id="BLOGGER_PHOTO_ID_5289445653912753490" style="FLOAT: right; MARGIN: 0px 0px 10px 10px; WIDTH: 400px; CURSOR: hand; HEIGHT: 162px" alt="" src="http://2.bp.blogspot.com/_SHRRgijB18E/SWfizCw0FVI/AAAAAAAAAIc/FZQ2fqozBms/s400/DrillThroughFrom.JPG" border="0" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;div&gt;I hope that one day SQL Server will bring back drill through to relational data. It is such a useful function for many cubes. In the meantime, if you want to include drill through to the atomic records, you will need to create a cube action and write some custom code. &lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;One thing to take into account when you do drill through to atomic data in the relational database is SQL performance. As we know, cube queries tend to have more reliable response times than ad hoc SQL queries. If you write your own customer drill through functionality, I have two suggestions&lt;/div&gt;&lt;ol&gt;&lt;li&gt;Use "Top n" in your query (or equivalent from a non ms db). The optimiser takes this into account.&lt;/li&gt;&lt;li&gt;Create a non clustered index on each of the foreign keys&lt;a href="http://2.bp.blogspot.com/_SHRRgijB18E/SWfjBF6sVWI/AAAAAAAAAIk/OfEy5XlILM0/s1600-h/DrillThroughDetails.JPG"&gt;&lt;img id="BLOGGER_PHOTO_ID_5289445895277663586" style="FLOAT: right; MARGIN: 0px 0px 10px 10px; WIDTH: 400px; CURSOR: hand; HEIGHT: 272px" alt="" src="http://2.bp.blogspot.com/_SHRRgijB18E/SWfjBF6sVWI/AAAAAAAAAIk/OfEy5XlILM0/s400/DrillThroughDetails.JPG" border="0" /&gt;&lt;/a&gt;&lt;/li&gt;&lt;/ol&gt;&lt;div&gt;For an example of how easy drill through is simply go to my demonstrations home page &lt;a href="http://richardlees.com.au/Sites/Demonstrations"&gt;http://RichardLees.com.au/Sites/Demonstrations&lt;/a&gt;, right click on one of the charts and select "show details". Here is an example of the "drill through" records you will be returned.&lt;/div&gt;&lt;br /&gt;&lt;div&gt;I didn't need to write any code for this drill through. The graph is presented by PerformancePoint, and like any good cube browser, it simply honors the drill through functionality of Analysis Services.&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;Enabling drill through on the cube, couldn't be simpler, you simply enable drill through on the role.&lt;/div&gt;&lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;Richard&lt;/div&gt;&lt;br /&gt;&lt;div&gt;&lt;a href="http://richardlees.com.au/"&gt;http://richardlees.com.au/&lt;/a&gt; &lt;/div&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3689647052791307812-664608253466711639?l=richardlees.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://richardlees.blogspot.com/feeds/664608253466711639/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3689647052791307812&amp;postID=664608253466711639' title='5 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3689647052791307812/posts/default/664608253466711639'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3689647052791307812/posts/default/664608253466711639'/><link rel='alternate' type='text/html' href='http://richardlees.blogspot.com/2009/01/using-drill-through-in-analysis.html' title='Drill Through in Analysis Services'/><author><name>Richard Lees</name><uri>http://www.blogger.com/profile/05671716466559973540</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='31' height='32' src='http://2.bp.blogspot.com/-AdeWScnQel8/Thj9ePsjpQI/AAAAAAAAAew/XnIn3xt4a0s/s220/RichardUni.png'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://2.bp.blogspot.com/_SHRRgijB18E/SWfizCw0FVI/AAAAAAAAAIc/FZQ2fqozBms/s72-c/DrillThroughFrom.JPG' height='72' width='72'/><thr:total>5</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3689647052791307812.post-3632534054568088396</id><published>2008-12-21T11:15:00.016+11:00</published><updated>2008-12-21T11:50:57.765+11:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Web Analytics of a third party blog activity'/><title type='text'>Who's Reading This Blog?</title><content type='html'>&lt;a href="http://4.bp.blogspot.com/_SHRRgijB18E/SU2OFSD7JAI/AAAAAAAAAIM/njjRohpS5Gw/s1600-h/BlogActivity.JPG"&gt;&lt;img id="BLOGGER_PHOTO_ID_5282034159374246914" style="FLOAT: right; MARGIN: 0px 0px 10px 10px; WIDTH: 400px; CURSOR: hand; HEIGHT: 311px" alt="" src="http://4.bp.blogspot.com/_SHRRgijB18E/SU2OFSD7JAI/AAAAAAAAAIM/njjRohpS5Gw/s400/BlogActivity.JPG" border="0" /&gt;&lt;/a&gt;I don't host this blog site, consequently I don't have access to the logs to analyse activity on the site. However, that doesn't mean that I can't monitor blog activity, and indeed, let all of you analyse the activity of this blog.&lt;br /&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;&lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;&lt;div&gt;This is an old trick really. I have simply embedded an tag in my profile that has a reference to &lt;a href="http://easternmining.com.au/blogs.gif"&gt;http://EasternMining.com.au/blogs.gif&lt;/a&gt; That resource does not exist, but it requests my own web server, which I have an automated process to add logs to a publicly available cube. You can see lots of real-time web log analysis demonstrations on my home site &lt;a href="http://richardlees.com.au/Sites/Demonstrations"&gt;http://RichardLees.com.au/Sites/Demonstrations&lt;/a&gt; &lt;/div&gt;&lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;&lt;div&gt;So you and I can view how many people are reading this blog, when and where they are reading from. See &lt;a href="http://richardlees.com.au/Sites/Demonstrations/Shared%20Documents/Web%20Dashboard%20-%20close%20to%20real%20time%20analytics%20of%20this%20internet%20site/Web%20Performance.aspx"&gt;Blog Activity&lt;/a&gt; or navigate there from the "Web Performance" page of the "Web Analytics" dashboard on my demonstrations site. Ignore all the graphs, except the chart at the bottom left of the page. This chart is showing the number of visitors by Geography for the last 60 days. It's only showing about 10 days today, since I added the tag in on December 11. You don't have to stop there, since the graph is presented by Microsoft PerformancePoint, it is interactive and you are able to drill down further, change chart type etc. for example, here is a sorted list of client IP addresses.&lt;a href="http://richardlees.com.au/Sites/Demonstrations/Shared%20Documents/Web%20Dashboard%20-%20close%20to%20real%20time%20analytics%20of%20this%20internet%20site/Web%20Performance.aspx"&gt;&lt;img id="BLOGGER_PHOTO_ID_5282033683290099234" style="FLOAT: right; MARGIN: 0px 0px 10px 10px; WIDTH: 400px; CURSOR: hand; HEIGHT: 252px" alt="" src="http://1.bp.blogspot.com/_SHRRgijB18E/SU2NpkgnjiI/AAAAAAAAAH0/tOHczaenXvM/s400/BlogActivity2.JPG" border="0" /&gt;&lt;/a&gt; You can ask for exactly the same, and you will be able to find your own IP address in the list from when you read this blog. Because, whenever you go to this page, you make a real-time query to the web logs cube.&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;&lt;div&gt;I would like to point out that I have added this functionality simply to provide an interesting live demonstration. I don't have any particular reason to know anyone's IP address. Although having said that, this might be a useful reminder to many people of the tracks they are leaving behind on the Internet.&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;&lt;div&gt;Richard &lt;/div&gt;&lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;&lt;div&gt;&lt;a href="http://richardlees.com.au/"&gt;http://richardlees.com.au/&lt;/a&gt; &lt;a href="http://2.bp.blogspot.com/_SHRRgijB18E/SU2RgvjEGpI/AAAAAAAAAIU/ld7F7vmHUy8/s1600-h/BlogActivityIP.JPG"&gt;&lt;img id="BLOGGER_PHOTO_ID_5282037929680837266" style="FLOAT: right; MARGIN: 0px 0px 10px 10px; WIDTH: 320px; CURSOR: hand; HEIGHT: 278px" alt="" src="http://2.bp.blogspot.com/_SHRRgijB18E/SU2RgvjEGpI/AAAAAAAAAIU/ld7F7vmHUy8/s320/BlogActivityIP.JPG" border="0" /&gt;&lt;/a&gt;&lt;a href="http://richardlees.com.au/sites/Demonstrations"&gt;&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3689647052791307812-3632534054568088396?l=richardlees.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://richardlees.blogspot.com/feeds/3632534054568088396/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3689647052791307812&amp;postID=3632534054568088396' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3689647052791307812/posts/default/3632534054568088396'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3689647052791307812/posts/default/3632534054568088396'/><link rel='alternate' type='text/html' href='http://richardlees.blogspot.com/2008/12/whos-reading-this-blog.html' title='Who&apos;s Reading This Blog?'/><author><name>Richard Lees</name><uri>http://www.blogger.com/profile/05671716466559973540</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='31' height='32' src='http://2.bp.blogspot.com/-AdeWScnQel8/Thj9ePsjpQI/AAAAAAAAAew/XnIn3xt4a0s/s220/RichardUni.png'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://4.bp.blogspot.com/_SHRRgijB18E/SU2OFSD7JAI/AAAAAAAAAIM/njjRohpS5Gw/s72-c/BlogActivity.JPG' height='72' width='72'/><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3689647052791307812.post-7421581696303713833</id><published>2008-12-11T20:39:00.006+11:00</published><updated>2008-12-12T07:06:49.765+11:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Book Review: Data Mining with SQL Server 2008'/><title type='text'>Book Review: Data Mining with SQL Server 2008</title><content type='html'>&lt;a href="http://1.bp.blogspot.com/_SHRRgijB18E/SUDjzTA1fzI/AAAAAAAAAHk/zLqGH577TU8/s1600-h/dm2008.JPG"&gt;&lt;img id="BLOGGER_PHOTO_ID_5278469233694834482" style="FLOAT: right; MARGIN: 0px 0px 10px 10px; WIDTH: 321px; CURSOR: hand; HEIGHT: 400px" alt="" src="http://1.bp.blogspot.com/_SHRRgijB18E/SUDjzTA1fzI/AAAAAAAAAHk/zLqGH577TU8/s400/dm2008.JPG" border="0" /&gt;&lt;/a&gt;I remember when data mining algorithms were first included in SQL Server 2000. It was very exciting and I immediately went to the municipal library and talked them into giving me an extract of their database for data mining demonstrations. That version of SQL Server had no data mining documentation. But that didn't worry me and I helped a few organisations exploit data mining. However there weren't many people that went to the trouble of learning SQL Server's data mining technolgy. SQL Server 2005 had some documentation but data mining still wasn't used to its potential. SQL Server 2008 has been greatly beefed up in its data mining capability, primarily in the ancillary tools that professional data miners demand, such as lift charts and validity testing. Now SQL Server 2008 has, arguably, the best set of data mining tools in the market. So if you have been putting off data mining, don't delay any longer.&lt;br /&gt;&lt;br /&gt;This book by Jamie MacLennan, ZhaoHui Tang and Bogdan Crivat (all developers of the product in Redmond) is a very practical guide and quite readable by someone new to data mining. It starts with the data mining tools included in Excel 2007 and goes on to detail all the algorithms, the syntax of the DMX language and embedding data mining in your applications. Experienced data mining will also find the book useful. I found many useful tips. For example, I only just learnt that you can nest MDX in your DMX queries. It is more common to embed SQL in data mining queries.&lt;br /&gt;&lt;br /&gt;To learn about data mining, I really believe that you need some real data to explore. The book has a download site where readers can download databases and demonstrations to experiment with.&lt;br /&gt;&lt;br /&gt;If you are using Analysis Services and haven't yet started data mining, I suggest that you get a copy of this book and teach yourself data mining. Data mining is going to be really big.&lt;br /&gt;&lt;br /&gt;I did have trouble with Wiley download url, but here is a direct link for the exercise data. &lt;a href="http://www.wiley.com/WileyCDA/WileyTitle/productCd-0470277742,descCd-DOWNLOAD.html"&gt;http://www.wiley.com/WileyCDA/WileyTitle/productCd-0470277742,descCd-DOWNLOAD.html&lt;/a&gt;&lt;br /&gt;&lt;p&gt;By the way, for anyone who is interested, there are a couple of live data mining demonstrations on my site. Where data mining is embedded in Reporting Services (also covered in the book). There is a book suggestion tool using the library data I mentioned above, and the another that predicts response times for the last 50 http requests on my web server. This demonstration has no practical value, but hopefully you can draw the analogy to a similar model that predicts customer profitability etc. &lt;a href="http://richardlees.com.au/sites/Demonstrations/Pages/LibrariesSuggestions.aspx"&gt;http://RichardLees.com.au/sites/Demonstrations/Pages/LibrariesSuggestions.aspx&lt;/a&gt;&lt;/p&gt;Richard&lt;br /&gt;&lt;br /&gt;&lt;a href="http://richardlees.com.au/"&gt;http://richardlees.com.au/&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3689647052791307812-7421581696303713833?l=richardlees.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://richardlees.blogspot.com/feeds/7421581696303713833/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3689647052791307812&amp;postID=7421581696303713833' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3689647052791307812/posts/default/7421581696303713833'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3689647052791307812/posts/default/7421581696303713833'/><link rel='alternate' type='text/html' href='http://richardlees.blogspot.com/2008/12/book-review-data-mining-with-sql-server.html' title='Book Review: Data Mining with SQL Server 2008'/><author><name>Richard Lees</name><uri>http://www.blogger.com/profile/05671716466559973540</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='31' height='32' src='http://2.bp.blogspot.com/-AdeWScnQel8/Thj9ePsjpQI/AAAAAAAAAew/XnIn3xt4a0s/s220/RichardUni.png'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://1.bp.blogspot.com/_SHRRgijB18E/SUDjzTA1fzI/AAAAAAAAAHk/zLqGH577TU8/s72-c/dm2008.JPG' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3689647052791307812.post-7659313138161138886</id><published>2008-12-10T12:42:00.002+11:00</published><updated>2008-12-10T12:56:08.864+11:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='heterogeneous MDX SQL queries OpenRowset'/><title type='text'>Mixing SQL and MDX queries</title><content type='html'>A very useful tool in any BI developers toolbox is the OpenRowset query that can combine the data from an SQL and AS database.  If you do Data Mining, the chances are that you use it all the time.  The basic format is quite simple.&lt;br /&gt;&lt;span style="color:#3333ff;"&gt;SELECT * FROM OPENROWSET(provider, connection, query)&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;There are many occasions when this heterogeneous query is useful, for example&lt;br /&gt;&lt;ol&gt;&lt;li&gt;ETL processes that need to query source systems depending on what data is in the OLAP database, or where the OLAP database is up to.&lt;/li&gt;&lt;li&gt;SQL transaction system might need to query summary data, and while the programmers can query the OLAP database with a MSOLAP provider, they might find it easier to use their existing SQL provider with an OpenRowset.&lt;/li&gt;&lt;li&gt;You need a combination of aggregated and atomic data for a query&lt;/li&gt;&lt;li&gt;You are data mining and want to predict values based on data within an SQL database&lt;/li&gt;&lt;/ol&gt;Here is a trivial example of an SQL query joining with an OLAP query.  The OLAP database is the Perfmon cube (on &lt;a href="http://richardlees.com.au/Sites/Demonstrations"&gt;http://RichardLees.com.au/Sites/Demonstrations&lt;/a&gt; Perfmon) which is joined with sql data from &lt;span style="color:#009900;"&gt;sys.databases&lt;/span&gt;.&lt;br /&gt;&lt;br /&gt;&lt;span style="color:#3333ff;"&gt;SELECT&lt;br /&gt;  db.*,&lt;br /&gt;  olap."[Measures].[Value]" as "Average Transactions/Second"&lt;br /&gt;FROM OpenRowset(&lt;span style="color:#330033;"&gt;'MSOLAP'&lt;/span&gt;,'&lt;span style="color:#000000;"&gt;DATASOURCE=Localhost; Initial Catalog=Perfmon;&lt;/span&gt;',&lt;br /&gt;    &lt;span style="color:#cc0000;"&gt;'SELECT&lt;br /&gt;      Measures.[Value] ON COLUMNS,&lt;br /&gt;      [Counter Details].[Database Name].[Database Name] ON ROWS&lt;br /&gt;    FROM Perfmon&lt;br /&gt;    Where ([Counter Details].[Counter].[Object].&amp;amp;[SQLServer:Databases].&amp;amp;[Transactions/sec])&lt;/span&gt; ') as olap&lt;br /&gt;INNER JOIN &lt;span style="color:#009900;"&gt;sys.databases&lt;/span&gt; db on convert(varchar(255),olap."[Counter Details].[Database Name].[Database Name].[MEMBER_CAPTION]")=db.name&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Richard,&lt;br /&gt;&lt;a href="http://richardlees.com.au/"&gt;http://RichardLees.com.au&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3689647052791307812-7659313138161138886?l=richardlees.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://richardlees.blogspot.com/feeds/7659313138161138886/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3689647052791307812&amp;postID=7659313138161138886' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3689647052791307812/posts/default/7659313138161138886'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3689647052791307812/posts/default/7659313138161138886'/><link rel='alternate' type='text/html' href='http://richardlees.blogspot.com/2008/12/mixing-sql-and-mdx-queries.html' title='Mixing SQL and MDX queries'/><author><name>Richard Lees</name><uri>http://www.blogger.com/profile/05671716466559973540</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='31' height='32' src='http://2.bp.blogspot.com/-AdeWScnQel8/Thj9ePsjpQI/AAAAAAAAAew/XnIn3xt4a0s/s220/RichardUni.png'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3689647052791307812.post-486838409285818262</id><published>2008-12-06T19:25:00.005+11:00</published><updated>2008-12-06T19:38:58.353+11:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Imagination more important than knowledge business intelligence business strategy'/><title type='text'>Imagination is More Important Than Knowledge</title><content type='html'>&lt;a href="http://3.bp.blogspot.com/_SHRRgijB18E/STo3gq-YBSI/AAAAAAAAAHM/1fT_Wz5kH1g/s1600-h/Anna+and+Richard+Unicycling.JPG"&gt;&lt;img id="BLOGGER_PHOTO_ID_5276590947849209122" style="FLOAT: right; MARGIN: 0px 0px 10px 10px; WIDTH: 400px; CURSOR: hand; HEIGHT: 393px" alt="" src="http://3.bp.blogspot.com/_SHRRgijB18E/STo3gq-YBSI/AAAAAAAAAHM/1fT_Wz5kH1g/s400/Anna+and+Richard+Unicycling.JPG" border="0" /&gt;&lt;/a&gt;This immortal quote from Albert Einstein applies perfectly in developing Business Intelligence solutions. Not to belittle knowledge and skills, they are obviously important, but just like its cousin, Business Strategy, BI requires a great deal of imagination and creative thinking to get great solutions.&lt;br /&gt;&lt;br /&gt;In my view, you don’t create great BI solutions by implementing text book solutions. Every organization has its unique set of data and its unique Business Strategy so BI solutions need to be uniquely developed.&lt;br /&gt;&lt;br /&gt;The question is, how do you improve your creative thinking? In my view there are a few “other side of brain” activities that help. I find that juggling, unicycling and surfing help me. When I first joined Microsoft (1995) there was a culture of juggling, and every few hours we would take a 10 minutes juggling break. At the time, I simply saw juggling as a more healthy excuse than smoking for social interaction. But I do think the peripheral brain activity somehow facilitated creative thinking. When you juggle, you cannot focus on the balls, rather, you rely on peripheral vision. This leaves you to focus on something else, and somehow you come up with insightful options with your peripheral brain busy. &lt;a href="http://2.bp.blogspot.com/_SHRRgijB18E/STo3gktv8XI/AAAAAAAAAHU/-QGTMxRkXhI/s1600-h/Richard+Unicycling+EE.JPG"&gt;&lt;img id="BLOGGER_PHOTO_ID_5276590946168861042" style="FLOAT: right; MARGIN: 0px 0px 10px 10px; WIDTH: 186px; CURSOR: hand; HEIGHT: 400px" alt="" src="http://2.bp.blogspot.com/_SHRRgijB18E/STo3gktv8XI/AAAAAAAAAHU/-QGTMxRkXhI/s400/Richard+Unicycling+EE.JPG" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Recently, our family has taken up unicycling. Yes, anyone can learn to unicycle. I am getting that same feeling of exercising the other half of my brain when unicycling. So, if you want to be more creative, I encourage you to spend time doing these “other side brain” activities. What’s your creative activity; drama, photography, creating music?&lt;br /&gt;&lt;br /&gt;Richard Lees&lt;br /&gt;&lt;a href="http://richardlees.com.au/"&gt;http://richardlees.com.au/&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3689647052791307812-486838409285818262?l=richardlees.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://richardlees.blogspot.com/feeds/486838409285818262/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3689647052791307812&amp;postID=486838409285818262' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3689647052791307812/posts/default/486838409285818262'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3689647052791307812/posts/default/486838409285818262'/><link rel='alternate' type='text/html' href='http://richardlees.blogspot.com/2008/12/imagination-is-more-important-than.html' title='Imagination is More Important Than Knowledge'/><author><name>Richard Lees</name><uri>http://www.blogger.com/profile/05671716466559973540</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='31' height='32' src='http://2.bp.blogspot.com/-AdeWScnQel8/Thj9ePsjpQI/AAAAAAAAAew/XnIn3xt4a0s/s220/RichardUni.png'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://3.bp.blogspot.com/_SHRRgijB18E/STo3gq-YBSI/AAAAAAAAAHM/1fT_Wz5kH1g/s72-c/Anna+and+Richard+Unicycling.JPG' height='72' width='72'/><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3689647052791307812.post-6472364824281036938</id><published>2008-12-05T09:25:00.004+11:00</published><updated>2008-12-05T09:59:43.290+11:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='MSSQLServerOLAPService File system error.  Invalid Data Directory'/><title type='text'>Analysis Services Won't Start - Invalid Data Directory.</title><content type='html'>&lt;a href="http://4.bp.blogspot.com/_SHRRgijB18E/SThgyjWnR8I/AAAAAAAAAHE/-eR6Ds10-ZE/s1600-h/InvalidDataDrive.jpg"&gt;&lt;img id="BLOGGER_PHOTO_ID_5276073385064351682" style="FLOAT: right; MARGIN: 0px 0px 10px 10px; WIDTH: 361px; CURSOR: hand; HEIGHT: 400px" alt="" src="http://4.bp.blogspot.com/_SHRRgijB18E/SThgyjWnR8I/AAAAAAAAAHE/-eR6Ds10-ZE/s400/InvalidDataDrive.jpg" border="0" /&gt;&lt;/a&gt;&lt;span style="color:#ff0000;"&gt;MSSQLServerOLAPService File system error: Error occurred during the creation of directory.&lt;/span&gt;&lt;br /&gt;This can be really annoying. You are trying to start Analysis Services service, but it refuses to start and you either don't know why, or you now know realise that you have updated the data directory and it doesn't exist. But how do you change it now that Analysis Services won't start?&lt;br /&gt;This can happen when you change the data directory for Analysis Services to an invalid directory, or the drive is not available. An invalid drive includes a root directory drive. You really need to create a directory for Analysis Services and ensure that the service has read/write access rights to that directory.&lt;br /&gt;The solution is relatively simple. Simply find the msmdsrv.ini file (it is typically in a directory like &lt;span style="color:#006600;"&gt;C:\Program Files\Microsoft SQL Server\MSSQL.2\OLAP\Config.&lt;/span&gt; Edit the file using notepad and change the DataDir value to what it should be. Analysis Services should start up now.&lt;br /&gt;Having said that, I do encourage people to set the data and temp directories for Analysis Services to appropriate directories. On a large OLAP server, you really want to have these directories on fast disk subsystems. The temp directory, in particular, can incur large volumes of read/write activity when processing a large cube.&lt;br /&gt;&lt;br /&gt;Richard&lt;br /&gt;&lt;a href="http://richardlees.com.au/"&gt;http://richardlees.com.au/&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3689647052791307812-6472364824281036938?l=richardlees.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://richardlees.blogspot.com/feeds/6472364824281036938/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3689647052791307812&amp;postID=6472364824281036938' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3689647052791307812/posts/default/6472364824281036938'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3689647052791307812/posts/default/6472364824281036938'/><link rel='alternate' type='text/html' href='http://richardlees.blogspot.com/2008/12/analysis-services-wont-start-invalid.html' title='Analysis Services Won&apos;t Start - Invalid Data Directory.'/><author><name>Richard Lees</name><uri>http://www.blogger.com/profile/05671716466559973540</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='31' height='32' src='http://2.bp.blogspot.com/-AdeWScnQel8/Thj9ePsjpQI/AAAAAAAAAew/XnIn3xt4a0s/s220/RichardUni.png'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://4.bp.blogspot.com/_SHRRgijB18E/SThgyjWnR8I/AAAAAAAAAHE/-eR6Ds10-ZE/s72-c/InvalidDataDrive.jpg' height='72' width='72'/><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3689647052791307812.post-109506068781684615</id><published>2008-12-03T09:12:00.001+11:00</published><updated>2008-12-03T09:37:31.291+11:00</updated><title type='text'>Customer Retention and other complex analysis made easy with reports over OLAP</title><content type='html'>&lt;a href="http://4.bp.blogspot.com/_SHRRgijB18E/STW4BUHPRoI/AAAAAAAAAGs/mse7JhHed2M/s1600-h/retention.jpg"&gt;&lt;img id="BLOGGER_PHOTO_ID_5275324871252854402" style="FLOAT: right; MARGIN: 0px 0px 10px 10px; WIDTH: 400px; CURSOR: hand; HEIGHT: 202px" alt="" src="http://4.bp.blogspot.com/_SHRRgijB18E/STW4BUHPRoI/AAAAAAAAAGs/mse7JhHed2M/s400/retention.jpg" border="0" /&gt;&lt;/a&gt;Analysis Services and Reporting Services make it easy to create complex analysis embedded in easy to consume reports.  You can embed complex queries inside a report that everyone can use.  For example, see my "Customer Retention" live demonstration on&lt;br /&gt;&lt;a href="http://richardlees.com.au/sites/Demonstrations/Pages/VisitorRetention.aspx"&gt;http://richardlees.com.au/sites/Demonstrations/Pages/VisitorRetention.aspx&lt;/a&gt; It shows the retention figures for my web site over the last rolling 12 months. The algorithm for calculating retention in this demonstration is as follows&lt;br /&gt;Number of Customers who were active in the preceding 2 months who have also been active in the current month&lt;br /&gt;/Number of Customers who were active in the preceding 2 months &lt;a href="http://1.bp.blogspot.com/_SHRRgijB18E/STW4BzMWeLI/AAAAAAAAAG0/sR4r5K0VOB0/s1600-h/retention2.jpg"&gt;&lt;img id="BLOGGER_PHOTO_ID_5275324879595796658" style="FLOAT: right; MARGIN: 0px 0px 10px 10px; WIDTH: 400px; CURSOR: hand; HEIGHT: 206px" alt="" src="http://1.bp.blogspot.com/_SHRRgijB18E/STW4BzMWeLI/AAAAAAAAAG0/sR4r5K0VOB0/s400/retention2.jpg" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;This demonstration is live and dynamic, so as you click on the report a query executes to determine the answer at the current point in time. The data is continually changing. Notice that you can also click on a particular retention figure to drill through to the customers that were active in the preceding two months but have not been active in the current month.  This is just one example of a "complex" query made easy within a Reporting Services report. Imagine how difficult it would be to get the same answer by simply using the slice and dice functionality of a cube browser (albeit a powerful one) such as Excel. Combining dynamic MDX with SQL Server Reports is a great way of providing analysis to end users in a very easy to consume format.&lt;br /&gt;&lt;br /&gt;Richard&lt;br /&gt;&lt;br /&gt;&lt;a href="http://richardlees.com.au/"&gt;http://RichardLees.com.au&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3689647052791307812-109506068781684615?l=richardlees.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://richardlees.blogspot.com/feeds/109506068781684615/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3689647052791307812&amp;postID=109506068781684615' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3689647052791307812/posts/default/109506068781684615'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3689647052791307812/posts/default/109506068781684615'/><link rel='alternate' type='text/html' href='http://richardlees.blogspot.com/2008/12/blog-post.html' title='Customer Retention and other complex analysis made easy with reports over OLAP'/><author><name>Richard Lees</name><uri>http://www.blogger.com/profile/05671716466559973540</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='31' height='32' src='http://2.bp.blogspot.com/-AdeWScnQel8/Thj9ePsjpQI/AAAAAAAAAew/XnIn3xt4a0s/s220/RichardUni.png'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://4.bp.blogspot.com/_SHRRgijB18E/STW4BUHPRoI/AAAAAAAAAGs/mse7JhHed2M/s72-c/retention.jpg' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3689647052791307812.post-2165357351414055945</id><published>2008-11-29T06:12:00.004+11:00</published><updated>2008-11-29T06:42:56.819+11:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Conditional Formatting Analysis Services Excel ThinSlicer'/><title type='text'>Elaborate your cubes with conditional formatting</title><content type='html'>&lt;a href="http://3.bp.blogspot.com/_SHRRgijB18E/STBJY9-p0uI/AAAAAAAAAGU/O07rTud2ANU/s1600-h/ThinSlicerConditionalFormatting.JPG"&gt;&lt;img id="BLOGGER_PHOTO_ID_5273795856953889506" style="FLOAT: right; MARGIN: 0px 0px 10px 10px; WIDTH: 400px; CURSOR: hand; HEIGHT: 258px" alt="" src="http://3.bp.blogspot.com/_SHRRgijB18E/STBJY9-p0uI/AAAAAAAAAGU/O07rTud2ANU/s400/ThinSlicerConditionalFormatting.JPG" border="0" /&gt;&lt;/a&gt; &lt;div&gt;&lt;div&gt;&lt;div&gt;SQL Server Analysis Services cubes have supported conditional formatting of measures since 2000. Now that Excel supports conditional formatting, they really are a "must have" to make some cubes more readable. For example, my PerfMon cube has the same “Value” measure, which could be “Bytes of Virtual Storage” or “Seconds per Read IO”. You really want to format the number depending on what it is. For example, a 9 digit number probably wants to be formatted as #,#, while a number of about 0.001 probably wants 6 significant digits.&lt;br /&gt;This is all very easy with Analysis Services, simply put a condition on the FORMAT clause. For example&lt;br /&gt;&lt;span style="color:#6600cc;"&gt;FORMAT_STRING =&lt;br /&gt;iif(ROOT(DateTime),[Measures].[Value])&gt;100,"#,#",&lt;br /&gt;iif(ROOT(DateTime),[Measures].[Value])&gt;0.1,"#,#0.000","#,#0.000000"))&lt;br /&gt;&lt;/span&gt;I like to add colour formatting, so that the number formatting stands out. For example, on the same measure, I might add&lt;br /&gt;&lt;span style="color:#6600cc;"&gt;FORE_COLOR=&lt;br /&gt;iif(ROOT(DateTime),[Measures].[Value])&gt;100, RGB(0,0,0),&lt;br /&gt;iif(ROOT(DateTime),[Measures].[Value])&gt;0.1, RGB(0,155,0), rgb(180,180,180)))&lt;br /&gt;&lt;/span&gt;I use the ROOT() function so that the formatting is not specific to any particular cell, which would be annoying to the eye. Rather all cells for any one counter will be consistent in their format. For an demonstration of conditional formatting at work (on a live cube using the cube browser I wrote, ThinSlicer) go to &lt;a href="http://richardlees.com.au:8080/iislog/tsPerfmon.aspx"&gt;http://richardlees.com.au:8080/iislog/tsPerfmon.aspx&lt;/a&gt; Just clicking “Display” you will see the numbers in a light grey colour, since these numbers don’t mean anything. Once you drill down one of the counter hierarchies the true number and colour formatting come into effect. It works exactly the same in Excel. Unfortunately, Reporting Services does not support these conditional formats yet, although, one could argue that the need for RS to honour conditional formatting is less. Performance Point dashboards support the conditional number formatting. For a demonstration you can see the same live cube through a PPS dashboard on &lt;a href="http://richardlees.com.au/sites/Demonstrations/Shared%20Documents/Windows%20Performance%20Monitor/Interactive%20Chart-Grid.aspx"&gt;http://RichardLees.com.au/sites/Demonstrations/Shared%20Documents/Windows%20Performance%20Monitor/Interactive%20Chart-Grid.aspx&lt;/a&gt; &lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;a href="http://4.bp.blogspot.com/_SHRRgijB18E/STBI85WKMZI/AAAAAAAAAGM/3TgS9n0oAqM/s1600-h/PPSConditionalFormatting.JPG"&gt;&lt;img id="BLOGGER_PHOTO_ID_5273795374673965458" style="FLOAT: right; MARGIN: 0px 0px 10px 10px; WIDTH: 400px; CURSOR: hand; HEIGHT: 305px" alt="" src="http://4.bp.blogspot.com/_SHRRgijB18E/STBI85WKMZI/AAAAAAAAAGM/3TgS9n0oAqM/s400/PPSConditionalFormatting.JPG" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;div&gt;Not all cubes have the need for conditional formatting, since the numbers in any one measure tend to be homogenous. However, cubes that contain company accounts (GL cubes etc) really need this sort of conditional formatting, as the number you are looking at could "Turnover", "Average Tax/Transaction" etc. These really need conditional formatting.&lt;/div&gt;&lt;br /&gt;&lt;div&gt;Richard&lt;/div&gt;&lt;br /&gt;&lt;div&gt;&lt;a href="http://richardlees.com.au/"&gt;http://RichardLees.com.au&lt;/a&gt; &lt;/div&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3689647052791307812-2165357351414055945?l=richardlees.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://richardlees.blogspot.com/feeds/2165357351414055945/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3689647052791307812&amp;postID=2165357351414055945' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3689647052791307812/posts/default/2165357351414055945'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3689647052791307812/posts/default/2165357351414055945'/><link rel='alternate' type='text/html' href='http://richardlees.blogspot.com/2008/11/elaborate-your-cubes-with-conditional.html' title='Elaborate your cubes with conditional formatting'/><author><name>Richard Lees</name><uri>http://www.blogger.com/profile/05671716466559973540</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='31' height='32' src='http://2.bp.blogspot.com/-AdeWScnQel8/Thj9ePsjpQI/AAAAAAAAAew/XnIn3xt4a0s/s220/RichardUni.png'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://3.bp.blogspot.com/_SHRRgijB18E/STBJY9-p0uI/AAAAAAAAAGU/O07rTud2ANU/s72-c/ThinSlicerConditionalFormatting.JPG' height='72' width='72'/><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3689647052791307812.post-5037024702632834259</id><published>2008-11-24T10:30:00.006+11:00</published><updated>2008-11-24T11:40:14.523+11:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Saving table changes SQL Server 2008'/><title type='text'>Can No Longer Change Tables in SQL Server 2008 Management Studio</title><content type='html'>&lt;a href="http://1.bp.blogspot.com/_SHRRgijB18E/SSn23itXYcI/AAAAAAAAAF0/4rjn8gvLpos/s1600-h/SQLError.JPG"&gt;&lt;img id="BLOGGER_PHOTO_ID_5272016272884195778" style="FLOAT: right; MARGIN: 0px 0px 10px 10px; WIDTH: 400px; CURSOR: hand; HEIGHT: 327px" alt="" src="http://1.bp.blogspot.com/_SHRRgijB18E/SSn23itXYcI/AAAAAAAAAF0/4rjn8gvLpos/s400/SQLError.JPG" border="0" /&gt;&lt;/a&gt; Have you made a design change to a table in SQL Server 2008 and got an error message saying that &lt;span style="color:#ff0000;"&gt;"Saving changes is not permitted? The changes you have made require the following tables to be dropped and re-created. You have either made changes to a table that can't be re-created or enabled the option Prevent saving changes that require the table to be re-created."&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;The default option has been changed in SQL 2008, so that changes requiring dropping and recreating tables are not permitted dynamically. I guess this has been changed because &lt;a href="http://4.bp.blogspot.com/_SHRRgijB18E/SSn2_T-fmEI/AAAAAAAAAF8/kyPIx7JAuUM/s1600-h/AllowTableChanges.JPG"&gt;&lt;img id="BLOGGER_PHOTO_ID_5272016406368458818" style="FLOAT: right; MARGIN: 0px 0px 10px 10px; WIDTH: 400px; CURSOR: hand; HEIGHT: 237px" alt="" src="http://4.bp.blogspot.com/_SHRRgijB18E/SSn2_T-fmEI/AAAAAAAAAF8/kyPIx7JAuUM/s400/AllowTableChanges.JPG" border="0" /&gt;&lt;/a&gt;too many DBAs have made changes to large tables and got themselves into trouble.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;If you know that your table is small, and you want to be able to save changes, you can simply change the default action in the &lt;span style="color:#3333ff;"&gt;Tools / Options / Designers&lt;/span&gt; settings and uncheck the &lt;span style="color:#ff0000;"&gt;"Prevent saving changes that require table re-creation"&lt;/span&gt; option.&lt;br /&gt;&lt;br /&gt;Warning, don't use this to make changes to very large tables. For very large tables, I suggest you manually create the new table and use SSIS to load the new table from the old table. Then drop the old table and rename the new table. Also remember to recompile your views on this table.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;a href="http://richardlees.com.au/"&gt;http://RichardLees.com.au&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3689647052791307812-5037024702632834259?l=richardlees.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://richardlees.blogspot.com/feeds/5037024702632834259/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3689647052791307812&amp;postID=5037024702632834259' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3689647052791307812/posts/default/5037024702632834259'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3689647052791307812/posts/default/5037024702632834259'/><link rel='alternate' type='text/html' href='http://richardlees.blogspot.com/2008/11/can-no-longer-change-tables-in-sql.html' title='Can No Longer Change Tables in SQL Server 2008 Management Studio'/><author><name>Richard Lees</name><uri>http://www.blogger.com/profile/05671716466559973540</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='31' height='32' src='http://2.bp.blogspot.com/-AdeWScnQel8/Thj9ePsjpQI/AAAAAAAAAew/XnIn3xt4a0s/s220/RichardUni.png'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://1.bp.blogspot.com/_SHRRgijB18E/SSn23itXYcI/AAAAAAAAAF0/4rjn8gvLpos/s72-c/SQLError.JPG' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3689647052791307812.post-5232524796764403137</id><published>2008-11-17T19:51:00.004+11:00</published><updated>2008-11-17T20:06:34.441+11:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Analysis Services Cancel long running MDX query'/><title type='text'>Cancelling runaway MDX queries on SQL Server 2008</title><content type='html'>It is now very easy to cancel Analysis Services MDX queries in SQL Server 2008. Here is a command you can execute from an MDX window to see what's running.&lt;br /&gt;By the way, if you have a run away query, you don’t need to restart AS. You can determine what queries are running and consuming resources with&lt;br /&gt;&lt;br /&gt;&lt;span style="color:#3333ff;"&gt;&lt;strong&gt;&lt;span style="font-family:arial;"&gt;select * from $system.discover_commands&lt;/span&gt;&lt;br /&gt;&lt;/strong&gt;&lt;/span&gt;&lt;br /&gt;You can then cancel any of the executing queries with the following xmla command. Just put in the appropriate SPID. &lt;img id="BLOGGER_PHOTO_ID_5269549991670045778" style="FLOAT: right; MARGIN: 0px 0px 10px 10px; WIDTH: 400px; CURSOR: hand; HEIGHT: 55px" alt="" src="http://3.bp.blogspot.com/_SHRRgijB18E/SSEzzFOn3FI/AAAAAAAAAFs/6Tjabs6yD7Q/s400/CancelMDX.JPG" border="0" /&gt;&lt;br /&gt;&lt;br /&gt;It's that easy. You could even use Reporting Services to display the output of the Discover_Commands and link it to the Cancel command. That way, you could cancel queries through a web browser. Just make sure that you have integrated security so that a non administrator couldn't use the report to cancel others commands.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3689647052791307812-5232524796764403137?l=richardlees.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://richardlees.blogspot.com/feeds/5232524796764403137/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3689647052791307812&amp;postID=5232524796764403137' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3689647052791307812/posts/default/
