﻿<?xml version="1.0" encoding="UTF-8"?>
<!--RSS generated by Windows SharePoint Services V3 RSS Generator on 5/17/2012 3:38:39 PM-->
<?xml-stylesheet type="text/xsl" href="/blogs/greggalloway/_layouts/RssXslt.aspx?List=83c71d76-a4d8-4197-9257-38d6b857634f" version="1.0"?>
<rss version="2.0">
  <channel>
    <title>Greg Galloway: Posts</title>
    <link>http://www.artisconsulting.com/blogs/greggalloway/Lists/Posts/AllPosts.aspx</link>
    <description>RSS feed for the Posts list.</description>
    <lastBuildDate>Thu, 17 May 2012 20:38:39 GMT</lastBuildDate>
    <generator>Windows SharePoint Services V3 RSS Generator</generator>
    <ttl>60</ttl>
    <image>
      <title>Greg Galloway: Posts</title>
      <url>/blogs/greggalloway/_layouts/images/homepage.gif</url>
      <link>http://www.artisconsulting.com/blogs/greggalloway/Lists/Posts/AllPosts.aspx</link>
    </image>
    <item>
      <title>ProcessAdd Examples</title>
      <link>http://www.artisconsulting.com/blogs/greggalloway/Lists/Posts/ViewPost.aspx?ID=4</link>
      <description><![CDATA[<div><b>Body:</b> <div class=ExternalClass79315F9ECA054CAD9198FE3C042BD1B9>
<p>The concept of incremental processing is well documented by T.K. Anand's fabulous <a href="http://msdn2.microsoft.com/en-us/library/ms345142.aspx">whitepaper</a> on processing (which you should probably digest before continuing), but there is a severe lack of full working example XMLA commands. The purpose of this post is to provide full working examples of ProcessAdd against Adventure Works. 
<p><b>1. ProcessAdd Partition</b> (download <font color="#0000ff"><a href="/blogs/greggalloway/Downloads/ProcessAdd%20Partition.xmla">ProcessAdd Partition.xmla</a></font>)<br>This example uses an out-of-line query binding to run a ProcessAdd on the 2004 partition of the Internet Sales measure group. Real-life use for such an XMLA command would require that you specify an out-of-line SQL query which retrieves rows that currently do not exist in the cube. No such rows were available in AdventureWorksDW, so I just specified a query which would add duplicate rows to the partition for illustration purposes. This XMLA command can be created in Management Studio by right-clicking on a partition, choosing Process, changing to Process Incremental, clicking Configure, setting a query, then clicking the Script button:<br><img border=0 alt=ProcessIncremental src="/blogs/greggalloway/Lists/Posts/Attachments/4/ProcessIncremental_dcda0b9d11864afe9182ac30038e90de.gif" width=588 height=240> <strong></strong></p>
<p><strong>2. ProcessAdd Dimension </strong>(download <a href="/blogs/greggalloway/Downloads/ProcessAdd%20Dimension.xmla">ProcessAdd Dimension.xmla</a> and <a href="/blogs/greggalloway/Downloads/ProcessAdd%20Dimension%202008.xmla">ProcessAdd Dimension 2008.xmla</a>)<br>This example uses an out-of-line data source view to run a ProcessAdd on the Sales Territory dimension. Because dimensions can be built off of multiple tables from the DSV, you cannot use an out-of-line query binding like what was used for example #1. Instead, you have to use an out-of-line DSV. Real-life use for such an XMLA command would entail specifying a named query in the out-of-line DSV which returned only rows which did not exist in the Analysis Services dimension. Rows which already exist will be ignored, so it is wasteful to allow your query to return them. No such rows were available in AdventureWorksDW, so I just specified a query which returned rows that already exist in the dimension for illustration purposes. With the example out-of-line DSV in “ProcessAdd Dimension.xmla” using the query it does, the Sales Territory dimension will not actually get any extra members. However, the example in “ProcessAdd Dimension 2008.xmla” uses a query that will add Texas to the Sales Territory dimension. </p>
<p><em><font color="#000000">Update: Note that the default error configuration changes in AS2008 so that duplicate key errors are not ignored during processing. Because of this design change, if you created your dimension in the development tools for AS2008, you have to override the ErrorConfiguration in the Batch in which you’re doing ProcessAdd on dimensions unless the rows you’re adding contain completely new values for every attribute in the dimension. In the “ProcessAdd Dimension 2008.xmla” example, we are adding a new region “Texas” to an existing country “United States”. Because “United States” already exists, we had to override the ErrorConfiguration and set KeyDuplicate to IgnoreError.</font></em> </p>
<p>There is no UI for creating this XMLA command in Management Studio. So I recommend the following steps if you wish to build your own ProcessAdd command for a dimension. 
<ol>
<li>Right-click on the dimension in Management Studio, choose Process, change to ProcessUpdate, and click the Script button. Change ProcessUpdate to ProcessAdd in the XMLA script. 
<li>In Business Intelligence Development Studio (BIDS), right-click in Solution Explorer on the Data Source object that currently provides the connection for processing this dimension, and choose View Code. Copy that XML. Paste it into the &lt;Process&gt; tag. Remove all the properties of the DataSource except for ID, Name, and ConnectionString. Note that even if you're not changing the connection string, it is still necessary to include an out-of-line data source with the out-of-line DSV. This is a known issue that will hopefully be fixed sometime after SP2. 
<li>In BIDS, open the data source view which your dimension is currently processed off of. Right-click on the table(s) your dimension currently uses, and choose &quot;Replace Table... With New Named Query...&quot; Then edit the query specifying the appropriate where clause. Do not save the DSV changes. Right-click on the .dsv file in Solution Explorer and choose View Code. Copy the entire XML and paste it into the &lt;Process&gt; tag. Remove the CreatedTimestamp and LastSchemaUpdate properties (to avoid a warning message). There are other large sections (such as the Annotations) you can remove if you like, but they will not hurt anything. </li></ol>
<p>Note that if you have more than one &lt;Process&gt; command, the out-of-line DSV must contain the tables required for all dimensions or partitions being processed which use that DSV normally. Here is such an example: <a href="/blogs/greggalloway/Downloads/Process%20Multiple%20Objects%20With%20Out-of-Line%20DSV.xmla">Process Multiple Objects With Out-of-Line DSV.xmla</a> 
<p>Normal processing involves Analysis Services querying a SQL database for the necessary data. Analysis Services also supports push mode processing where the data itself is provided in the XMLA command. Integration Services uses push mode processing whenever you have a Partition or Dimension Destination:<br><img border=0 alt=PartitionDestination src="/blogs/greggalloway/Lists/Posts/Attachments/4/PartitionDestination_4e3738ec20ea447d9061f4d7fd5ee36e.gif" width=328 height=59> <br><img border=0 alt=DimensionDestination src="/blogs/greggalloway/Lists/Posts/Attachments/4/DimensionDestination_ff3c9a58ebfd483d9ab03b58964e906a.gif" width=328 height=62> 
<p><b>3. Push Mode ProcessAdd for Partition</b> (download <a href="/blogs/greggalloway/Downloads/Push%20Mode%20ProcessAdd%20for%20Partition.xmla">Push Mode ProcessAdd for Partition.xmla</a>)<br>This example shows how to process a partition using push mode by specifying an out-of-line rowset. Generally push mode processing is done using Integration Services. If you need to do it without the aid of SSIS, I still recommend you build a simple package in SSIS, then run Profiler while you run the SSIS package. That XMLA query you will see in Profiler will not have the rowset schema or the actual data, but it will get you started. Note that in SSIS when you're using a Partition or Dimension destination, if a column is used twice (such as in the key and the name), you need to duplicate that column and put it in the pipeline twice. This is only a restriction of the UI in SSIS, not a restriction of push mode processing. 
<p><b>4. Push Mode ProcessAdd for Dimension</b> (download <a href="/blogs/greggalloway/Downloads/Push%20Mode%20ProcessAdd%20for%20Dimension.xmla">Push Mode ProcessAdd for Dimension.xmla</a>)<br>This example shows how to process a dimension using push mode by specifying an out-of-line rowset. See above for more information on push mode processing. 
<p><b>Miscellaneous Notes</b> 
<ul>
<li>If a dimension or partition is unprocessed, you can't do ProcessAdd on it. So make sure your processing procedures check this and do a ProcessFull the first time. 
<li>The <a href="http://www.codeplex.com/ASStoredProcedures/Wiki/View.aspx?title=MemoryUsage&amp;referringTitle=Home">MemoryUsage</a> class of ASSP uses push mode processing to load a cube incrementally without reading from a SQL database. 
<li>This post goes hand-in-hand with <a href="/blogs/greggalloway/Lists/Posts/ViewPost.aspx?ID=7">another post</a> which examines the performance of ProcessAdd. 
<li>Push mode processing is only allowed on partitions or dimensions which use one table from the DSV. So that eliminates snowflaked dimensions or measure groups with reference dimensions. </li></ul></div></div>
<div><b>Category:</b> Cube Processing;SSAS</div>
<div><b>Published:</b> 4/20/2007 6:25 PM</div>
<div><b>Archives:</b> April 2007</div>
<div><b>ArchiveSort:</b> 20070420</div>
<div><b>Attachments:</b> <a href="http://www.artisconsulting.com/blogs/greggalloway/Lists/Posts/Attachments/4/DimensionDestination_73ee66aa6d9c4e7a9a57b8f2c46dcbc6.gif">http://www.artisconsulting.com/blogs/greggalloway/Lists/Posts/Attachments/4/DimensionDestination_73ee66aa6d9c4e7a9a57b8f2c46dcbc6.gif</a><br><a href="http://www.artisconsulting.com/blogs/greggalloway/Lists/Posts/Attachments/4/DimensionDestination_ff3c9a58ebfd483d9ab03b58964e906a.gif">http://www.artisconsulting.com/blogs/greggalloway/Lists/Posts/Attachments/4/DimensionDestination_ff3c9a58ebfd483d9ab03b58964e906a.gif</a><br><a href="http://www.artisconsulting.com/blogs/greggalloway/Lists/Posts/Attachments/4/PartitionDestination_4c25a9ab783e4f9d973421bb5dc6bf89.gif">http://www.artisconsulting.com/blogs/greggalloway/Lists/Posts/Attachments/4/PartitionDestination_4c25a9ab783e4f9d973421bb5dc6bf89.gif</a><br><a href="http://www.artisconsulting.com/blogs/greggalloway/Lists/Posts/Attachments/4/PartitionDestination_4e3738ec20ea447d9061f4d7fd5ee36e.gif">http://www.artisconsulting.com/blogs/greggalloway/Lists/Posts/Attachments/4/PartitionDestination_4e3738ec20ea447d9061f4d7fd5ee36e.gif</a><br><a href="http://www.artisconsulting.com/blogs/greggalloway/Lists/Posts/Attachments/4/ProcessIncremental_3.gif">http://www.artisconsulting.com/blogs/greggalloway/Lists/Posts/Attachments/4/ProcessIncremental_3.gif</a><br><a href="http://www.artisconsulting.com/blogs/greggalloway/Lists/Posts/Attachments/4/ProcessIncremental_dcda0b9d11864afe9182ac30038e90de.gif">http://www.artisconsulting.com/blogs/greggalloway/Lists/Posts/Attachments/4/ProcessIncremental_dcda0b9d11864afe9182ac30038e90de.gif</a><br><a href=""></a></div>
]]></description>
      <author>Greg Galloway</author>
      <category>Cube Processing;SSAS</category>
      <pubDate>Fri, 03 Oct 2008 15:55:11 GMT</pubDate>
      <guid isPermaLink="true">http://www.artisconsulting.com/blogs/greggalloway/Lists/Posts/ViewPost.aspx?ID=4</guid>
    </item>
    <item>
      <title>Excel Writeback – Security and Leveraging the UPDATEABLE Cell Property</title>
      <link>http://www.artisconsulting.com/blogs/greggalloway/Lists/Posts/ViewPost.aspx?ID=20</link>
      <description><![CDATA[<div><b>Body:</b> <div class=ExternalClass378222FF87334127AAFD4D6A3FE2012A><p>Most of my blog posts chronicle some lesson learned in the heat of battle at a client site. This blog post is a bit different since the lesson learned came from an Artis internal project we started and completed last year. At Artis, our time entry system feeds into an Artis data warehouse and an Analysis Services cube for reporting. We also support writeback against the cube for project managers to enter forecasts for projects. That way, at the weekly staffing meeting, it’s pretty simple to see that Greg is forecasted to work 95 hours between four projects next week. At that point, preventative measures can be taken… like alerting local emergency rooms that I may be making a visit.</p> <p>Seriously though, Artis is a great place to work since I get to work with people like Drew Jones and Cindy Liang (who actually implemented this resource planning cube), and I’ve never once gotten close to working 95 hours. I’ve got an MDX query to prove it! But I digress…</p> <p><strong>Writeback Cube Security</strong><br>Since forecasting and resource planning is the goal, we use role-based security in the cube to prevent project managers from setting a forecast for a prior week (since it’s already too late to plan resources at that point). A typical scenario in industry where write security would be needed would be making sure each regional manager can only set the budget for his region. Setting up this security was a bit tricky, so I’ll describe it step-by-step.</p> <p>The first step is to create a ReadAll role. In this case, the ReadAll role grants members access to read the whole cube. I’ve definitely seen a number of clients make mistakes here and grant extra permissions that are not required. Nothing needs to be checked on the General tab (not even Read definition which grants them access to see the metadata in tools like SSMS). On the Cubes tab you need Read and optionally Drillthrough access. If you want a user to be able to read the whole cube from a tool like Excel, that’s all you need to do.</p> <p><img style="background-image:none;border-bottom:0px;border-left:0px;padding-left:0px;padding-right:0px;display:inline;border-top:0px;border-right:0px;padding-top:0px" title=image border=0 alt=image src="/blogs/greggalloway/Lists/Posts/Attachments/20/image_3_47D68324.png" width=710 height=141></p> <p>Next, build a separate Writeback role. On the Cubes tab, grant Read/Write, check Process, and (optionally) Drillthrough permissions. I’m frankly not sure why you need Process permissions to perform ROLAP writeback, but it appears you need Process permissions for ROLAP or MOLAP writeback:</p> <p><img style="background-image:none;border-bottom:0px;border-left:0px;padding-left:0px;padding-right:0px;display:inline;border-top:0px;border-right:0px;padding-top:0px" title=image border=0 alt=image src="/blogs/greggalloway/Lists/Posts/Attachments/20/image_308d37b7-44f8-41ce-80e9-94c5588bb0fe_47D68324.png" width=706 height=136></p> <p>In the Data Sources tab, make sure to grant Read permissions on the data source used for writeback. For example, when I created my writeback partition, I used the Greg Test data source to direct that writeback table to live in another database (such as a database with Full recovery model):</p> <p><img style="background-image:none;border-bottom:0px;border-left:0px;padding-left:0px;padding-right:0px;display:inline;border-top:0px;border-right:0px;padding-top:0px" title=image border=0 alt=image src="/blogs/greggalloway/Lists/Posts/Attachments/20/image_4ea50396-da9e-42d6-9ce8-9ce6079d7c82_47D68324.png" width=707 height=160></p> <p>Finally, if you want to only let them writeback to certain cells, control this on the Cell Data tab. Since I was prototyping against Adventure Works data, the expression below returns true when the current year is later than 2003. In your cube, the expression might look at a current week flag in the Date dimension or something like that.</p> <p><img style="background-image:none;border-bottom:0px;border-left:0px;padding-left:0px;padding-right:0px;display:inline;border-top:0px;border-right:0px;padding-top:0px" title=image border=0 alt=image src="/blogs/greggalloway/Lists/Posts/Attachments/20/image_a6ae1a16-8a05-4739-8f56-5d6926c84666_47D68324.png" width=712 height=486></p> <p> </p> <p><strong>Am I Seriously Recommending Cell Security?!?</strong><br>Wait! Cell security! Won’t that kill performance? Definitely Analysis Services best practices call to avoid cell security in favor of dimension data security because cell security is a performance killer. The reason I use it here is because I don’t know of a way to accomplish the writeback security without cell security. The question is whether this particular combination of cell security designed to be write security impacts read performance. I verified this security only impacts the performance of writes with a simple trick. In the read/write permissions box above, I put in the following expression to prove this:</p> <p>ASSP.FireTraceEventAndReturnValue(99) &lt; 100</p> <p>Let me explain what that’s doing. This <a href="http://asstoredprocedures.codeplex.com/wikipage?title=TraceEvent" target="_blank">FireTraceEventAndReturnValue</a> function was designed for the very purpose of being able to detect via Profiler when Analysis Services evaluates it. So it simply fires a “User Defined” event in Profiler and then returns the integer you pass into the function. That way, you can watch Profiler and verify cell security isn’t being evaluated during read-only queries. It only appears in Profiler when you query the UPDATEABLE property or when you perform a write. So just building a PivotTable in Excel isn’t slowed down by the above cell security since Excel currently doesn’t query the UPDATEABLE cell property. But I’m getting ahead of myself as I’ll describe the UPDATEABLE cell property below.</p> <p>Why is this the case that cell security didn’t impact read performance? Note that we used <strong>two roles</strong>. One role grants read permissions which let you read the whole cube. Another role grants write permissions but then says you can only write to recent years. If we had just created the Writeback role, users would not have been able to read 2003 data and cell security would impact read performance, too. By creating two roles, the read and write permissions are managed separately.</p> <p>As a side note, we have a support case open to fix a bug where attempting to write data to 2003 sometimes causes SSAS to crash. It appears this may happen when the user is in two roles, as described above. Hopefully that bug will be fixed soon. <font color="#ff0000">(Update:It looks like it will be fixed in SQL 2008 R2 SP1 CU6.)</font> Coincidentally, the VBA described below provides a workaround to the bug.</p> <p><br><strong>Testing Out Writeback in Excel 2010<br></strong>Now that we’ve setup our role-based security, let’s see what the user experience is in Excel 2010. We start a PivotTable as usual. But since I’m an administrator on my Analysis Services dev server, I need to tweak the connection string to pretend to be a lower privileged user. Go to the Data tab, click Connections, and click Properties for the connection. Then append either of the following snippets to the end of the connection string:</p> <p>;Roles=ReadAll,Writeback</p> <p>;EffectiveUserName=DOMAIN\UserWithReadWritePermissions</p> <p>The first pretends to be a member of the ReadAll and the Writeback roles. This works fine as long as the security is hardcoded (i.e. not dynamic security which uses the UserName() function in MDX expressions in the role). The EffectiveUserName option is another option which impersonates a specific user, and I’ve <a href="/blogs/greggalloway/Lists/Posts/Post.aspx?ID=18" target="_blank">blogged</a> about it before.</p> <p>Now I’ll build a simple PivotTable then <a href="http://blogs.office.com/b/microsoft-excel/archive/2009/10/12/excel-2010-pivottable-what-if-analysis.aspx" target="_blank">enable What-If analysis</a>. What happens when I edit a 2003 cell and publish my changes? I get this error message:</p> <p><img style="background-image:none;border-bottom:0px;border-left:0px;padding-left:0px;padding-right:0px;display:inline;border-top:0px;border-right:0px;padding-top:0px" title=image border=0 alt=image src="/blogs/greggalloway/Lists/Posts/Attachments/20/image_6434497c-baba-4ce8-9191-00eaa24cfcb9_47D68324.png" width=502 height=170></p> <p>The error message is at least helpful in describing what went wrong. But it’s not helpful in describing which cell was secured. Also, it’s not helpful that it waited until I was finished entering my data before giving me an error during the Publish. Some immediate feedback would have been nice.</p> <p>So how do you tell in advance which cells are secured and which cells are updateable. Fortunately, there’s a handy cell property called <a href="http://msdn.microsoft.com/en-us/library/ms145573.aspx" target="_blank">UPDATEABLE</a> which does just that. Unfortunately, Excel 2010 what-if analysis doesn’t leverage it, so I have <a href="https://connect.microsoft.com/SQLServer/feedback/details/714410/excel-2010-writeback-should-use-the-updateable-cell-property" target="_blank">proposed</a> this feature. For now we can use a little VBA to roll our own.</p> <p><br><strong>Using VBA to Query the UPDATEABLE Cell Property<br></strong>The following VBA code catches the Worksheet’s <a href="http://msdn.microsoft.com/en-us/library/ff193517.aspx" target="_blank">PivotTableAfterValueChange</a> event. I could have taken a number of approaches, and I’ll discuss the pros and cons of this approach below, but I chose this approach for this prototype because it was the most straightforward. When you type in a new value for a cell in a PivotTable, this event fires. This code borrows the ADO connection from the PivotTable and runs a simple query that retrieves the UPDATEABLE cell property for that cell’s coordinates in the cube. I definitely haven’t explored all the possible values of the UPDATEABLE cell property, so you’ll need to verify this code with your cube. But for the simple example in this blog post, I’ve seen two possible values. If UPDATEABLE is CELL_UPDATE_ENABLED then writeback is allowed and the cell is not secured. If UPDATEABLE is CELL_UPDATE_NOT_ENABLED_SECURE then the cell is secured and will produce an error upon writeback, so we discard that change and popup the error message:</p> <p><img style="background-image:none;border-bottom:0px;border-left:0px;padding-left:0px;padding-right:0px;display:inline;border-top:0px;border-right:0px;padding-top:0px" title=image border=0 alt=image src="/blogs/greggalloway/Lists/Posts/Attachments/20/image_cc01390d-6425-4b20-aa21-7f3e2113fb3d_47D68324.png" width=458 height=188></p> <p>In my mind, that’s a better user experience. You get immediate feedback that tells you which cell is the problem. So without further ado, here’s the code:</p> <p style="padding-bottom:0px;line-height:normal;margin:0in 0in 0pt;padding-top:0px" class=MsoNormal><font face="Courier New"><font size=2><span style="font-family:"><font color="#0000ff">Private</font></span><span style="font-family:"> <span><font color="#0000ff">Sub</font></span> Worksheet_PivotTableAfterValueChange(<span><font color="#0000ff">ByVal</font></span> TargetPivotTable <span><font color="#0000ff">As</font></span> PivotTable, <span><font color="#0000ff">ByVal</font></span> TargetRange <span><font color="#0000ff">As</font></span> Range)</span></font></font></p> <p style="padding-bottom:0px;line-height:normal;margin:0in 0in 0pt;padding-top:0px" class=MsoNormal><span style="font-family:"><font size=2 face="Courier New"> </font></span></p> <p style="padding-bottom:0px;line-height:normal;margin:0in 0in 0pt;padding-top:0px" class=MsoNormal><span style="font-family:"><font face="Courier New"><font size=2><span style="">    </span><span><font color="#0000ff">On</font></span> <span><font color="#0000ff">Error</font></span> <span><font color="#0000ff">GoTo</font></span> ErrHandler</font></font></span></p> <p style="padding-bottom:0px;line-height:normal;margin:0in 0in 0pt;padding-top:0px" class=MsoNormal><span style="font-family:"><font size=2 face="Courier New"> </font></span></p> <p style="padding-bottom:0px;line-height:normal;margin:0in 0in 0pt;padding-top:0px" class=MsoNormal><span style="font-family:"><font face="Courier New"><font size=2><span style="">    </span>Application.ScreenUpdating = <span><font color="#0000ff">False</font></span> <span><font color="#008000">' turns off screen updating</font></span></font></font></span></p> <p style="padding-bottom:0px;line-height:normal;margin:0in 0in 0pt;padding-top:0px" class=MsoNormal><span style="font-family:"><font face="Courier New"><font color="#008000" size=2> </font></font></span></p> <p style="padding-bottom:0px;line-height:normal;margin:0in 0in 0pt;padding-top:0px" class=MsoNormal><span style="font-family:"><font face="Courier New"><font size=2><span style="">    </span><span><font color="#008000">'from http://msdn.microsoft.com/en-us/library/ms145573.aspx</font></span></font></font></span></p> <p style="padding-bottom:0px;line-height:normal;margin:0in 0in 0pt;padding-top:0px" class=MsoNormal><span style="font-family:"><font face="Courier New"><font size=2><span style="">    </span><span><font color="#0000ff">Const</font></span> MD_MASK_ENABLED = &amp;H0</font></font></span></p> <p style="padding-bottom:0px;line-height:normal;margin:0in 0in 0pt;padding-top:0px" class=MsoNormal><span style="font-family:"><font face="Courier New"><font size=2><span style="">    </span><span><font color="#0000ff">Const</font></span> MD_MASK_NOT_ENABLED = &amp;H10000000</font></font></span></p> <p style="padding-bottom:0px;line-height:normal;margin:0in 0in 0pt;padding-top:0px" class=MsoNormal><span style="font-family:"><font face="Courier New"><font size=2><span style="">    </span><span><font color="#0000ff">Const</font></span> CELL_UPDATE_ENABLED = &amp;H1</font></font></span></p> <p style="padding-bottom:0px;line-height:normal;margin:0in 0in 0pt;padding-top:0px" class=MsoNormal><span style="font-family:"><font face="Courier New"><font size=2><span style="">    </span><span><font color="#0000ff">Const</font></span> CELL_UPDATE_ENABLED_WITH_UPDATE = &amp;H2</font></font></span></p> <p style="padding-bottom:0px;line-height:normal;margin:0in 0in 0pt;padding-top:0px" class=MsoNormal><span style="font-family:"><font face="Courier New"><font size=2><span style="">    </span><span><font color="#0000ff">Const</font></span> CELL_UPDATE_NOT_ENABLED_FORMULA = &amp;H10000001</font></font></span></p> <p style="padding-bottom:0px;line-height:normal;margin:0in 0in 0pt;padding-top:0px" class=MsoNormal><span style="font-family:"><font face="Courier New"><font size=2><span style="">    </span><span><font color="#0000ff">Const</font></span> CELL_UPDATE_NOT_ENABLED_NONSUM_MEASURE = &amp;H10000002</font></font></span></p> <p style="padding-bottom:0px;line-height:normal;margin:0in 0in 0pt;padding-top:0px" class=MsoNormal><span style="font-family:"><font face="Courier New"><font size=2><span style="">    </span><span><font color="#0000ff">Const</font></span> CELL_UPDATE_NOT_ENABLED_NACELL_VIRTUALCUBE = &amp;H10000003</font></font></span></p> <p style="padding-bottom:0px;line-height:normal;margin:0in 0in 0pt;padding-top:0px" class=MsoNormal><span style="font-family:"><font face="Courier New"><font size=2><span style="">    </span><span><font color="#0000ff">Const</font></span> CELL_UPDATE_NOT_ENABLED_SECURE = &amp;H10000005</font></font></span></p> <p style="padding-bottom:0px;line-height:normal;margin:0in 0in 0pt;padding-top:0px" class=MsoNormal><span style="font-family:"><font face="Courier New"><font size=2><span style="">    </span><span><font color="#0000ff">Const</font></span> CELL_UPDATE_NOT_ENABLED_CALCLEVEL = &amp;H10000006</font></font></span></p> <p style="padding-bottom:0px;line-height:normal;margin:0in 0in 0pt;padding-top:0px" class=MsoNormal><span style="font-family:"><font face="Courier New"><font size=2><span style="">    </span><span><font color="#0000ff">Const</font></span> CELL_UPDATE_NOT_ENABLED_CANNOTUPDATE = &amp;H10000007</font></font></span></p> <p style="padding-bottom:0px;line-height:normal;margin:0in 0in 0pt;padding-top:0px" class=MsoNormal><span style="font-family:"><font face="Courier New"><font size=2><span style="">    </span><span><font color="#0000ff">Const</font></span> CELL_UPDATE_NOT_ENABLED_INVALIDDIMENSIONTYPE = &amp;H10000009</font></font></span></p> <p style="padding-bottom:0px;line-height:normal;margin:0in 0in 0pt;padding-top:0px" class=MsoNormal><span style="font-family:"><font size=2 face="Courier New"> </font></span></p> <p style="padding-bottom:0px;line-height:normal;margin:0in 0in 0pt;padding-top:0px" class=MsoNormal><span style="font-family:"><font face="Courier New"><font size=2><span style="">    </span><span><font color="#0000ff">Dim</font></span> pcache <span><font color="#0000ff">As</font></span> PivotCache</font></font></span></p> <p style="padding-bottom:0px;line-height:normal;margin:0in 0in 0pt;padding-top:0px" class=MsoNormal><span style="font-family:"><font face="Courier New"><font size=2><span style="">    </span><font color="#0000ff">Set</font> pcache = TargetPivotTable.PivotCache</font></font></span></p> <p style="padding-bottom:0px;line-height:normal;margin:0in 0in 0pt;padding-top:0px" class=MsoNormal><span style="font-family:"><font size=2 face="Courier New"> </font></span></p> <p style="padding-bottom:0px;line-height:normal;margin:0in 0in 0pt;padding-top:0px" class=MsoNormal><span style="font-family:"><font face="Courier New"><font size=2><span style="">    </span><span><font color="#0000ff">If</font></span> <span><font color="#0000ff">Not</font></span> pcache.IsConnected <span><font color="#0000ff">Then</font></span></font></font></span></p> <p style="padding-bottom:0px;line-height:normal;margin:0in 0in 0pt;padding-top:0px" class=MsoNormal><span style="font-family:"><font face="Courier New"><font size=2><span style="">        </span>pcache.MakeConnection</font></font></span></p> <p style="padding-bottom:0px;line-height:normal;margin:0in 0in 0pt;padding-top:0px" class=MsoNormal><span style="font-family:"><font face="Courier New"><font size=2><span style="">    </span><span><font color="#0000ff">End</font></span> <span><font color="#0000ff">If</font></span></font></font></span></p> <p style="padding-bottom:0px;line-height:normal;margin:0in 0in 0pt;padding-top:0px" class=MsoNormal><span style="font-family:"><font face="Courier New"><font color="#0000ff" size=2> </font></font></span></p> <p style="padding-bottom:0px;line-height:normal;margin:0in 0in 0pt;padding-top:0px" class=MsoNormal><span style="font-family:"><font face="Courier New"><font size=2><span style="">    </span><span><font color="#0000ff">Dim</font></span> oCellset <span><font color="#0000ff">As</font></span> <span><font color="#0000ff">New</font></span> ADOMD.cellset</font></font></span></p> <p style="padding-bottom:0px;line-height:normal;margin:0in 0in 0pt;padding-top:0px" class=MsoNormal><span style="font-family:"><font face="Courier New"><font size=2><span style="">    </span>oCellset.ActiveConnection = pcache.ADOConnection</font></font></span></p> <p style="padding-bottom:0px;line-height:normal;margin:0in 0in 0pt;padding-top:0px" class=MsoNormal><span style="font-family:"><font size=2 face="Courier New"> </font></span></p> <p style="padding-bottom:0px;line-height:normal;margin:0in 0in 0pt;padding-top:0px" class=MsoNormal><span style="font-family:"><font face="Courier New"><font size=2><span style="">    </span><span><font color="#0000ff">Dim</font></span> sError <span><font color="#0000ff">As</font></span> <span><font color="#0000ff">String</font></span></font></font></span></p> <p style="padding-bottom:0px;line-height:normal;margin:0in 0in 0pt;padding-top:0px" class=MsoNormal><span style="font-family:"><font face="Courier New"><font size=2><span style="">    </span><span><font color="#0000ff">Dim</font></span> oPivotTableCell <span><font color="#0000ff">As</font></span> Range</font></font></span></p> <p style="padding-bottom:0px;line-height:normal;margin:0in 0in 0pt;padding-top:0px" class=MsoNormal><span style="font-family:"><font face="Courier New"><font size=2><span style="">    </span><span><font color="#0000ff">For</font></span> <span><font color="#0000ff">Each</font></span> oPivotTableCell <span><font color="#0000ff">In</font></span> TargetRange.Cells</font></font></span></p> <p style="padding-bottom:0px;line-height:normal;margin:0in 0in 0pt;padding-top:0px" class=MsoNormal><span style="font-family:"><font face="Courier New"><font size=2><span style="">        </span><span><font color="#0000ff">Dim</font></span> oPivotCell <span><font color="#0000ff">As</font></span> PivotCell</font></font></span></p> <p style="padding-bottom:0px;line-height:normal;margin:0in 0in 0pt;padding-top:0px" class=MsoNormal><span style="font-family:"><font face="Courier New"><font size=2><span style="">        <font color="#0000ff">Set</font> </span>oPivotCell = oPivotTableCell.PivotCell</font></font></span></p> <p style="padding-bottom:0px;line-height:normal;margin:0in 0in 0pt;padding-top:0px" class=MsoNormal><span style="font-family:"><font size=2 face="Courier New"> </font></span></p> <p style="padding-bottom:0px;line-height:normal;margin:0in 0in 0pt;padding-top:0px" class=MsoNormal><span style="font-family:"><font face="Courier New"><font size=2><span style="">        </span><span><font color="#0000ff">Dim</font></span> sMDX <span><font color="#0000ff">As</font></span> <span><font color="#0000ff">String</font></span></font></font></span></p> <p style="padding-bottom:0px;line-height:normal;margin:0in 0in 0pt;padding-top:0px" class=MsoNormal><span style="font-family:"><font face="Courier New"><font size=2><span style="">        </span>sMDX = <span><font color="#a31515">&quot;select from [&quot;</font></span> &amp; pcache.CommandText &amp; <span><font color="#a31515">&quot;] WHERE &quot; </font><font color="#000000">_</font></span> <br><span style="font-family:"><font face="Courier New"><span style="">         </span></font></span>&amp; oPivotCell.MDX &amp; <span><font color="#a31515">&quot; CELL PROPERTIES UPDATEABLE&quot;</font></span></font></font></span></p> <p style="padding-bottom:0px;line-height:normal;margin:0in 0in 0pt;padding-top:0px" class=MsoNormal><span style="font-family:"><font face="Courier New"><font color="#a31515" size=2> </font></font></span></p> <p style="padding-bottom:0px;line-height:normal;margin:0in 0in 0pt;padding-top:0px" class=MsoNormal><span style="font-family:"><font face="Courier New"><font size=2><span style="">        </span>oCellset.Source = sMDX</font></font></span></p> <p style="padding-bottom:0px;line-height:normal;margin:0in 0in 0pt;padding-top:0px" class=MsoNormal><span style="font-family:"><font face="Courier New"><font size=2><span style="">        </span>oCellset.Open</font></font></span></p> <p style="padding-bottom:0px;line-height:normal;margin:0in 0in 0pt;padding-top:0px" class=MsoNormal><span style="font-family:"><font size=2 face="Courier New"> </font></span></p> <p style="padding-bottom:0px;line-height:normal;margin:0in 0in 0pt;padding-top:0px" class=MsoNormal><span style="font-family:"><font face="Courier New"><font size=2><span style="">        </span><span><font color="#0000ff">Dim</font></span> iUPDATEABLE <span><font color="#0000ff">As</font></span> <span><font color="#0000ff">Long</font></span></font></font></span></p> <p style="padding-bottom:0px;line-height:normal;margin:0in 0in 0pt;padding-top:0px" class=MsoNormal><span style="font-family:"><font face="Courier New"><font size=2><span style="">        </span>iUPDATEABLE = oCellset(0).Properties(<span><font color="#a31515">&quot;UPDATEABLE&quot;</font></span>)</font></font></span></p> <p style="padding-bottom:0px;line-height:normal;margin:0in 0in 0pt;padding-top:0px" class=MsoNormal><span style="font-family:"><font size=2 face="Courier New"> </font></span></p> <p style="padding-bottom:0px;line-height:normal;margin:0in 0in 0pt;padding-top:0px" class=MsoNormal><span style="font-family:"><font face="Courier New"><font size=2><span style="">        </span><span><font color="#0000ff">If</font></span> iUPDATEABLE = CELL_UPDATE_ENABLED <span><font color="#0000ff">Then</font></span></font></font></span></p> <p style="padding-bottom:0px;line-height:normal;margin:0in 0in 0pt;padding-top:0px" class=MsoNormal><span style="font-family:"><font face="Courier New"><font size=2><span style="">            </span><span><font color="#008000">'update allowed</font></span></font></font></span></p> <p style="padding-bottom:0px;line-height:normal;margin:0in 0in 0pt;padding-top:0px" class=MsoNormal><span style="font-family:"><font face="Courier New"><font size=2><span style="">        </span><span><font color="#0000ff">ElseIf</font></span> iUPDATEABLE = CELL_UPDATE_NOT_ENABLED_SECURE <span><font color="#0000ff">Then</font></span></font></font></span></p> <p style="padding-bottom:0px;line-height:normal;margin:0in 0in 0pt;padding-top:0px" class=MsoNormal><span style="font-family:"><font face="Courier New"><font size=2><span style="">            </span>sError = sError &amp; <span><font color="#a31515">&quot;Cell &quot;</font></span> &amp; Replace(oPivotTableCell.Address, <span><font color="#a31515">&quot;$&quot;</font></span>, <span><font color="#a31515">&quot;&quot;</font></span>) _<br><span style="font-family:"><font face="Courier New"><span style="">             </span></font></span>&amp; <span><font color="#a31515">&quot; is secured.&quot;</font></span> &amp; vbCrLf</font></font></span></p> <p style="padding-bottom:0px;line-height:normal;margin:0in 0in 0pt;padding-top:0px" class=MsoNormal><span style="font-family:"><font face="Courier New"><font size=2><span style="">            </span>oPivotCell.DiscardChange</font></font></span></p> <p style="padding-bottom:0px;line-height:normal;margin:0in 0in 0pt;padding-top:0px" class=MsoNormal><span style="font-family:"><font face="Courier New"><font size=2><span style="">        </span><span><font color="#0000ff">End</font></span> <span><font color="#0000ff">If</font></span></font></font></span></p> <p style="padding-bottom:0px;line-height:normal;margin:0in 0in 0pt;padding-top:0px" class=MsoNormal><span style="font-family:"><font face="Courier New"><font color="#0000ff" size=2> </font></font></span></p> <p style="padding-bottom:0px;line-height:normal;margin:0in 0in 0pt;padding-top:0px" class=MsoNormal><span style="font-family:"><font face="Courier New"><font size=2><span style="">        </span>oCellset.Close</font></font></span></p> <p style="padding-bottom:0px;line-height:normal;margin:0in 0in 0pt;padding-top:0px" class=MsoNormal><span style="font-family:"><font size=2 face="Courier New"> </font></span></p> <p style="padding-bottom:0px;line-height:normal;margin:0in 0in 0pt;padding-top:0px" class=MsoNormal><span style="font-family:"><font face="Courier New"><font size=2><span style="">    </span><span><font color="#0000ff">Next</font></span></font></font></span></p> <p style="padding-bottom:0px;line-height:normal;margin:0in 0in 0pt;padding-top:0px" class=MsoNormal><span style="font-family:"><font face="Courier New"><font color="#0000ff" size=2> </font></font></span></p> <p style="padding-bottom:0px;line-height:normal;margin:0in 0in 0pt;padding-top:0px" class=MsoNormal><span style="font-family:"><font face="Courier New"><font size=2><span style="">    </span><span><font color="#0000ff">If</font></span> sError &lt;&gt; <span><font color="#a31515">&quot;&quot;</font></span> <span><font color="#0000ff">Then</font></span></font></font></span></p> <p style="padding-bottom:0px;line-height:normal;margin:0in 0in 0pt;padding-top:0px" class=MsoNormal><span style="font-family:"><font face="Courier New"><font size=2><span style="">        </span>sError = </font><font size=2><span><font color="#a31515">&quot;The following cells do not allow writeback, &quot; _<br><span style="font-family:"><font face="Courier New"><span style="">         &amp; &quot;</span></font></span>so their values were discarded.&quot;</font></span> &amp; vbCrLf &amp; vbCrLf &amp; sError</font></font></span></p> <p style="padding-bottom:0px;line-height:normal;margin:0in 0in 0pt;padding-top:0px" class=MsoNormal><span style="font-family:"><font face="Courier New"><font size=2><span style="">        </span>MsgBox(sError)</font></font></span></p> <p style="padding-bottom:0px;line-height:normal;margin:0in 0in 0pt;padding-top:0px" class=MsoNormal><span style="font-family:"><font face="Courier New"><font size=2><span style="">    </span><span><font color="#0000ff">End</font></span> <span><font color="#0000ff">If</font></span></font></font></span></p> <p style="padding-bottom:0px;line-height:normal;margin:0in 0in 0pt;padding-top:0px" class=MsoNormal><span style="font-family:"><font face="Courier New"><font color="#0000ff" size=2> </font></font></span></p> <p style="padding-bottom:0px;line-height:normal;margin:0in 0in 0pt;padding-top:0px" class=MsoNormal><span style="font-family:"><font size=2 face="Courier New">ErrHandler:</font></span></p> <p style="padding-bottom:0px;line-height:normal;margin:0in 0in 0pt;padding-top:0px" class=MsoNormal><span style="font-family:"><font face="Courier New"><font size=2><span style="">    </span>Application.ScreenUpdating = <span><font color="#0000ff">True</font></span> <span><font color="#008000">' turns on screen updating</font></span></font></font></span></p> <p style="padding-bottom:0px;line-height:normal;margin:0in 0in 0pt;padding-top:0px" class=MsoNormal><span style="font-family:"><font face="Courier New"><font color="#008000" size=2> </font></font></span></p> <p style="padding-bottom:0px;line-height:normal;margin:0in 0in 0pt;padding-top:0px" class=MsoNormal><font face="Courier New"><font size=2><span style="font-family:"><font color="#0000ff">End</font></span><span style="font-family:"> <span><font color="#0000ff">Sub</font></span></span></font></font></p> <p style="padding-bottom:0px;line-height:13pt;margin:0in 0in 10pt;padding-top:0px" class=MsoNormal><font face=Calibri><font style="font-size:11pt"> </font></font></p> <p>There is one main downside to this approach. It can slow down the data entry process since after each cell is changed, we execute one MDX query per cell to retrieve the UPDATEABLE cell property. This is generally fairly snappy as running 100 of these MDX queries only took a second against the local network and several seconds over a slower VPN connection. But when there are a bunch of secured cells you attempt to update, discarding each change one cell at a time takes a couple of seconds, which adds up. Another downside to consider is that if you enter a change on Monday then save the workbook without publishing those changes, when you open the workbook on Tuesday your writeback permissions in the cube may have changed yet this VBA code doesn’t rerun for previous changes you’ve entered but not published.</p> <p>Another approach would be to catch the PivotTableUpdate event and grab the coordinates of all the cells and run one MDX query to retrieve the UPDATEABLE property for all cells in the PivotTable, caching this information for later when the user updates cells. The reason this approach would be more complex is because the .MDX property of a subtotal PivotCell returns a tuple with one less member than a PivotCell that’s a detail level cell since the All member isn’t mentioned in the subtotal’s MDX. And you can’t easily combine two tuples with different dimensionality. So I went with the more straightforward approach for this prototype.</p> <p>Note in the VBA above the use of the ADOMD.Cellset object. This requires a reference to any version of Microsoft ActiveX Data Objects (Multi-dimensional), and you can add that reference from within the VBA editor from the Tools… References menu:</p> <p><img style="background-image:none;border-bottom:0px;border-left:0px;padding-left:0px;padding-right:0px;display:inline;border-top:0px;border-right:0px;padding-top:0px" title=image border=0 alt=image src="/blogs/greggalloway/Lists/Posts/Attachments/20/image_d8bf1a24-fb88-4777-9f78-62671951c5f8_60D25369.png" width=463 height=374></p> <p> </p><strong>What’s He Not Telling Me?<br></strong> <p>This post focused on role-based security for writeback and how to improve that user experience. I didn’t talk about setting up a <a href="http://msdn.microsoft.com/en-us/library/ms175664(v=sql.90).aspx" target="_blank">writeback partition</a> or whether to choose MOLAP or ROLAP writeback. I didn’t discuss how allocation method impacts writeback performance. I didn’t discuss how to writeback to non-leaf cells. I didn’t discuss the <a href="http://blogs.msdn.com/b/sqlcat/archive/2009/04/16/proper-partitioning-can-improve-dramatically-the-writeback-process-when-dealing-with-large-data-sets.aspx" target="_blank">necessity</a> of periodically moving data from the writeback table into the regular fact table. Maybe I’ll blog about those in the future.</p> <p>I also didn’t discuss the pros and cons of the different architectures we could have chosen. This project lent itself to extending our existing time entry reporting cube with forecast writeback, but we could have also chosen to implement a good budgeting and planning tool like <a href="http://www.defactoglobal.com/application/deFacto_software.html" target="_blank">deFacto</a>. That decision process is a blog post for another time.<br><br></p> <p><strong>Conclusion<br></strong>To summarize, be sure to grant read permissions and write permissions in separate roles if the user should be able to read more of the cube than they can write to. The UPDATEABLE cell property can come in handy if you want to know in advance which cells a user has permissions to update. With a little VBA, it’s pretty straightforward to improve the writeback experience where writeback security is involved by querying the UPDATEABLE cell property.</p> <p><em><font color="#ff0000"><strong>Updates</strong><br>1. Fixing some VBA formatting/syntax issues that happened when I pasted my the code into my blog<br>2. Updating the status of a hotfix for the SSAS bug I mentioned which will be fixed in SQL 2008 R2 SP1 CU6.</font></em></p></div></div>
<div><b>Category:</b> SSAS;MDX</div>
<div><b>Published:</b> 1/10/2012 7:31 PM</div>
<div><b>Archives:</b> January 2012</div>
<div><b>ArchiveSort:</b> 20120110</div>
<div><b>Attachments:</b> <a href="http://www.artisconsulting.com/blogs/greggalloway/Lists/Posts/Attachments/20/image_3_47D68324.png">http://www.artisconsulting.com/blogs/greggalloway/Lists/Posts/Attachments/20/image_3_47D68324.png</a><br><a href="http://www.artisconsulting.com/blogs/greggalloway/Lists/Posts/Attachments/20/image_308d37b7-44f8-41ce-80e9-94c5588bb0fe_47D68324.png">http://www.artisconsulting.com/blogs/greggalloway/Lists/Posts/Attachments/20/image_308d37b7-44f8-41ce-80e9-94c5588bb0fe_47D68324.png</a><br><a href="http://www.artisconsulting.com/blogs/greggalloway/Lists/Posts/Attachments/20/image_4ea50396-da9e-42d6-9ce8-9ce6079d7c82_47D68324.png">http://www.artisconsulting.com/blogs/greggalloway/Lists/Posts/Attachments/20/image_4ea50396-da9e-42d6-9ce8-9ce6079d7c82_47D68324.png</a><br><a href="http://www.artisconsulting.com/blogs/greggalloway/Lists/Posts/Attachments/20/image_6434497c-baba-4ce8-9191-00eaa24cfcb9_47D68324.png">http://www.artisconsulting.com/blogs/greggalloway/Lists/Posts/Attachments/20/image_6434497c-baba-4ce8-9191-00eaa24cfcb9_47D68324.png</a><br><a href="http://www.artisconsulting.com/blogs/greggalloway/Lists/Posts/Attachments/20/image_a6ae1a16-8a05-4739-8f56-5d6926c84666_47D68324.png">http://www.artisconsulting.com/blogs/greggalloway/Lists/Posts/Attachments/20/image_a6ae1a16-8a05-4739-8f56-5d6926c84666_47D68324.png</a><br><a href="http://www.artisconsulting.com/blogs/greggalloway/Lists/Posts/Attachments/20/image_cc01390d-6425-4b20-aa21-7f3e2113fb3d_47D68324.png">http://www.artisconsulting.com/blogs/greggalloway/Lists/Posts/Attachments/20/image_cc01390d-6425-4b20-aa21-7f3e2113fb3d_47D68324.png</a><br><a href="http://www.artisconsulting.com/blogs/greggalloway/Lists/Posts/Attachments/20/image_d8bf1a24-fb88-4777-9f78-62671951c5f8_60D25369.png">http://www.artisconsulting.com/blogs/greggalloway/Lists/Posts/Attachments/20/image_d8bf1a24-fb88-4777-9f78-62671951c5f8_60D25369.png</a><br><a href=""></a></div>
]]></description>
      <author>Greg Galloway</author>
      <category>SSAS;MDX</category>
      <pubDate>Wed, 11 Jan 2012 02:31:01 GMT</pubDate>
      <guid isPermaLink="true">http://www.artisconsulting.com/blogs/greggalloway/Lists/Posts/ViewPost.aspx?ID=20</guid>
    </item>
  </channel>
</rss>
