Quantcast
Channel: tim laqua dot com » analysis services
Viewing all articles
Browse latest Browse all 9

Analysis Services Writeback – Working with Weight Expressions

$
0
0

While writeback has been around for a while, it didn't really get easy to do until Excel 2010 was released because wrapping a UI around it was cumbersome. Now that we have a simple writeback UI via the What-If analysis dialogs in Excel, it's pry worth poking around at how to get things to allocate the way you want.

Here's our starting point in the Adventure Works cube (note, I commented out all the scope statements in the MDX script that distribute quarterly quotas/targets to months - in AW, quotas only exist in the data warehouse at the calendar quarter level):

Let's start with something simple - Equal Allocation:


Now let's try to figure out this Weight Expression thing by throwing a 1 in there:

Now discard previous changes and enter the quota at the Semester level again:

Ok, that wasn't what we intended - but it does kind of explain what it's up to. That's the number of detail cells (leaves) that it wrote data to - and it wrote the entire quota amount to each cell, then added them all up. Note that the first leaf got the true-up balance - if your custom aggregation doesn't roll up properly, it seems that it figures out how much you were off and tosses whatever number is required to make it aggregate right in to the first leaf.

Now to get the right weight expression, we have to divide 1 (the entire quota) by the number of leaves that it's writing data to. Sales quotas are related to the Date dimension, the Sales Territory dimension, and the Employee dimension, so it's going to write out one value for every intersection of the leaves of those dimensions (at whichever level the measure group is related to the dimension, so it doesn't write out a value for every date, it writes out a value for every quarter - because that's the level that quotas are associated with the date dimension, calendar quarter). So we basically want:

1 / [number of employees] / [number of sales regions] / [number of quarters]

Here's the weight expression to accomplish that (note that we assume that we know what we're trying to do here: enter quotas at the fiscal semester level. That's why the following expression gets the Semester ancestor of the current fiscal calendar member, because we know that we want to distribute evenly across all quarters and we entered data at the semester level)

1
/Count(
	Descendants(
		 [Employee].[Employee].[All]
		,[Employee].[Employee].[Employee]
	)
 )
/Count(
	Descendants(
		 [Sales Territory].[Sales Territory Region].[All]
		,[Sales Territory].[Sales Territory Region].[Sales Territory Region]
	)
 )
/Count(
	Descendants(
		 Ancestor(
			 [Date].[Fiscal].CurrentMember
			,[Date].[Fiscal].[Fiscal Semester]
		 )
		,[Date].[Fiscal].[Fiscal Quarter]
	)
)

And, of course, we end up with our old friend Equal Allocation:

Now say you wanted to enter quotas at the intersection of the Fiscal Year and the Country. Now we don't want all the sales regions anymore, we just want the Descendants of the current sales region's Ancestor at the Country level:

We start here:

And enter the following Weight Expression:

1
/Count(
	Descendants(
		 [Employee].[Employee].[All]
		,[Employee].[Employee].[Employee]
	)
 )
/Count(
	Descendants(
		 Ancestor(
			 [Sales Territory].[Sales Territory].CurrentMember
			,[Sales Territory].[Sales Territory].[Country]
		 ) 
		,[Sales Territory].[Sales Territory].[Region]
	)
 )
/Count(
	Descendants(
		 Ancestor(
			 [Date].[Fiscal].CurrentMember
			,[Date].[Fiscal].[Fiscal Year]
		 )
		,[Date].[Fiscal].[Fiscal Quarter]
	)
)

And then we enter the quota at the intersection of Fiscal Semester and Country:

So all we've done this far is reproduce numbers similar to what Equal Allocation would get us. But what did we just do really? We distributed the sales quotas across all employees - but only Sales People have sales quotas... oops (the following is sliced by [Employee].[Sales Person Flag] on columns):

And now we finally get to why we want to use Weight Expressions in the first place. We only want to distribute quotas across sales people (note that we've went back to just distributing evenly across all sales regions again for simplicity):

iif([Employee].[Employee].CurrentMember.Properties("Sales Person Flag") = "True"
,
	1
	/Count(
		Descendants(
			 [Employee].[Employee].[All]
			,[Employee].[Employee].[Employee]
		) * [Employee].[Sales Person Flag].&[True]
	 )
	/Count(
		Descendants(
			 [Sales Territory].[Sales Territory Region].[All]
			,[Sales Territory].[Sales Territory Region].[Sales Territory Region]
		)
	 )
	/Count(
		Descendants(
			 Ancestor(
				 [Date].[Fiscal].CurrentMember
				,[Date].[Fiscal].[Fiscal Semester]
			 )
			,[Date].[Fiscal].[Fiscal Quarter]
		)
	)
,Null)

And the results:

Great, we got that particular slice all fixed up - but what about if we slice by employee? Did we distribute sales quotas for the US across sales people that don't even work in the US? What-If analysis is just that - "what happens if I do this?" If you're never going to look at more detailed slices, it's fine the way it is. If you're using writeback to publish data to the cube and let others slice on it all willy-nilly, you need to examine all the slices you just wrote data to and make sure your weight expression allocates the data the way you intended it to.

In the end, do we expect business users to be able to write their own weight expressions, publish to the masses, and understand what exactly just happened? I doubt it. That's a bit much to ask. We can, however, create accurate weight expressions for business users if we know exactly where they want to enter the new data.


Viewing all articles
Browse latest Browse all 9

Trending Articles