<?xml version="1.0" encoding="UTF-8" ?>
<?xml-stylesheet type="text/xsl" href="http://blogs.officezealot.com/utility/FeedStylesheets/rss.xsl" media="screen"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/" xmlns:wfw="http://wellformedweb.org/CommentAPI/"><channel><title>Steve Hansen : VBA, Excel</title><link>http://blogs.officezealot.com/hansen/archive/tags/VBA/Excel/default.aspx</link><description>Tags: VBA, Excel</description><dc:language>en</dc:language><generator>CommunityServer 2.1 SP2 (Build: 61129.2)</generator><item><title>VBA to VSTO: A resolution for the new year</title><link>http://blogs.officezealot.com/hansen/archive/2007/01/08/20040.aspx</link><pubDate>Tue, 09 Jan 2007 00:38:00 GMT</pubDate><guid isPermaLink="false">a446e06f-2cc4-48dd-a534-c024bd1e2687:20040</guid><dc:creator>hansen</dc:creator><slash:comments>1</slash:comments><comments>http://blogs.officezealot.com/hansen/comments/20040.aspx</comments><wfw:commentRss>http://blogs.officezealot.com/hansen/commentrss.aspx?PostID=20040</wfw:commentRss><description>&lt;P&gt;Recently a friend of mine asked me for help with a data management issue. Same story, different day - he had numerous Excel files (about 8-10 he tells me) with&amp;nbsp;sales prospects&amp;nbsp;that he needed to combine and clean. This friend is former professional water skier and national champion with a warm and sunny Florida residence on a private lake. As an avid tournament waterskier and resident of the freezing tundra that is Minnesota, I thought I'd offer a trade - I'll clean up your data (actually I volunteered my wife) and you provide some coaching in the warm waters of Florida. "Done!" he replies.&lt;/P&gt;
&lt;P&gt;&amp;nbsp;Then I get them. The files. 25 of them. 1000's of records (~30,000). No common structure. Gulp. I quickly realized that either my wife is going to be busy for a very, very long time or I better intervene and build her some tools. So, did I use all of the goodness of VSTO for this or VBA? &lt;/P&gt;
&lt;P&gt;VBA in a heartbeat.&lt;/P&gt;
&lt;P&gt;As much as I like VSTO and developing in Visual Studio rather than the VBA IDE, I find myself still using VBA for a lot of "ad-hoc" development tasks when time is of the essence. The reason:&amp;nbsp;&amp;nbsp;a decade old&amp;nbsp;library of tried and true VBA code that I can draw on to accomplish tasks rapidly. Find the code (or template)&amp;nbsp;I need, add the bits specific to the task at hand, connect all the dots, done. My VSTO library pales by comparison.&lt;/P&gt;
&lt;P&gt;One of the things I'd like to accomplish this year is migrate&amp;nbsp;all&amp;nbsp;my "old faithful" VBA code to&amp;nbsp;.NET. As I progress on this journey, I hope to fulfill another resolution - share more of what I know and learn through regular blogging and additions to &lt;A class="" title="Office Zealot" href="http://www.officezealot.com/" target=_blank&gt;Office Zealot&lt;/A&gt;. &lt;/P&gt;
&lt;P&gt;Back to my immediate problem for a minute. I decided the first major task was to somehow get all of this into a database. The problem was that I didn't even know what I had - none of the files shared a layout much less common fields. One of the tools in my VBA toolkit is an Excel&amp;nbsp;batch processing utility that I use whenever I need to run some VBA process against a set of workbooks. I've used and refined this for at least seven years. It's really not a lot of code, but it is robust, works exactly like I want it, and is simple&amp;nbsp;to use. Just add the specific processing code and you're done.&amp;nbsp;Anyway, I added grabbed this utility and added workbook processing code to collect the field information (23 of the files at least had the fields in one of the first three rows) and put it all on a common worksheet so I could try and make sense of it all. &lt;/P&gt;
&lt;P&gt;Based on the collection of fields, I created two tables. One table contained all of the common fields. A second table was for the "miscellaneous" data that he wanted to keep "just in case". This table was merely a collection of name/value records. &lt;/P&gt;
&lt;P&gt;Next, I used the batch processing utility to insert a data mapping row in each workbook/worksheet that needed to be processed.&amp;nbsp;Finally, I used&amp;nbsp;the batch processing utility again to cycle through all the workbooks and load the data.&lt;/P&gt;
&lt;P&gt;I bring all of this up to point out the usefulness of this batch processing&amp;nbsp;utility. I use this all the time to do things in minutes that would otherwise take hours or days. My journey begins with this utility...&lt;/P&gt;&lt;img src="http://blogs.officezealot.com/aggbug.aspx?PostID=20040" width="1" height="1"&gt;</description><category domain="http://blogs.officezealot.com/hansen/archive/tags/Excel/default.aspx">Excel</category><category domain="http://blogs.officezealot.com/hansen/archive/tags/VBA/default.aspx">VBA</category><category domain="http://blogs.officezealot.com/hansen/archive/tags/VSTO/default.aspx">VSTO</category></item><item><title>Retrieve the login name of the current user in Excel using VBA (with Vista/Excel 2007 screencast)</title><link>http://blogs.officezealot.com/hansen/archive/2007/01/03/20012.aspx</link><pubDate>Wed, 03 Jan 2007 16:43:00 GMT</pubDate><guid isPermaLink="false">a446e06f-2cc4-48dd-a534-c024bd1e2687:20012</guid><dc:creator>hansen</dc:creator><slash:comments>0</slash:comments><comments>http://blogs.officezealot.com/hansen/comments/20012.aspx</comments><wfw:commentRss>http://blogs.officezealot.com/hansen/commentrss.aspx?PostID=20012</wfw:commentRss><description>&lt;P&gt;How do I retrieve the login name of the current user in Excel or VBA? This has to be one of the most common questions asked by people building Office-based applications. Though &lt;A class="" title=Charles href="http://blogs.officezealot.com/charles/archive/2004/12/10/3574.aspx" target=_blank&gt;Charles&lt;/A&gt; covered it pretty throughly a couple of years ago, I thought I'd give it some more coverage. Seems as though many people still go the API route when the VBA Environ function is so simple and handy.&lt;/P&gt;
&lt;P&gt;The short answer is to write a simple VBA function that calls Environ:&lt;/P&gt;
&lt;P&gt;Function UserName() As String&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; UserName = Environ("USERNAME")&lt;BR&gt;End Function&lt;/P&gt;
&lt;P&gt;Just for fun, I created a &lt;A class="" title=screencast href="http://www.officezealot.com/screencasts/xluser/" target=_blank&gt;screencast&lt;/A&gt; that shows how to do this. If you're never written a VBA macro in your life, check it out as it'll show you step by step what you need to do. If you know VBA like the back of your hand but haven't checked out Windows Vista or Excel 2007, you might want to check it out to see what they look like.&lt;/P&gt;&lt;img src="http://blogs.officezealot.com/aggbug.aspx?PostID=20012" width="1" height="1"&gt;</description><category domain="http://blogs.officezealot.com/hansen/archive/tags/Excel/default.aspx">Excel</category><category domain="http://blogs.officezealot.com/hansen/archive/tags/VBA/default.aspx">VBA</category><category domain="http://blogs.officezealot.com/hansen/archive/tags/Screencast/default.aspx">Screencast</category></item><item><title>Truly bad book or simple misunderstanding?</title><link>http://blogs.officezealot.com/hansen/archive/2006/01/04/8934.aspx</link><pubDate>Wed, 04 Jan 2006 17:01:00 GMT</pubDate><guid isPermaLink="false">a446e06f-2cc4-48dd-a534-c024bd1e2687:8934</guid><dc:creator>hansen</dc:creator><slash:comments>0</slash:comments><comments>http://blogs.officezealot.com/hansen/comments/8934.aspx</comments><wfw:commentRss>http://blogs.officezealot.com/hansen/commentrss.aspx?PostID=8934</wfw:commentRss><description>&lt;P&gt;There are a lot of authors (some with books in the works) that maintain blogs that grace the pages of OfficeZealot.com including &lt;A href="http://spaces.msn.com/members/cwebbbi/PersonalSpace.aspx"&gt;Chris Webb&lt;/A&gt; (book in the works), &lt;A href="http://blogs.officezealot.com/mauro/"&gt;Mauro Cardarelli&lt;/A&gt; (book in the works), &lt;A href="http://www.edbott.com/weblog/"&gt;Ed Bott&lt;/A&gt;, &lt;A href="http://blogs.msdn.com/eric_carter/"&gt;Eric Carter&lt;/A&gt;, &lt;A href="http://blogs.officezealot.com/whitechapel/"&gt;Andrew Whitechapel&lt;/A&gt; and yes, yours truly. There is even an editor &amp;#8211; &lt;A href="http://blogs.officezealot.com/spiller/"&gt;Melanie Spiller&lt;/A&gt; who can attest to the following: &lt;STRONG&gt;a lot &lt;/STRONG&gt;of effort and eyeballs go into any book. Nonetheless, errors are bound to make it into the printed version.&amp;nbsp;Also, most authors I know are perfectly happy to answer any questions readers have about a book, either responding to errors, helping clarify sections of the book, or answering other questions.&lt;/P&gt;
&lt;P&gt;That said, on behalf of everyone who has spent ungodly hours writing a book (for not a whole lot of money I might add), I&amp;#8217;d ask one favor of those folks who choose to leave comments on Amazon and Barnes and Noble. Please be factual and give the author a chance to clarify something if you find an error or problem. I&amp;#8217;ll be the first to admit that when I&amp;#8217;m learning something new, chances are that when I&amp;#8217;m having problems it is due to incomplete knowledge, misunderstanding, or misinterpretation.&lt;/P&gt;
&lt;P&gt;Recently, a reader of my Excel VBA book got me a bit flustered when he left borderline defamatory comments on &lt;A href="http://www.amazon.com/gp/product/0782142818/qid=1136387792/sr=8-1/ref=sr_8_xs_ap_i1_xgl14/002-6760269-1940865?n=507846&amp;amp;s=books&amp;amp;v=glance"&gt;Amazon&lt;/A&gt; and Barnes and Noble. To make things worse, he also contacted me and asked me his question. It&amp;#8217;s perhaps this fact that got me so flustered considering I promptly (within an hour) and politely (I thought anyway) answered his question. I thought I did the right thing, but perhaps the reader just has an ax to grind.&amp;nbsp;His email to me said:&lt;/P&gt;
&lt;BLOCKQUOTE dir=ltr style="MARGIN-RIGHT:0px;"&gt;
&lt;P&gt;I just got your book: &amp;#8220;Mastering Excel 2003 Programming with VBA&amp;#8221;, and I think it's great. I tried to implement the program you have listed on page 147 (Listing 7.9: Alphabetizing Worksheets in a Workbook). I keyed in the macro word for word, and could not get it to work. Excel could not &amp;#8220;see&amp;#8221; the macro if (wb As Workbook) was in the parens instead of (). So, I downloaded the example files off of dakotatechgroup.com, and tried to run listing 7.9. Although it was in the module if I engaged Alt-F11, Excel could not &amp;#8220;see&amp;#8221; the macro and thus not run it.&lt;/P&gt;
&lt;P&gt;Can you tell me what I need to do to run the listing 7.9 macro?&lt;/P&gt;
&lt;P&gt;Thank you!&lt;/P&gt;&lt;/BLOCKQUOTE&gt;
&lt;P&gt;The listing in question simply alphabetizes worksheets in a workbook using a simple bubble sort.&lt;/P&gt;
&lt;P&gt;&lt;FONT face="Courier New"&gt;' performs a simple bubble sort to&lt;BR&gt;' sort the worksheets in the workbook&lt;BR&gt;Sub AlphabetizeWorksheets(wb As Workbook)&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; Dim bSorted As Boolean&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; Dim nSheetsSorted As Integer&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; Dim nSheets As Integer&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; Dim n As Integer&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; nSheets = wb.Worksheets.Count&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; nSheetsSorted = 0&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; Do While (nSheetsSorted &amp;lt; nSheets) And Not bSorted&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; bSorted = True&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; nSheetsSorted = nSheetsSorted + 1&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; For n = 1 To nSheets - nSheetsSorted&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; If StrComp(wb.Worksheets(n).Name, _&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; wb.Worksheets(n + 1).Name, _&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; vbTextCompare) &amp;gt; 0 Then&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; ' out of order - swap the sheets&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; wb.Worksheets(n + 1).Move _&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; before:=wb.Worksheets(n)&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; bSorted = False&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; End If&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; Next&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; Loop&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;BR&gt;End Sub&lt;/FONT&gt;&lt;/P&gt;
&lt;P&gt;I took the time to respond to him that since this procedure requires a parameter, it can not be executed directly from the Macro dialog box, you need to call it from another procedure such as:&lt;/P&gt;
&lt;P&gt;&lt;FONT face="Courier New"&gt;Sub Test()&lt;BR&gt;&amp;nbsp;AlphabetizeWorksheets ThisWorkbook&lt;BR&gt;End Sub&lt;/FONT&gt;&lt;/P&gt;
&lt;P&gt;Given his email, I can only assume that his simple misunderstanding of how parameterized procedures are used and the fact that they do not show up in the Macro dialog box caused him to conclude that none of the procedures in the book worked because they wouldn&amp;#8217;t show up in the Macro dialog box. He must have skipped over the section of the book that talked about using parameters with procedures and other basic VBA coverage.&lt;/P&gt;
&lt;P&gt;On a side note, I would recommend that as a rule of thumb, most of the procedures in your VBA project should NOT be visible in the&amp;nbsp;Macro dialog box. The only procedures that should be visible in the Macro dialog box are procedures that you want the user to directly execute. Even then, I typically hide these procedures, preferring instead to attach them to a user interface element. In order to prevent non-parameterized sub routines from showing up in the Macro dialog box you can declare the sub routine as Private such as: &lt;/P&gt;
&lt;P&gt;&lt;FONT face="Courier New"&gt;Private Sub Test()&lt;BR&gt;&amp;nbsp;AlphabetizeWorksheets ThisWorkbook&lt;BR&gt;End Sub&lt;/FONT&gt;&amp;nbsp; &lt;/P&gt;
&lt;P&gt;Alternatively, you can use the Option Private Module statement at the top of the module. Note that even if you use Option Private Module, you can still hook up user interface elements to the sub routines in the module that are not marked as Private and do not contain parameters.&lt;/P&gt;
&lt;P&gt;Oh well. I guess there is something to be learned about this. While Amazon&amp;#8217;s &amp;#8220;Real-Name&amp;#8221; feature is a step in the right direction, you still have to take these things with a grain of salt.&lt;/P&gt;
&lt;P&gt;Anyway, here is an open invitation to Mr. Reynolds &amp;#8211; I&amp;#8217;m sorry my book wasn&amp;#8217;t helpful to you. Send it back to me and I will personally refund your money including the cost of you shipping it to me:&lt;/P&gt;
&lt;P&gt;13335 Palomino Drive Suite 204&lt;BR&gt;Apple Valley, MN 55124&lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;img src="http://blogs.officezealot.com/aggbug.aspx?PostID=8934" width="1" height="1"&gt;</description><category domain="http://blogs.officezealot.com/hansen/archive/tags/Excel/default.aspx">Excel</category><category domain="http://blogs.officezealot.com/hansen/archive/tags/VBA/default.aspx">VBA</category></item><item><title>Part II: Analysis Services Reporting in Excel</title><link>http://blogs.officezealot.com/hansen/archive/2005/11/03/8470.aspx</link><pubDate>Thu, 03 Nov 2005 16:20:00 GMT</pubDate><guid isPermaLink="false">a446e06f-2cc4-48dd-a534-c024bd1e2687:8470</guid><dc:creator>hansen</dc:creator><slash:comments>4</slash:comments><comments>http://blogs.officezealot.com/hansen/comments/8470.aspx</comments><wfw:commentRss>http://blogs.officezealot.com/hansen/commentrss.aspx?PostID=8470</wfw:commentRss><description>&lt;P&gt;In &lt;A href="http://blogs.officezealot.com/hansen/archive/2005/10/27/8406.aspx"&gt;Part I&lt;/A&gt;, I discussed how to set the worksheet up, now it&amp;#8217;s time to look at some code. Building a report consists of three logical operations: determining the correct MDX that will yield the desired results, fetching the data, and displaying the data. In this post I&amp;#8217;ll focus on the first task &amp;#8211; determining the correct MDX. Before I jump into that task, I&amp;#8217;d like to show the main procedure that glues these three tasks together along with some module level information.&lt;/P&gt;
&lt;P&gt;LISTING 1:&lt;BR&gt;Option Explicit&lt;/P&gt;
&lt;P&gt;'/ Module variable&lt;BR&gt;Dim msMDX As String&lt;/P&gt;
&lt;P&gt;'/ Connection String - modify data source property appropriately&lt;BR&gt;Private Const CONN = "Data Source=;Initial Catalog=foodmart 2000;" &amp; _&lt;BR&gt;                     "Provider=msolap;Execution Location=3;" &amp; _&lt;BR&gt;                     "Default Isolation Mode=1;"&lt;/P&gt;
&lt;P&gt;Public Enum OPERATION&lt;BR&gt;    REFRESH_REPORT&lt;BR&gt;    DEFAULT_REPORT&lt;BR&gt;    PREVIOUS_REPORT&lt;BR&gt;    REBUILD_REPORT_WS&lt;BR&gt;    REBUILD_REPORT_CODE&lt;BR&gt;End Enum&lt;/P&gt;
&lt;P&gt;Public Sub DefaultReport()&lt;BR&gt;    GetReport DEFAULT_REPORT&lt;BR&gt;End Sub&lt;/P&gt;
&lt;P&gt;Public Sub PreviousReport()&lt;BR&gt;    GetReport PREVIOUS_REPORT&lt;BR&gt;End Sub&lt;/P&gt;
&lt;P&gt;Private Sub GetReport(opType As OPERATION)&lt;BR&gt;    Dim cst As ADOMD.Cellset&lt;BR&gt;    Dim sMDX As String&lt;BR&gt;    Dim ws As Worksheet&lt;BR&gt;    Dim nMonth As Integer&lt;BR&gt;    &lt;BR&gt;    On Error GoTo ErrHandler&lt;BR&gt;    &lt;BR&gt;    Set ws = ThisWorkbook.Worksheets("Report")&lt;BR&gt;    &lt;BR&gt;    '/ Build the MDX statement&lt;BR&gt;    Select Case opType&lt;BR&gt;        Case REFRESH_REPORT&lt;BR&gt;            sMDX = ws.Range("CurrentMDX")&lt;BR&gt;        Case DEFAULT_REPORT&lt;BR&gt;            sMDX = BuildMDX(ws, True)&lt;BR&gt;        Case REBUILD_REPORT_WS&lt;BR&gt;            sMDX = BuildMDX(ws, False)&lt;BR&gt;        Case PREVIOUS_REPORT&lt;BR&gt;            sMDX = ws.Range("PriorMDX")&lt;BR&gt;        Case REBUILD_REPORT_CODE&lt;BR&gt;            sMDX = msMDX&lt;BR&gt;    End Select&lt;BR&gt;    SaveMDX ws, sMDX&lt;BR&gt;    &lt;BR&gt;    '/ Get the data&lt;BR&gt;    Set cst = GetCellset(sMDX)&lt;BR&gt;    If cst.State = 0 Then Exit Sub&lt;BR&gt;    &lt;BR&gt;    '/ Display the results&lt;BR&gt;    ws.Range(ws.Range("TopLeft").Offset(1, 0), _&lt;BR&gt;             ws.Range("R65536")).ClearContents&lt;BR&gt;    DisplayCellset cst, ws, ws.Range("TopLeft").Offset(1, 0)&lt;BR&gt;    &lt;BR&gt;ExitPoint:&lt;BR&gt;    Set ws = Nothing&lt;BR&gt;    Set cst = Nothing&lt;BR&gt;    Exit Sub&lt;BR&gt;ErrHandler:&lt;BR&gt;    If Err.Number &lt;&gt; 18 Then&lt;BR&gt;        Beep&lt;BR&gt;        MsgBox "The Following Error has occurred:" &amp; vbCrLf &amp; _&lt;BR&gt;           Err.Description, vbCritical, " Error!"&lt;BR&gt;    End If&lt;BR&gt;    Resume ExitPoint&lt;BR&gt;End Sub&lt;/P&gt;
&lt;P&gt;In Part I I put two drawing objects on the worksheet called &amp;#8220;Default Report&amp;#8221; and &amp;#8220;Previous Report&amp;#8221;. These &amp;#8220;buttons&amp;#8221; are hooked up to the DefaultReport and PreviousReport procedures respectively. As you can see, all they do is call the GetReport procedure with the correct Operation. In Part I, I only showed off three types of Operations: DEFAULT_REPORT, PREVIOUS_REPORT, and REBUILD_REPORT_CODE (I didn&amp;#8217;t mention this explicitly, but this type of operation occurs when you drill down on an item). You could add a simple procedure to call GetReport specifying the REFRESH_REPORT operation. All this does is build the report using the MDX stored in the CurrentMDX named range. For this example, I didn&amp;#8217;t implement the REBUILD_REPORT_WS operation so specifying this operation results in the same functionality as the DEFAULT_REPORT operation. The idea of REBUILD_REPORT_WS is to figure out what the MDX should be based on the items on the worksheet. This allows the user to build some &amp;#8220;free-form&amp;#8221; reports. For example, they can just enter the stores they want to see sales numbers for (without drilling to the correct spot in the hierarchy) and the procedure figures out the correct MDX.&lt;/P&gt;
&lt;P&gt;For the purposes of this example, I simplified the BuildMDX function and its related functions. Here is an elementary implementation.&lt;/P&gt;
&lt;P&gt;LISTING 2: Building the MDX&lt;BR&gt;Private Function BuildMDX(ws As Worksheet, bDefault As Boolean) As String&lt;/P&gt;
&lt;P&gt;    BuildMDX = "SELECT " &amp; _&lt;BR&gt;           ColumnMDX() &amp; _&lt;BR&gt;           RowMDX(ws, bDefault) &amp; _&lt;BR&gt;           "FROM [Sales] " &amp; _&lt;BR&gt;           SlicerMDX&lt;BR&gt;           &lt;BR&gt;End Function&lt;/P&gt;
&lt;P&gt;Private Function ColumnMDX() As String&lt;BR&gt;    ColumnMDX = "{[Time].[1997].[Q1], [Time].[1997].[Q2]} on columns, "&lt;BR&gt;End Function&lt;/P&gt;
&lt;P&gt;Private Function RowMDX(ws As Worksheet, bDefault As Boolean) As String&lt;BR&gt;    Dim sMDX As String&lt;BR&gt;    Dim nRow As Integer&lt;BR&gt;    Dim sCurrent As String&lt;BR&gt;    Dim sPrevious As String&lt;BR&gt;    Dim sDefaultMDX As String&lt;BR&gt;    &lt;BR&gt;    sDefaultMDX = _&lt;BR&gt;        "NON EMPTY { (Descendants([Store].[USA], 1, SELF_AND_BEFORE)) }"&lt;BR&gt;       &lt;BR&gt;    If bDefault Then&lt;BR&gt;        sMDX = sDefaultMDX &amp; " ON ROWS " &amp; vbCrLf&lt;BR&gt;    Else&lt;BR&gt;        sMDX = "NON EMPTY { "&lt;BR&gt;        nRow = ws.Range("TopLeft").Row + 1&lt;BR&gt;        sPrevious = ""&lt;BR&gt;        If IsEmpty(ws.Cells(nRow, 1)) Then&lt;BR&gt;            sMDX = "NON EMPTY " &amp; sDefaultMDX &amp; " ON ROWS " &amp; vbCrLf&lt;BR&gt;        Else&lt;BR&gt;            Do Until IsEmpty(ws.Cells(nRow, 1))&lt;BR&gt;                sCurrent = Trim(ws.Cells(nRow, 1).Value)&lt;BR&gt;                If sCurrent &lt;&gt; sPrevious Then&lt;BR&gt;                    sMDX = sMDX &amp; _&lt;BR&gt;                           "([Store].[" &amp; Trim(ws.Cells(nRow, 1).Value) &amp; "]),"&lt;BR&gt;                End If&lt;BR&gt;                sPrevious = sCurrent&lt;BR&gt;                nRow = nRow + 1&lt;BR&gt;            Loop&lt;BR&gt;            sMDX = Left(sMDX, Len(sMDX) - 1)&lt;BR&gt;            sMDX = sMDX &amp; "} ON ROWS" &amp; vbCrLf&lt;BR&gt;        End If&lt;BR&gt;    End If&lt;BR&gt;    RowMDX = sMDX&lt;BR&gt;End Function&lt;/P&gt;
&lt;P&gt;Private Function SlicerMDX() As String&lt;BR&gt;    SlicerMDX = "WHERE([Measures].[Store Sales])"&lt;BR&gt;End Function &lt;/P&gt;
&lt;P&gt;I could have simplified this some more by just folding everything into one or two procedures. For example, ColumnMDX and SlicerMDX simply return a static string &amp;#8211; why break those out into separate functions? The reason I left them as separate functions is because normally you&amp;#8217;ll want to build these dynamically as well. It would be a trivial exercise to modify these functions so that the user could select which measure they want to see (SlicerMDX) or to offer the ability to navigate across the time periods (ColumnMDX). &lt;/P&gt;
&lt;P&gt;The RowMDX function is the only procedure that does any real work here. Most of the procedure handles the case where the MDX needs to be determined based on the items on the worksheet. As you can see, this implementation is fairly primitive &amp;#8211; all it does is loop until the first empty cell is found, adding the value of each cell as a member name item in the row specification.&lt;/P&gt;
&lt;P&gt;This is probably a good time to address a potential security concern. Is this code subject to a SQL Injection attack? The simple answer is yes. It would be possible to break this code by crafting a special snippet of MDX and placing it in one of the worksheet cells. On the other hand, MDX and Analysis Services is a bit different than SQL and a database. Most cubes I come across are write-only &amp;#8211; you cannot modify the values or add new values to the cube. If you do have a cube with writeback enabled, the potential risk is much higher. Also, note that you cannot use the CommandText and Parameters.AddWithValue method that is used with SQL statements in ADO. The ICommandWithParameters interface is not supported by the MSOLAP provider. That said, I&amp;#8217;d strongly recommend that you check any values you read off the spreadsheet before using them in your MDX. Maybe I&amp;#8217;ll post on this subject in an upcoming post.&lt;/P&gt;
&lt;P&gt;Though handling the drill up/down could probably be considered part of determining the correct MDX, I save this piece for a future post. Next up in this series: fetching the data and displaying the results.&lt;BR&gt;&lt;/P&gt;&lt;img src="http://blogs.officezealot.com/aggbug.aspx?PostID=8470" width="1" height="1"&gt;</description><category domain="http://blogs.officezealot.com/hansen/archive/tags/Analysis+Services/default.aspx">Analysis Services</category><category domain="http://blogs.officezealot.com/hansen/archive/tags/Excel/default.aspx">Excel</category><category domain="http://blogs.officezealot.com/hansen/archive/tags/VBA/default.aspx">VBA</category></item><item><title>Part I: Analysis Services Reporting in Excel</title><link>http://blogs.officezealot.com/hansen/archive/2005/10/27/8406.aspx</link><pubDate>Thu, 27 Oct 2005 20:10:00 GMT</pubDate><guid isPermaLink="false">a446e06f-2cc4-48dd-a534-c024bd1e2687:8406</guid><dc:creator>hansen</dc:creator><slash:comments>1</slash:comments><comments>http://blogs.officezealot.com/hansen/comments/8406.aspx</comments><wfw:commentRss>http://blogs.officezealot.com/hansen/commentrss.aspx?PostID=8406</wfw:commentRss><description>&lt;P class=MsoNormal style="MARGIN:0in 0in 0pt;"&gt;The main objectives in this post are to show some screenshots that demonstrate what you can expect when you complete the exercise and to do all of the preliminary setup on the workbook. The goal of this example is to show how you can create reports in Excel that consume Analysis Services data and provide basic drill up/drill down functionality. &lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN:0in 0in 0pt;"&gt;&amp;nbsp;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN:0in 0in 0pt;"&gt;&lt;STRONG&gt;Functionality Overview&lt;/STRONG&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN:0in 0in 0pt;"&gt;&lt;STRONG&gt;&lt;/STRONG&gt;&amp;nbsp;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN:0in 0in 0pt;"&gt;Figure 1 shows the empty worksheet I setup to demonstrate basic drilling functionality.&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN:0in 0in 0pt;"&gt;&amp;nbsp;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN:0in 0in 0pt;"&gt;&lt;STRONG&gt;Figure 1&lt;/STRONG&gt;: The empty worksheet used by this exercise.&lt;/P&gt;&lt;IMG id=Drill1 style="BORDER-RIGHT:black 2px solid;BORDER-TOP:black 2px solid;BORDER-LEFT:black 2px solid;WIDTH:539px;BORDER-BOTTOM:black 2px solid;HEIGHT:480px;" height=480 src="http://archives.officezealot.com/images/blogs_officezealot_com/hansen/202/r_drill1.jpg" width=539&gt; 
&lt;P class=MsoNormal style="MARGIN:0in 0in 0pt;"&gt;&amp;nbsp;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN:0in 0in 0pt;"&gt;&amp;nbsp;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN:0in 0in 0pt;"&gt;When you click “Default Report”, the report will execute a default Multidimensional Expression (MDX) statement against the Sales cube in the Food Mart 2000 database and return the results to the worksheet. MDX is to Analysis Services what SQL is to SQL Server.&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN:0in 0in 0pt;"&gt;&lt;o:p&gt;&amp;nbsp;&lt;/o:p&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN:0in 0in 0pt;"&gt;&lt;B style="mso-bidi-font-weight:normal;"&gt;Figure 2&lt;/B&gt;: The results of the default report.&lt;/P&gt;&lt;IMG id=Drill2 style="BORDER-RIGHT:black 2px solid;BORDER-TOP:black 2px solid;BORDER-LEFT:black 2px solid;WIDTH:539px;BORDER-BOTTOM:black 2px solid;HEIGHT:480px;" src="http://archives.officezealot.com/images/blogs_officezealot_com/hansen/202/r_drill2.jpg"&gt; 
&lt;P class=MsoNormal style="MARGIN:0in 0in 0pt;"&gt;&amp;nbsp;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN:0in 0in 0pt;"&gt;In Figure 2 you can see the results of the default report and the MDX used to retrieve data from Analysis Services. One thing you’ll notice here is that the different levels of the Store hierarchy are displayed in a single column. This has advantages over the PivotTable method where levels are displayed in separate columns. One immediate advantage is that the fact columns are always in the same column no matter how many levels are displayed. For example, Figures&amp;nbsp;3 and&amp;nbsp;4&amp;nbsp;demonstrate&amp;nbsp;the column shift that occurs in PivotTables when you drill down/up.&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN:0in 0in 0pt;"&gt;&amp;nbsp;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN:0in 0in 0pt;"&gt;&lt;STRONG&gt;Figure 3&lt;/STRONG&gt;:&amp;nbsp;A&amp;nbsp;PivotTable report showing the same data as Figure 2. Notice that Q1 and Q2 are in columns B &amp;amp; C respectively.&lt;/P&gt;&lt;IMG id=Drill3 style="BORDER-RIGHT:black 2px solid;BORDER-TOP:black 2px solid;BORDER-LEFT:black 2px solid;WIDTH:539px;BORDER-BOTTOM:black 2px solid;HEIGHT:480px;" src="http://archives.officezealot.com/images/blogs_officezealot_com/hansen/202/r_drill3.jpg"&gt; 
&lt;P class=MsoNormal style="MARGIN:0in 0in 0pt;"&gt;&amp;nbsp;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN:0in 0in 0pt;"&gt;&amp;nbsp;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN:0in 0in 0pt;"&gt;&lt;STRONG&gt;Figure 4&lt;/STRONG&gt;: Drilling down in a&amp;nbsp;PivotTable causes the&amp;nbsp;columns to shift over to &lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN:0in 0in 0pt;"&gt;accommodate the new level that appears.&amp;nbsp;Notice that Q1 and Q2 are in columns C &amp;amp; D.&lt;/P&gt;&lt;IMG id=Drill4 style="BORDER-RIGHT:black 2px solid;BORDER-TOP:black 2px solid;BORDER-LEFT:black 2px solid;WIDTH:581px;BORDER-BOTTOM:black 2px solid;HEIGHT:420px;" height=420 src="http://archives.officezealot.com/images/blogs_officezealot_com/hansen/202/r_drill4.jpg" width=581&gt; 
&lt;P class=MsoNormal style="MARGIN:0in 0in 0pt;"&gt;&amp;nbsp;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN:0in 0in 0pt;"&gt;&amp;nbsp;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN:0in 0in 0pt;"&gt;Back to the default report for this exercise, if you double-click on OR, the report drills down on the data underneath OR.&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN:0in 0in 0pt;"&gt;&lt;o:p&gt;&amp;nbsp;&lt;/o:p&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN:0in 0in 0pt;"&gt;&lt;STRONG&gt;Figure 5&lt;/STRONG&gt;: Drilling down on Oregon&lt;/P&gt;&lt;IMG id=Drill5 style="BORDER-RIGHT:black 2px solid;BORDER-TOP:black 2px solid;BORDER-LEFT:black 2px solid;WIDTH:539px;BORDER-BOTTOM:black 2px solid;HEIGHT:480px;" src="http://archives.officezealot.com/images/blogs_officezealot_com/hansen/202/r_drill5.jpg"&gt; 
&lt;P class=MsoNormal style="MARGIN:0in 0in 0pt;"&gt;&amp;nbsp;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN:0in 0in 0pt;"&gt;Notice that the MDX from the prior report was copied to the PriorMDX range. This makes it extremely easy to provide a level of undo (aka FlashBack for you Essbase diehards). If you double-click on OR again, the report will drill up which results in the same view as in Figure 2.&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN:0in 0in 0pt;"&gt;&lt;o:p&gt;&amp;nbsp;&lt;/o:p&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN:0in 0in 0pt;"&gt;&lt;STRONG&gt;Figure 6&lt;/STRONG&gt;: Simple drill up/down allows users to determine the amount of detail they want&lt;/P&gt;&lt;IMG id=Drill6 style="BORDER-RIGHT:black 2px solid;BORDER-TOP:black 2px solid;BORDER-LEFT:black 2px solid;WIDTH:507px;BORDER-BOTTOM:black 2px solid;HEIGHT:480px;" height=480 src="http://archives.officezealot.com/images/blogs_officezealot_com/hansen/202/r_drill6.jpg" width=507&gt; 
&lt;P class=MsoNormal style="MARGIN:0in 0in 0pt;"&gt;&amp;nbsp;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN:0in 0in 0pt;"&gt;&lt;B style="mso-bidi-font-weight:normal;"&gt;Setting up the Report Worksheet&lt;o:p&gt;&lt;/o:p&gt;&lt;/B&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN:0in 0in 0pt;"&gt;The first thing you can do is set the worksheet up as shown in &lt;B style="mso-bidi-font-weight:normal;"&gt;Figure 1&lt;/B&gt;. If you’re going to literally follow along, begin by naming the worksheet “Report”. &lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN:0in 0in 0pt;"&gt;&lt;o:p&gt;&amp;nbsp;&lt;/o:p&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN:0in 0in 0pt;"&gt;The code that I’ll be presenting requires three named ranges: CurrentMDX, PriorMDX, and TopLeft. You &lt;I style="mso-bidi-font-style:normal;"&gt;do not&lt;/I&gt; need to populate these cells with any data.&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN:0in 0in 0pt;"&gt;&lt;o:p&gt;&amp;nbsp;&lt;/o:p&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN:0in 0in 0pt 0.75in;TEXT-INDENT:-0.25in;mso-list:l0 level1 lfo1;tab-stops:list .75in;"&gt;&lt;SPAN style="mso-list:Ignore;"&gt;1)&lt;SPAN style="FONT:7pt 'Times New Roman';"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/SPAN&gt;&lt;/SPAN&gt;Select cell A1&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN:0in 0in 0pt 0.75in;TEXT-INDENT:-0.25in;mso-list:l0 level1 lfo1;tab-stops:list .75in;"&gt;&lt;SPAN style="mso-list:Ignore;"&gt;2)&lt;SPAN style="FONT:7pt 'Times New Roman';"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/SPAN&gt;&lt;/SPAN&gt;Enter ‘Report’!CurrentMDX in the name box and press Enter.&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN:0in 0in 0pt 0.75in;TEXT-INDENT:-0.25in;mso-list:l0 level1 lfo1;tab-stops:list .75in;"&gt;&lt;SPAN style="mso-list:Ignore;"&gt;3)&lt;SPAN style="FONT:7pt 'Times New Roman';"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/SPAN&gt;&lt;/SPAN&gt;Select cell A2&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN:0in 0in 0pt 0.75in;TEXT-INDENT:-0.25in;mso-list:l0 level1 lfo1;tab-stops:list .75in;"&gt;&lt;SPAN style="mso-list:Ignore;"&gt;4)&lt;SPAN style="FONT:7pt 'Times New Roman';"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/SPAN&gt;&lt;/SPAN&gt;Enter ‘Report’!PriorMDX in the name box and press Enter.&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN:0in 0in 0pt 0.75in;TEXT-INDENT:-0.25in;mso-list:l0 level1 lfo1;tab-stops:list .75in;"&gt;&lt;SPAN style="mso-list:Ignore;"&gt;5)&lt;SPAN style="FONT:7pt 'Times New Roman';"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/SPAN&gt;&lt;/SPAN&gt;Select cell A7&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN:0in 0in 0pt 0.75in;TEXT-INDENT:-0.25in;mso-list:l0 level1 lfo1;tab-stops:list .75in;"&gt;&lt;SPAN style="mso-list:Ignore;"&gt;6)&lt;SPAN style="FONT:7pt 'Times New Roman';"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/SPAN&gt;&lt;/SPAN&gt;Enter ‘Report’!TopLeft in the name box and press Enter.&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN:0in 0in 0pt;"&gt;&lt;o:p&gt;&amp;nbsp;&lt;/o:p&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN:0in 0in 0pt;"&gt;The code that I’ll present later on in the series will refer to these named ranges. The CurrentMDX and PriorMDX are just persistent storage places for MDX statements. I put them directly on the Report worksheet to keep things simple and to provide a live glimpse of the MDX that gets constructed by your code for learning (and occasional debugging &lt;SPAN style="FONT-FAMILY:Wingdings;mso-ascii-font-family:'Times New Roman';mso-hansi-font-family:'Times New Roman';mso-char-type:symbol;mso-symbol-font-family:Wingdings;"&gt;&lt;SPAN style="mso-char-type:symbol;mso-symbol-font-family:Wingdings;"&gt;J&lt;/SPAN&gt;&lt;/SPAN&gt;). Normally, I’d use a separate worksheet for the MDX statements and set this worksheet to xlSheetVeryHidden. The TopLeft name is just a “stake in the ground” so that the process that displays the results knows where to dump the data.&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN:0in 0in 0pt;"&gt;&lt;o:p&gt;&amp;nbsp;&lt;/o:p&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN:0in 0in 0pt;"&gt;For this example, the columns returned are always Q1 and Q2. The display process will not build the column headings, so you should go ahead and enter Q1 in cell B8 and enter Q2 in cell C8.&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN:0in 0in 0pt;"&gt;&lt;o:p&gt;&amp;nbsp;&lt;/o:p&gt;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN:0in 0in 0pt;"&gt;In the upper right corner of the sheet, I placed two simple drawing objects and added the text “Default Report” to one and “Previous Report” to the other. You’ll attach a macro to each of these in the future. Alternatively, you could use a command button.&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN:0in 0in 0pt;"&gt;&amp;nbsp;&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN:0in 0in 0pt;"&gt;That's it for setting up the reporting worksheet. Starting with the next post, I'll go over the code required to make this happen.&lt;/P&gt;
&lt;P class=MsoNormal style="MARGIN:0in 0in 0pt;"&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;img src="http://blogs.officezealot.com/aggbug.aspx?PostID=8406" width="1" height="1"&gt;</description><category domain="http://blogs.officezealot.com/hansen/archive/tags/Analysis+Services/default.aspx">Analysis Services</category><category domain="http://blogs.officezealot.com/hansen/archive/tags/Excel/default.aspx">Excel</category><category domain="http://blogs.officezealot.com/hansen/archive/tags/VBA/default.aspx">VBA</category></item></channel></rss>