<?xml version="1.0" encoding="UTF-8" ?>
<?xml-stylesheet type="text/xsl" href="http://blogs.adatis.co.uk/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>Jeremy Kashel&amp;#39;s Blog : SQL, PerformancePoint</title><link>http://blogs.adatis.co.uk/blogs/jeremykashel/archive/tags/SQL/PerformancePoint/default.aspx</link><description>Tags: SQL, PerformancePoint</description><dc:language>en</dc:language><generator>CommunityServer 2007 SP2 (Build: 20611.960)</generator><item><title>Audit Trail in PerformancePoint Planning</title><link>http://blogs.adatis.co.uk/blogs/jeremykashel/archive/2008/12/15/audit-trail-in-performancepoint-planning.aspx</link><pubDate>Mon, 15 Dec 2008 15:49:05 GMT</pubDate><guid isPermaLink="false">8d7d37f8-4a66-4c95-9fba-293fa87607dc:7485</guid><dc:creator>Jeremy Kashel</dc:creator><slash:comments>0</slash:comments><wfw:commentRss xmlns:wfw="http://wellformedweb.org/CommentAPI/">http://blogs.adatis.co.uk/blogs/jeremykashel/rsscomments.aspx?PostID=7485</wfw:commentRss><comments>http://blogs.adatis.co.uk/blogs/jeremykashel/archive/2008/12/15/audit-trail-in-performancepoint-planning.aspx#comments</comments><description>&lt;p&gt;I&amp;#39;ve noticed that the PPS Technet documentation has been updated recently to include an official Microsoft method to carry out auditing in PPS Planning.&lt;/p&gt;  &lt;p&gt;PPS will do some basic auditing out of the box, namely to the audit.log file on the server. This will automatically capture key events that occur on the server, e.g. creation of a model, updating of a dimension etc. The audit file does not, however, track changes to the model fact data. There has been a custom solution around for this for a while now - &lt;a href="http://blogs.adatis.co.uk/blogs/sachatomey/default.aspx" target="_blank"&gt;Sacha&lt;/a&gt; has written an excellent post &lt;a href="http://blogs.adatis.co.uk/blogs/sachatomey/default.aspx?PageIndex=2" target="_blank"&gt;here&lt;/a&gt; that details what you need to do in order to implement your own PPS audit trail.&lt;/p&gt;  &lt;p&gt;Like Sacha&amp;#39;s method, the Microsoft approach involves creating auditing tables, which should then be populated by running a custom stored procedure. The stored procedure should then be scheduled on a periodic basis (e.g. hourly) to capture any new activity. This is a bit different to Sacha&amp;#39;s method, where triggers are used to capture changes in real-time as they occur. In both cases the idea is to use something like Reporting Services to to view detailed auditing reports on your PPS data.&lt;/p&gt;  &lt;p&gt;One thing that did catch my eye on in the Technet documentation is a method to decode the binary &amp;#39;change list&amp;#39; column that&amp;#39;s held in the dbo.Submissions table. Whereas you can manually export the change list to a CSV file, there has historically been no way to take what&amp;#39;s in the change list column and automatically decode it into a useful format. The following C# code will read the change list, and then insert it into your newly created auditing table:&lt;/p&gt;  &lt;pre class="code"&gt;&lt;span style="color:teal;"&gt;DataSet &lt;/span&gt;ds = &lt;span style="color:blue;"&gt;new &lt;/span&gt;&lt;span style="color:teal;"&gt;DataSet&lt;/span&gt;();
DataLayer dl = &lt;span style="color:blue;"&gt;new &lt;/span&gt;DataLayer(&lt;span style="color:maroon;"&gt;&amp;quot;PPSConnection&amp;quot;&lt;/span&gt;);
ds = dl.ExecuteDataSetFromSQL(&lt;span style="color:maroon;"&gt;&amp;quot;SELECT [SubmissionID]FROM [_AppDB].[dbo].[Submissions] s1 where &lt;br /&gt;s1.SubmissionID not in (select SubmissionID from [_StagingDB].[dbo].[SubmissionsAudited]) &lt;br /&gt;and s1.[Status] = 0&amp;quot;&lt;/span&gt;);
&lt;span style="color:blue;"&gt;string &lt;/span&gt;sSQL = &lt;span style="color:maroon;"&gt;&amp;quot;&amp;quot;&lt;/span&gt;;

&lt;span style="color:blue;"&gt;foreach &lt;/span&gt;(&lt;span style="color:teal;"&gt;DataRow &lt;/span&gt;r &lt;span style="color:blue;"&gt;in &lt;/span&gt;ds.Tables[0].Rows)
{
    sSQL = &lt;span style="color:maroon;"&gt;@&amp;quot;INSERT INTO SubmissionsAudited(&amp;#8230; ) VALUES(&amp;quot;&lt;/span&gt;;

    &lt;span style="color:green;"&gt;// RETRIEVE THE CHANGELIST FOR THIS SUBMISSION 
    &lt;/span&gt;DataSetWrapper dsw = &lt;span style="color:blue;"&gt;new &lt;/span&gt;DataSetWrapper((&lt;span style="color:teal;"&gt;Byte&lt;/span&gt;[])r[&lt;span style="color:maroon;"&gt;&amp;quot;ChangeList&amp;quot;&lt;/span&gt;]);
    &lt;span style="color:blue;"&gt;foreach &lt;/span&gt;(&lt;span style="color:teal;"&gt;DataRow &lt;/span&gt;cldr &lt;span style="color:blue;"&gt;in &lt;/span&gt;dsw.DataSet.Tables[0].Rows)
    {
        &lt;span style="color:green;"&gt;// SUBMISSION ROW DATA
        &lt;/span&gt;sSQL += r[0].ToString() + &lt;span style="color:maroon;"&gt;&amp;quot;, &amp;quot;
        &lt;/span&gt;+ r[1].ToString() + &lt;span style="color:maroon;"&gt;&amp;quot;, &amp;quot;
        &lt;/span&gt;+ r[2].ToString() + &lt;span style="color:maroon;"&gt;&amp;quot;, &amp;quot;
        &lt;/span&gt;+ r[3].ToString() + &lt;span style="color:maroon;"&gt;&amp;quot;, &amp;#39;&amp;quot;
        &lt;/span&gt;+ r[4].ToString() + &lt;span style="color:maroon;"&gt;&amp;quot;&amp;#39;, &amp;quot;&lt;/span&gt;;

        &lt;span style="color:green;"&gt;// CHANGELIST ROW DATA
        &lt;/span&gt;&lt;span style="color:blue;"&gt;foreach &lt;/span&gt;(&lt;span style="color:blue;"&gt;object &lt;/span&gt;o &lt;span style="color:blue;"&gt;in &lt;/span&gt;cldr.ItemArray)
        {
            sSQL += &lt;span style="color:maroon;"&gt;&amp;quot;,&amp;quot; &lt;/span&gt;+ o.ToString();
        }
        sSQL += &lt;span style="color:maroon;"&gt;&amp;quot;)&amp;quot;&lt;/span&gt;;
    }
    &lt;span style="color:green;"&gt;// STORE EACH CHANGE TO THE AUDIT TABLE
    &lt;/span&gt;dl.ExecuteNonQuery(sSQL);&lt;/pre&gt;
&lt;a href="http://11011.net/software/vspaste"&gt;&lt;/a&gt;&lt;a href="http://11011.net/software/vspaste"&gt;&lt;/a&gt;&lt;a href="http://11011.net/software/vspaste"&gt;&lt;/a&gt;

&lt;p&gt;Click &lt;a href="http://technet.microsoft.com/en-us/library/dd309567.aspx" target="_blank"&gt;here&lt;/a&gt; to view the Technet documentation.&lt;/p&gt;

&lt;div class="wlWriterSmartContent" id="scid:0767317B-992E-4b12-91E0-4F059A8CECA8:ab05c10d-b3ae-4d58-9e82-bde06bc7b995" style="padding-right:0px;display:inline;padding-left:0px;padding-bottom:0px;margin:0px;padding-top:0px;"&gt;Technorati Tags: &lt;a href="http://technorati.com/tags/PerformancePoint" rel="tag"&gt;PerformancePoint&lt;/a&gt;,&lt;a href="http://technorati.com/tags/SQL" rel="tag"&gt;SQL&lt;/a&gt;,&lt;a href="http://technorati.com/tags/C#" rel="tag"&gt;C#&lt;/a&gt;&lt;/div&gt;&lt;img src="http://blogs.adatis.co.uk/aggbug.aspx?PostID=7485" width="1" height="1"&gt;</description><category domain="http://blogs.adatis.co.uk/blogs/jeremykashel/archive/tags/PerformancePoint/default.aspx">PerformancePoint</category><category domain="http://blogs.adatis.co.uk/blogs/jeremykashel/archive/tags/SQL/default.aspx">SQL</category><category domain="http://blogs.adatis.co.uk/blogs/jeremykashel/archive/tags/C_2300_/default.aspx">C#</category></item><item><title>Entering Dates in PPS Planning Assignments</title><link>http://blogs.adatis.co.uk/blogs/jeremykashel/archive/2008/11/27/entering-dates-in-pps-planning-assignments.aspx</link><pubDate>Thu, 27 Nov 2008 09:14:57 GMT</pubDate><guid isPermaLink="false">8d7d37f8-4a66-4c95-9fba-293fa87607dc:7472</guid><dc:creator>Jeremy Kashel</dc:creator><slash:comments>2</slash:comments><wfw:commentRss xmlns:wfw="http://wellformedweb.org/CommentAPI/">http://blogs.adatis.co.uk/blogs/jeremykashel/rsscomments.aspx?PostID=7472</wfw:commentRss><comments>http://blogs.adatis.co.uk/blogs/jeremykashel/archive/2008/11/27/entering-dates-in-pps-planning-assignments.aspx#comments</comments><description>&lt;p&gt;In the recent PPS Planning projects that I&amp;#39;ve been involved in, the challenges have often been around subjects such as business rules, hence the often recurring theme of this blog. Recently the tables were turned though, as I was told by a user that they wanted to enter dates into a PPS assignment. &lt;/p&gt;  &lt;p&gt;I was initially a bit concerned that the Excel add-in may not be able to deliver here - after all its great at capturing numbers, but knowing the rigid structure of the fact tables, I couldn&amp;#39;t see how it would manage to store a date. Then I remembered something from my VBA days many years ago - that is that Excel stores dates as a number from 30/12/1899, meaning in theory it should be possible to get dates working in PPS. Thankfully it is possible, as this post explains.&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;Excel Setup&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;The first step to get this working when designing your form template is to set the matrix to have a matrix style of &amp;#39;none&amp;#39;. If you don&amp;#39;t do this, then the built-in matrix styles will over-ride your formatting changes to the required cells. Speaking of formatting, the next step is to format the data entry cells that will contain dates, just using the standard Excel formatting window:&lt;/p&gt;  &lt;p&gt;&lt;a href="http://blogs.adatis.co.uk/blogs/jeremykashel/WindowsLiveWriter/EnteringDatesinPPSPlanningAssignments_12FBC/image_2.png"&gt;&lt;img style="border-top-width:0px;border-left-width:0px;border-bottom-width:0px;border-right-width:0px;" height="373" alt="image" src="http://blogs.adatis.co.uk/blogs/jeremykashel/WindowsLiveWriter/EnteringDatesinPPSPlanningAssignments_12FBC/image_thumb.png" width="519" border="0" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;Once these few simple steps are done, then the assignment will behave just like any other. As the date is stored as a number, the numeric representation of the date will end up in the fact table just as any other piece of data.&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;Dates in Business Rules&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;Once the numbers are in the fact table, we need to convert them to dates to use them in business rules in some way. We can&amp;#39;t do much in PEL unfortunately, so the options are either NativeMDX or NativeSQL.&lt;/p&gt;  &lt;p&gt;As Analysis Services can pickup some of the VBA functions, it&amp;#39;s possible to use the &lt;a href="http://www.techonthenet.com/excel/formulas/dateadd.php" target="_blank"&gt;VBA DateAdd() function&lt;/a&gt; to convert the stored number back into a date. So in the example below, I&amp;#39;m using the DateAdd() function to convert the number to a date, before comparing the resulting date against another date using the VBA DateDiff() function:&lt;/p&gt;  &lt;pre class="code"&gt;&lt;span style="color:blue;"&gt;WITH 
MEMBER    &lt;/span&gt;[Measures].[DateExample] &lt;span style="color:blue;"&gt;AS &lt;/span&gt;VBA!DateAdd(&amp;quot;d&amp;quot;, [Measures].[Value], &amp;quot;30/12/1899&amp;quot;)
&lt;span style="color:blue;"&gt;MEMBER    &lt;/span&gt;[Measures].[DateDiff] &lt;span style="color:blue;"&gt;AS &lt;/span&gt;VBA!DateDiff(&amp;quot;d&amp;quot;, [Measures].[DateExample], &amp;quot;01/07/1987&amp;quot;)
&lt;span style="color:blue;"&gt;SELECT    Descendants&lt;/span&gt;([Time].[Monthly].[Year].&amp;amp;[2008],,&lt;span style="color:blue;"&gt;leaves&lt;/span&gt;) &lt;span style="color:blue;"&gt;ON &lt;/span&gt;0
&lt;span style="color:blue;"&gt;FROM      &lt;/span&gt;[Strategic Planning]
&lt;span style="color:blue;"&gt;WHERE     &lt;/span&gt;([Account].[Profit and Loss].&amp;amp;[5010], [Measures].[DateDiff], [Entity].[Divisions].&amp;amp;[5003])&lt;/pre&gt;
&lt;a href="http://11011.net/software/vspaste"&gt;&lt;/a&gt;

&lt;p&gt;Although the above is just a simple example, it should give you the idea of the kind of calculations that can be performed in Analysis Services. It&amp;#39;s possible to use these functions via a NativeMDXScript or a NativeMDXQuery.&lt;/p&gt;

&lt;p&gt;It&amp;#39;s a similar story with SQL, as it also has its own &lt;a href="http://msdn.microsoft.com/en-us/library/ms186819.aspx" target="_blank"&gt;DateAdd() function&lt;/a&gt;, as shown in the simple select statement below:&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color:blue;"&gt;SELECT    &lt;/span&gt;&lt;span style="color:magenta;"&gt;DateAdd&lt;/span&gt;&lt;span style="color:gray;"&gt;(&lt;/span&gt;d&lt;span style="color:gray;"&gt;, &lt;/span&gt;[Value]&lt;span style="color:gray;"&gt;, &lt;/span&gt;&lt;span style="color:red;"&gt;&amp;#39;30/12/1899&amp;#39;&lt;/span&gt;&lt;span style="color:gray;"&gt;)
&lt;/span&gt;&lt;span style="color:blue;"&gt;FROM      &lt;/span&gt;dbo&lt;span style="color:gray;"&gt;.&lt;/span&gt;[MG_Strategic Planning_MeasureGroup_default_partition]
&lt;span style="color:blue;"&gt;WHERE     &lt;/span&gt;Scenario_memberid &lt;span style="color:gray;"&gt;= &lt;/span&gt;4 &lt;span style="color:gray;"&gt;AND &lt;/span&gt;Account_MemberId &lt;span style="color:gray;"&gt;= &lt;/span&gt;5010&lt;/pre&gt;
&lt;a href="http://11011.net/software/vspaste"&gt;&lt;/a&gt;&lt;a href="http://11011.net/software/vspaste"&gt;&lt;/a&gt;

&lt;p&gt;So it&amp;#39;s a shame that PEL can&amp;#39;t work with dates, but the fact that both the database engine and Analysis Services have a DateAdd function means that it&amp;#39;s possible to use dates for logic in both definition and procedural business rules.&lt;/p&gt;

&lt;div class="wlWriterSmartContent" id="scid:0767317B-992E-4b12-91E0-4F059A8CECA8:f88f1694-887d-4e61-bc4f-fa717d954896" style="padding-right:0px;display:inline;padding-left:0px;padding-bottom:0px;margin:0px;padding-top:0px;"&gt;Technorati Tags: &lt;a href="http://technorati.com/tags/PerformancePoint" rel="tag"&gt;PerformancePoint&lt;/a&gt;,&lt;a href="http://technorati.com/tags/MDX" rel="tag"&gt;MDX&lt;/a&gt;,&lt;a href="http://technorati.com/tags/SQL" rel="tag"&gt;SQL&lt;/a&gt;,&lt;a href="http://technorati.com/tags/VBA" rel="tag"&gt;VBA&lt;/a&gt;,&lt;a href="http://technorati.com/tags/Excel" rel="tag"&gt;Excel&lt;/a&gt;&lt;/div&gt;&lt;img src="http://blogs.adatis.co.uk/aggbug.aspx?PostID=7472" width="1" height="1"&gt;</description><category domain="http://blogs.adatis.co.uk/blogs/jeremykashel/archive/tags/MDX/default.aspx">MDX</category><category domain="http://blogs.adatis.co.uk/blogs/jeremykashel/archive/tags/PerformancePoint/default.aspx">PerformancePoint</category><category domain="http://blogs.adatis.co.uk/blogs/jeremykashel/archive/tags/Excel/default.aspx">Excel</category><category domain="http://blogs.adatis.co.uk/blogs/jeremykashel/archive/tags/SQL/default.aspx">SQL</category><category domain="http://blogs.adatis.co.uk/blogs/jeremykashel/archive/tags/VBA/default.aspx">VBA</category></item><item><title>Tracking PerformancePoint Planning Submissions Using Reporting Services</title><link>http://blogs.adatis.co.uk/blogs/jeremykashel/archive/2008/09/26/tracking-performancepoint-planning-submissions-using-reporting-services.aspx</link><pubDate>Fri, 26 Sep 2008 16:29:29 GMT</pubDate><guid isPermaLink="false">8d7d37f8-4a66-4c95-9fba-293fa87607dc:7411</guid><dc:creator>Jeremy Kashel</dc:creator><slash:comments>0</slash:comments><wfw:commentRss xmlns:wfw="http://wellformedweb.org/CommentAPI/">http://blogs.adatis.co.uk/blogs/jeremykashel/rsscomments.aspx?PostID=7411</wfw:commentRss><comments>http://blogs.adatis.co.uk/blogs/jeremykashel/archive/2008/09/26/tracking-performancepoint-planning-submissions-using-reporting-services.aspx#comments</comments><description>&lt;p&gt;The standard &lt;a href="http://technet.microsoft.com/en-us/library/bb838765.aspx" target="_blank"&gt;operational reports&lt;/a&gt; that come with PerformancePoint will allow you to report on a variety of PerformancePoint admin related activities, such as cycles, assignments, forms, jobs and associations.&lt;/p&gt;  &lt;p&gt;I find that the assignments report is particularly useful - after all, finding out who has/hasn&amp;#39;t submitted is an important part of any data-gathering exercise. Whilst it is useful, I do find that the assignments report is the one that admin users want changed, especially when a model site exists containing many cycles and assignments.&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;Extra Functionality&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;With a large PPS Planning implementation you can easily end up with many assignment instances, cycles and users. I&amp;#39;ve been involved in such an implementation recently, and, due to the large number of assignments, the admin user requested a bit more filtering capability than the out of the box assignments report provides.&lt;/p&gt;  &lt;p&gt;Also, the existing assignments report tells will tell you that user A has submitted their assignment, but it won&amp;#39;t go into any detail about what the submission actually contained. E.g. did the user submit all their entities? For some users it is quite key to know what other users have been submitting - for one thing it makes navigation easier if as an approver you know exactly which department/entity to pick in the assignment filters.&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;Examples&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;By knowing which tables to use, you can write an SSRS report that provides the additional functionality mentioned above.&lt;/p&gt;  &lt;p&gt;The starting point is to get the base report query right. In my case, as I&amp;#39;m in a separate auditing database, the query goes inside a custom stored procedure, and is as follows:&lt;/p&gt;  &lt;pre class="code"&gt;&lt;span style="color:blue;"&gt;SELECT              &lt;/span&gt;A&lt;span style="color:gray;"&gt;.&lt;/span&gt;AssignmentId&lt;span style="color:gray;"&gt;,    &lt;/span&gt;C&lt;span style="color:gray;"&gt;.&lt;/span&gt;CycleInstanceName&lt;span style="color:gray;"&gt;, &lt;/span&gt;AD&lt;span style="color:gray;"&gt;.&lt;/span&gt;AssignmentDefName&lt;span style="color:gray;"&gt;, &lt;/span&gt;A&lt;span style="color:gray;"&gt;.&lt;/span&gt;AssignmentName&lt;span style="color:gray;"&gt;,
                    &lt;/span&gt;U&lt;span style="color:gray;"&gt;.&lt;/span&gt;UserId&lt;span style="color:gray;"&gt;, &lt;/span&gt;U&lt;span style="color:gray;"&gt;.&lt;/span&gt;UserName&lt;span style="color:gray;"&gt;, &lt;/span&gt;ENT&lt;span style="color:gray;"&gt;.&lt;/span&gt;Name &lt;span style="color:blue;"&gt;AS &lt;/span&gt;EntityName&lt;span style="color:gray;"&gt;, &lt;/span&gt;CUST&lt;span style="color:gray;"&gt;.&lt;/span&gt;Name &lt;span style="color:blue;"&gt;As &lt;/span&gt;CustomerName&lt;span style="color:gray;"&gt;,
                    &lt;/span&gt;&lt;span style="color:blue;"&gt;CASE
                        WHEN &lt;/span&gt;A&lt;span style="color:gray;"&gt;.&lt;/span&gt;Status &lt;span style="color:gray;"&gt;= &lt;/span&gt;&lt;span style="color:red;"&gt;&amp;#39;partial&amp;#39; &lt;/span&gt;&lt;span style="color:gray;"&gt;OR &lt;/span&gt;A&lt;span style="color:gray;"&gt;.&lt;/span&gt;Status &lt;span style="color:gray;"&gt;= &lt;/span&gt;&lt;span style="color:red;"&gt;&amp;#39;Approved&amp;#39; &lt;/span&gt;&lt;span style="color:gray;"&gt;OR &lt;/span&gt;A&lt;span style="color:gray;"&gt;.&lt;/span&gt;Status &lt;span style="color:gray;"&gt;= &lt;/span&gt;&lt;span style="color:red;"&gt;&amp;#39;Submitted&amp;#39; &lt;/span&gt;&lt;span style="color:blue;"&gt;THEN &lt;/span&gt;1
                        &lt;span style="color:blue;"&gt;ELSE &lt;/span&gt;0
                    &lt;span style="color:blue;"&gt;END AS &lt;/span&gt;Draft_Submitted&lt;span style="color:gray;"&gt;,
                    &lt;/span&gt;&lt;span style="color:blue;"&gt;CASE 
                        WHEN &lt;/span&gt;A&lt;span style="color:gray;"&gt;.&lt;/span&gt;Status &lt;span style="color:gray;"&gt;= &lt;/span&gt;&lt;span style="color:red;"&gt;&amp;#39;Approved&amp;#39; &lt;/span&gt;&lt;span style="color:gray;"&gt;OR &lt;/span&gt;A&lt;span style="color:gray;"&gt;.&lt;/span&gt;Status &lt;span style="color:gray;"&gt;= &lt;/span&gt;&lt;span style="color:red;"&gt;&amp;#39;Submitted&amp;#39; &lt;/span&gt;&lt;span style="color:blue;"&gt;THEN &lt;/span&gt;1
                        &lt;span style="color:blue;"&gt;ELSE &lt;/span&gt;0
                    &lt;span style="color:blue;"&gt;END As &lt;/span&gt;Final_Submitted&lt;span style="color:gray;"&gt;,
                    &lt;/span&gt;&lt;span style="color:blue;"&gt;CASE
                        WHEN &lt;/span&gt;A&lt;span style="color:gray;"&gt;.&lt;/span&gt;Status &lt;span style="color:gray;"&gt;=  &lt;/span&gt;&lt;span style="color:red;"&gt;&amp;#39;Approved&amp;#39; &lt;/span&gt;&lt;span style="color:blue;"&gt;THEN &lt;/span&gt;1
                        &lt;span style="color:blue;"&gt;ELSE &lt;/span&gt;0
                    &lt;span style="color:blue;"&gt;END AS &lt;/span&gt;Approved&lt;span style="color:gray;"&gt;, &lt;/span&gt;Approve&lt;span style="color:gray;"&gt;.&lt;/span&gt;UserName &lt;span style="color:blue;"&gt;As &lt;/span&gt;Approver
&lt;span style="color:blue;"&gt;FROM                &lt;/span&gt;dbo&lt;span style="color:gray;"&gt;.&lt;/span&gt;Assignments A 
&lt;span style="color:gray;"&gt;LEFT OUTER JOIN     &lt;/span&gt;dbo&lt;span style="color:gray;"&gt;.&lt;/span&gt;[MG_Planning_MeasureGroup_default_partition] Fact
                    &lt;span style="color:blue;"&gt;ON &lt;/span&gt;A&lt;span style="color:gray;"&gt;.&lt;/span&gt;AssignmentID &lt;span style="color:gray;"&gt;= &lt;/span&gt;Fact&lt;span style="color:gray;"&gt;.&lt;/span&gt;AssignmentID
&lt;span style="color:gray;"&gt;LEFT OUTER JOIN     &lt;/span&gt;dbo&lt;span style="color:gray;"&gt;.&lt;/span&gt;AssignmentDefinitions AD &lt;span style="color:blue;"&gt;ON &lt;/span&gt;AD&lt;span style="color:gray;"&gt;.&lt;/span&gt;AssignmentDefID &lt;span style="color:gray;"&gt;= &lt;/span&gt;A&lt;span style="color:gray;"&gt;.&lt;/span&gt;AssignmentDefID
&lt;span style="color:gray;"&gt;LEFT OUTER JOIN     &lt;/span&gt;dbo&lt;span style="color:gray;"&gt;.&lt;/span&gt;CycleInstances C &lt;span style="color:blue;"&gt;ON &lt;/span&gt;C&lt;span style="color:gray;"&gt;.&lt;/span&gt;CycleInstanceID &lt;span style="color:gray;"&gt;= &lt;/span&gt;A&lt;span style="color:gray;"&gt;.&lt;/span&gt;CycleInstanceID
&lt;span style="color:gray;"&gt;LEFT OUTER JOIN     &lt;/span&gt;dbo&lt;span style="color:gray;"&gt;.&lt;/span&gt;D_Entity ENT &lt;span style="color:blue;"&gt;ON &lt;/span&gt;ENT&lt;span style="color:gray;"&gt;.&lt;/span&gt;MemberId &lt;span style="color:gray;"&gt;= &lt;/span&gt;Fact&lt;span style="color:gray;"&gt;.&lt;/span&gt;Entity_MemberId
&lt;span style="color:gray;"&gt;LEFT OUTER JOIN     &lt;/span&gt;dbo&lt;span style="color:gray;"&gt;.&lt;/span&gt;D_Customer CUST &lt;span style="color:blue;"&gt;ON &lt;/span&gt;CUST&lt;span style="color:gray;"&gt;.&lt;/span&gt;MemberId &lt;span style="color:gray;"&gt;= &lt;/span&gt;Fact&lt;span style="color:gray;"&gt;.&lt;/span&gt;[Customer_MemberId]
&lt;span style="color:gray;"&gt;LEFT OUTER JOIN     &lt;/span&gt;dbo&lt;span style="color:gray;"&gt;.&lt;/span&gt;BizUsers U &lt;span style="color:blue;"&gt;ON &lt;/span&gt;U&lt;span style="color:gray;"&gt;.&lt;/span&gt;UserID &lt;span style="color:gray;"&gt;= &lt;/span&gt;A&lt;span style="color:gray;"&gt;.&lt;/span&gt;ContributorUserId
&lt;span style="color:gray;"&gt;LEFT OUTER JOIN     &lt;/span&gt;dbo&lt;span style="color:gray;"&gt;.&lt;/span&gt;ApproverList AL &lt;span style="color:blue;"&gt;ON &lt;/span&gt;AL&lt;span style="color:gray;"&gt;.&lt;/span&gt;AssignmentID &lt;span style="color:gray;"&gt;= &lt;/span&gt;A&lt;span style="color:gray;"&gt;.&lt;/span&gt;AssignmentId
&lt;span style="color:gray;"&gt;LEFT OUTER JOIN     &lt;/span&gt;dbo&lt;span style="color:gray;"&gt;.&lt;/span&gt;BizUsers Approve &lt;span style="color:blue;"&gt;ON &lt;/span&gt;Approve&lt;span style="color:gray;"&gt;.&lt;/span&gt;UserID &lt;span style="color:gray;"&gt;= &lt;/span&gt;AL&lt;span style="color:gray;"&gt;.&lt;/span&gt;ApproverUserID&lt;/pre&gt;
&lt;a href="http://11011.net/software/vspaste"&gt;&lt;/a&gt;&lt;a href="http://11011.net/software/vspaste"&gt;&lt;/a&gt;&lt;a href="http://11011.net/software/vspaste"&gt;&lt;/a&gt;&lt;a href="http://11011.net/software/vspaste"&gt;&lt;/a&gt;&lt;a href="http://11011.net/software/vspaste"&gt;&lt;/a&gt;

&lt;p&gt;You can figure out most of the tables to use by looking at a view called AssignmentsView within the application database.&lt;/p&gt;

&lt;p align="left"&gt;One thing that I have taken into account is assignment definitions. If you have large number of users completing an assignment, then the chances are that you will have set up an assignment definition that points at a business role or a submission hierarchy. You ideally want to be able to filter on the assignment definition to return all assignment instances that belong to that assignment definition. Therefore, in my case I have three filters for the report, but you could easily add more:&lt;/p&gt;

&lt;p align="center"&gt;&lt;a href="http://blogs.adatis.co.uk/blogs/jeremykashel/WindowsLiveWriter/TrackingPerformancePointPlanningSubmissi_108A4/image_4.png"&gt;&lt;img style="border-top-width:0px;border-left-width:0px;border-bottom-width:0px;border-right-width:0px;" height="62" alt="image" src="http://blogs.adatis.co.uk/blogs/jeremykashel/WindowsLiveWriter/TrackingPerformancePointPlanningSubmissi_108A4/image_thumb_1.png" width="683" border="0" /&gt;&lt;/a&gt; &lt;/p&gt;

&lt;p align="left"&gt;The final view is a report that shows the status of the assignments returned by the filter, but also, when expanded, shows the the entities and customers that the contributor has submitted:&lt;/p&gt;

&lt;p align="center"&gt;&lt;a href="http://blogs.adatis.co.uk/blogs/jeremykashel/WindowsLiveWriter/TrackingPerformancePointPlanningSubmissi_108A4/image_9.png" target="_blank"&gt;&lt;img style="border-top-width:0px;border-left-width:0px;border-bottom-width:0px;border-right-width:0px;" height="349" alt="SSRS Report" src="http://blogs.adatis.co.uk/blogs/jeremykashel/WindowsLiveWriter/TrackingPerformancePointPlanningSubmissi_108A4/image_thumb_3.png" width="719" border="0" /&gt;&lt;/a&gt; &lt;/p&gt;

&lt;p align="left"&gt;The above is just a taster of what can be achieved. A couple of ways that it can be extended include:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Integrating with &lt;a href="http://blogs.adatis.co.uk/blogs/sachatomey/archive/2008/06/30/performancepoint-planning-data-change-tracking-audit-trail.aspx"&gt;Sacha&amp;#39;s data auditing idea&lt;/a&gt; to provide detailed history on what values the contributor has changed; &lt;/li&gt;

  &lt;li&gt;Including comments, annotations and deadlines. &lt;/li&gt;
&lt;/ul&gt;

&lt;div class="wlWriterSmartContent" id="scid:0767317B-992E-4b12-91E0-4F059A8CECA8:6faa9f21-3a0a-492d-8182-5bee5fab2b0f" style="padding-right:0px;display:inline;padding-left:0px;padding-bottom:0px;margin:0px;padding-top:0px;"&gt;Technorati Tags: &lt;a href="http://technorati.com/tags/PerformancePoint" rel="tag"&gt;PerformancePoint&lt;/a&gt;,&lt;a href="http://technorati.com/tags/Planning" rel="tag"&gt;Planning&lt;/a&gt;,&lt;a href="http://technorati.com/tags/PPS" rel="tag"&gt;PPS&lt;/a&gt;,&lt;a href="http://technorati.com/tags/SSRS" rel="tag"&gt;SSRS&lt;/a&gt;,&lt;a href="http://technorati.com/tags/Reporting%20Services" rel="tag"&gt;Reporting Services&lt;/a&gt;,&lt;a href="http://technorati.com/tags/SQL%20Server%202005" rel="tag"&gt;SQL Server 2005&lt;/a&gt;&lt;/div&gt;&lt;img src="http://blogs.adatis.co.uk/aggbug.aspx?PostID=7411" width="1" height="1"&gt;</description><category domain="http://blogs.adatis.co.uk/blogs/jeremykashel/archive/tags/PerformancePoint/default.aspx">PerformancePoint</category><category domain="http://blogs.adatis.co.uk/blogs/jeremykashel/archive/tags/SQL/default.aspx">SQL</category><category domain="http://blogs.adatis.co.uk/blogs/jeremykashel/archive/tags/Reporting+Services/default.aspx">Reporting Services</category></item><item><title>Using NativeSQL Business Rules for Seeding or What Ifs</title><link>http://blogs.adatis.co.uk/blogs/jeremykashel/archive/2008/08/05/using-nativesql-business-rules-for-seeding-or-what-ifs.aspx</link><pubDate>Tue, 05 Aug 2008 11:45:00 GMT</pubDate><guid isPermaLink="false">8d7d37f8-4a66-4c95-9fba-293fa87607dc:7382</guid><dc:creator>Jeremy Kashel</dc:creator><slash:comments>4</slash:comments><wfw:commentRss xmlns:wfw="http://wellformedweb.org/CommentAPI/">http://blogs.adatis.co.uk/blogs/jeremykashel/rsscomments.aspx?PostID=7382</wfw:commentRss><comments>http://blogs.adatis.co.uk/blogs/jeremykashel/archive/2008/08/05/using-nativesql-business-rules-for-seeding-or-what-ifs.aspx#comments</comments><description>&lt;p&gt;You need to give really careful consideration to procedural business rules in PerformancePoint Planning. This is where multi-dimensional knowledge and experience helps, as you need to know whether its best to use a SQL or MDX implementation for your rules.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;User Requirements&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;One area where I&amp;#39;ve found that this is particularly relevant is user requests for procedural rules that accept parameters that are member(s) of a dimension. E.g. Lets run the rule for the selected member in the entity dimension, perhaps to carry out a what if on a particular entity. This is fine for a single member, but on occasion I&amp;#39;ve found that the users want to pick either a)a leaf level member (which is fine) or b)a non-leaf member, and for the rule to apply the logic to all descendants of that member.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The Technical Problem&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;What technical problem? You can use the descendants function to achieve this right? You can with an MDX rule, but you can&amp;#39;t with a SQL rule. So, surely just use an MDX rule then?&lt;/p&gt;
&lt;p&gt;This is where you have to think relational world Vs OLAP world. Putting a statement such as &lt;span style="COLOR:black;"&gt;Descendants(&lt;/span&gt;&lt;span style="COLOR:teal;"&gt;$Entity$&lt;/span&gt;&lt;span style="COLOR:black;"&gt;, 10, &lt;/span&gt;&lt;span style="COLOR:blue;"&gt;leaves&lt;/span&gt;&lt;span style="COLOR:black;"&gt;)&lt;/span&gt; in the scope of your rule will mean that when the rule is run, then it will always operate on the leaves of the dimension, regardless of whether you pick a leaf or non-leaf member. But the issue is that if your scope statement covers cells that have definition rules against them, then an MDX query will pick up the results of of those cells and and shove them into the fact table!&lt;/p&gt;
&lt;p&gt;So can you use the Except() function or something similar to filter out those cells that you don&amp;#39;t need? Yes you can, but not if the members start to span different levels, the compiler won&amp;#39;t let you do that. As I said, you can&amp;#39;t use a SQL implementation because using &lt;span style="COLOR:black;"&gt;Descendants(&lt;/span&gt;&lt;span style="COLOR:teal;"&gt;$Entity$&lt;/span&gt;&lt;span style="COLOR:black;"&gt;, 10, &lt;/span&gt;&lt;span style="COLOR:blue;"&gt;leaves&lt;/span&gt;&lt;span style="COLOR:black;"&gt;)&lt;/span&gt; in the scope statement will tell you - &amp;quot;A SQL implementation is forbidden by a parameter or variable reference in a MemberSet expression unless the reference is the entire expression.&amp;quot;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The Solution&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Rules that operate on fact data should really use SQL, its quicker and avoids hitting calculated data. I&amp;#39;m sure that you&amp;#39;ve seen this coming a mile off (!), but one way to go about this is to use a NativeSQL rule.&lt;/p&gt;
&lt;p&gt;You can reference database objects in your NativeSQL rule, one of which will make it easier for you carry out descendant/hierarchy type functions in SQL itself. A function will get created in the Calc schema of your Planning application database in the format fnCalc_0_DimensionName_MemberSetName_IsAncestorOrSelf - this will occur when you deploy your model site. Using this function in conjunction with NativeSQL will allow you to get the leaves of a member quite easily.&lt;/p&gt;
&lt;p&gt;The following NativeSQL rule creates a copy of one scenario&amp;#39;s data into another scenario, which is referenced by a parameter:&lt;/p&gt;&lt;pre class="code"&gt;&lt;span style="COLOR:green;"&gt;--Although integers, the parameters are actually &amp;#39;members of a dimension&amp;#39; parameters
&lt;/span&gt;&lt;span style="COLOR:blue;"&gt;create proc &lt;/span&gt;Calc&lt;span style="COLOR:gray;"&gt;.&lt;/span&gt;[sp$procedure] @scenario &lt;span style="COLOR:blue;"&gt;int&lt;/span&gt;&lt;span style="COLOR:gray;"&gt;, &lt;/span&gt;@entity &lt;span style="COLOR:blue;"&gt;int as
begin
begin transaction

    insert into &lt;/span&gt;dbo&lt;span style="COLOR:gray;"&gt;.&lt;/span&gt;[tbl$factTable] &lt;span style="COLOR:gray;"&gt;(&lt;/span&gt;alldim$except&lt;span style="COLOR:gray;"&gt;(&lt;/span&gt;dim$Entity&lt;span style="COLOR:gray;"&gt;, &lt;/span&gt;dim$Scenario&lt;span style="COLOR:gray;"&gt;), &lt;/span&gt;dim$Entity&lt;span style="COLOR:gray;"&gt;, &lt;/span&gt;dim$Scenario&lt;span style="COLOR:gray;"&gt;,
    &lt;/span&gt;&lt;span style="COLOR:blue;"&gt;column&lt;/span&gt;$ruleId&lt;span style="COLOR:gray;"&gt;, &lt;/span&gt;&lt;span style="COLOR:blue;"&gt;Value&lt;/span&gt;&lt;span style="COLOR:gray;"&gt;, &lt;/span&gt;LoadingControlID&lt;span style="COLOR:gray;"&gt;, &lt;/span&gt;bmo$CreateDateTime&lt;span style="COLOR:gray;"&gt;, &lt;/span&gt;bmo$ChangeDateTime&lt;span style="COLOR:gray;"&gt;, &lt;/span&gt;ContextID&lt;span style="COLOR:gray;"&gt;)
    &lt;/span&gt;&lt;span style="COLOR:blue;"&gt;select &lt;/span&gt;alldim$except&lt;span style="COLOR:gray;"&gt;(&lt;/span&gt;dim$Entity&lt;span style="COLOR:gray;"&gt;, &lt;/span&gt;dim$Scenario&lt;span style="COLOR:gray;"&gt;), &lt;/span&gt;dim$Entity&lt;span style="COLOR:gray;"&gt;, &lt;/span&gt;@scenario&lt;span style="COLOR:gray;"&gt;, &lt;/span&gt;$ruleId&lt;span style="COLOR:gray;"&gt;, &lt;/span&gt;&lt;span style="COLOR:blue;"&gt;Value&lt;/span&gt;&lt;span style="COLOR:gray;"&gt;, -&lt;/span&gt;9&lt;span style="COLOR:gray;"&gt;, &lt;/span&gt;&lt;span style="COLOR:magenta;"&gt;getutcdate&lt;/span&gt;&lt;span style="COLOR:gray;"&gt;(),
    &lt;/span&gt;&lt;span style="COLOR:magenta;"&gt;getutcdate&lt;/span&gt;&lt;span style="COLOR:gray;"&gt;(), -&lt;/span&gt;1
    &lt;span style="COLOR:green;"&gt;--No need to hard code the fact table name
    &lt;/span&gt;&lt;span style="COLOR:blue;"&gt;from &lt;/span&gt;dbo&lt;span style="COLOR:gray;"&gt;.&lt;/span&gt;[tbl$factTable] t
    &lt;span style="COLOR:gray;"&gt;inner join 
    (
        &lt;/span&gt;&lt;span style="COLOR:blue;"&gt;select &lt;/span&gt;e&lt;span style="COLOR:gray;"&gt;.&lt;/span&gt;memberId &lt;span style="COLOR:blue;"&gt;from &lt;/span&gt;dbo&lt;span style="COLOR:gray;"&gt;.&lt;/span&gt;d_Entity e
        &lt;span style="COLOR:green;"&gt;--Get leaves of the member key that is in @entity
        &lt;/span&gt;&lt;span style="COLOR:blue;"&gt;where &lt;/span&gt;Calc&lt;span style="COLOR:gray;"&gt;.&lt;/span&gt;fnCalc_0_Entity_Divisions_IsAncestorOrSelf&lt;span style="COLOR:gray;"&gt;(&lt;/span&gt;@entity&lt;span style="COLOR:gray;"&gt;, &lt;/span&gt;e&lt;span style="COLOR:gray;"&gt;.&lt;/span&gt;memberid&lt;span style="COLOR:gray;"&gt;) = &lt;/span&gt;1 &lt;span style="COLOR:gray;"&gt;and 
        &lt;/span&gt;e&lt;span style="COLOR:gray;"&gt;.&lt;/span&gt;memberid &lt;span style="COLOR:gray;"&gt;not in (&lt;/span&gt;&lt;span style="COLOR:blue;"&gt;select &lt;/span&gt;h&lt;span style="COLOR:gray;"&gt;.&lt;/span&gt;parentmemberid &lt;span style="COLOR:blue;"&gt;from &lt;/span&gt;dbo&lt;span style="COLOR:gray;"&gt;.&lt;/span&gt;H_Entity_Divisions h&lt;span style="COLOR:gray;"&gt;)
    )    &lt;/span&gt;lv &lt;span style="COLOR:blue;"&gt;on &lt;/span&gt;lv&lt;span style="COLOR:gray;"&gt;.&lt;/span&gt;memberid &lt;span style="COLOR:gray;"&gt;= &lt;/span&gt;dim$Entity
    &lt;span style="COLOR:blue;"&gt;where &lt;/span&gt;t&lt;span style="COLOR:gray;"&gt;.&lt;/span&gt;dim$Time &lt;span style="COLOR:gray;"&gt;&amp;gt;= &lt;/span&gt;200701 &lt;span style="COLOR:gray;"&gt;and &lt;/span&gt;t&lt;span style="COLOR:gray;"&gt;.&lt;/span&gt;dim$Time &lt;span style="COLOR:gray;"&gt;&amp;lt;= &lt;/span&gt;200712 &lt;span style="COLOR:gray;"&gt;and (&lt;/span&gt;dim$Scenario &lt;span style="COLOR:gray;"&gt;= &lt;/span&gt;1&lt;span style="COLOR:gray;"&gt;)

&lt;/span&gt;&lt;span style="COLOR:blue;"&gt;if &lt;/span&gt;&lt;span style="COLOR:magenta;"&gt;@@error &lt;/span&gt;&lt;span style="COLOR:gray;"&gt;&amp;lt;&amp;gt; &lt;/span&gt;0
&lt;span style="COLOR:blue;"&gt;rollback transaction
else
commit transaction

end&lt;/span&gt;&lt;/pre&gt;&lt;a href="http://11011.net/software/vspaste"&gt;&lt;/a&gt;&lt;a href="http://11011.net/software/vspaste"&gt;&lt;/a&gt;&lt;a href="http://11011.net/software/vspaste"&gt;&lt;/a&gt;&lt;a href="http://11011.net/software/vspaste"&gt;&lt;/a&gt;&lt;a href="http://11011.net/software/vspaste"&gt;&lt;/a&gt;
&lt;p&gt;I suppose the above rule may look a little bit daunting, due to all the dollar signs everywhere. These are just the &lt;a href="http://msdn.microsoft.com/en-us/library/bb839341.aspx" target="_blank"&gt;preprocessor commands&lt;/a&gt;, which will get replaced when the stored procedure gets created. But other than that, its just an insert statement. In some cases the syntax is actually quite handy, as you can use statements such as alldim$except(), which is a short hand way of returning fact table dimension column names very easily. The idea behind the preprocessor commands is that you can protect the logic in your rule from minor structural database changes.&lt;/p&gt;
&lt;p align="left"&gt;The key is that when the rule gets run, it has normal parameters, meaning the user will see the following interface:&lt;/p&gt;
&lt;p align="center"&gt;&lt;a href="http://blogs.adatis.co.uk/blogs/jeremykashel/WindowsLiveWriter/UsingNativeSQLBusinessRulesforSeedingorW_10AD1/image_2.png"&gt;&lt;img style="BORDER-TOP-WIDTH:0px;BORDER-LEFT-WIDTH:0px;BORDER-BOTTOM-WIDTH:0px;BORDER-RIGHT-WIDTH:0px;" height="320" alt="image" src="http://blogs.adatis.co.uk/blogs/jeremykashel/WindowsLiveWriter/UsingNativeSQLBusinessRulesforSeedingorW_10AD1/image_thumb.png" width="234" border="0" /&gt;&lt;/a&gt;&amp;nbsp;&lt;/p&gt;
&lt;p align="left"&gt;In the case above, the member key of North America Operations will end up in the stored procedure, as I have a parameter in the stored procedure called @entity. Inside the stored procedure it is then used in combination with the IsAncestorOrSelf function to pick up the leaf level descendants.&lt;/p&gt;
&lt;p&gt;The only caveat that I will make is that this hasn&amp;#39;t come from a production environment, so I&amp;#39;ve skimped a bit on a few of the operational details such as deleting the existing data, decent error handling and returning the number of records affected. These are all things that you should add in if you use this kind of thing in a production environment!&lt;/p&gt;
&lt;div class="wlWriterSmartContent" id="scid:0767317B-992E-4b12-91E0-4F059A8CECA8:1d4c9b0a-6844-4817-8d53-92dc2d0b86e0" style="PADDING-RIGHT:0px;DISPLAY:inline;PADDING-LEFT:0px;PADDING-BOTTOM:0px;MARGIN:0px;PADDING-TOP:0px;"&gt;Technorati Tags: &lt;a href="http://technorati.com/tags/PerformancePoint" rel="tag"&gt;PerformancePoint&lt;/a&gt;,&lt;a href="http://technorati.com/tags/SQL" rel="tag"&gt;SQL&lt;/a&gt;,&lt;a href="http://technorati.com/tags/NativeSQL" rel="tag"&gt;NativeSQL&lt;/a&gt;,&lt;a href="http://technorati.com/tags/Business%20Rules" rel="tag"&gt;Business Rules&lt;/a&gt;&lt;/div&gt;&lt;img src="http://blogs.adatis.co.uk/aggbug.aspx?PostID=7382" width="1" height="1"&gt;</description><category domain="http://blogs.adatis.co.uk/blogs/jeremykashel/archive/tags/PerformancePoint/default.aspx">PerformancePoint</category><category domain="http://blogs.adatis.co.uk/blogs/jeremykashel/archive/tags/SQL/default.aspx">SQL</category></item></channel></rss>