Wednesday, July 25, 2007

Data Member, Part 1

By Bryan C. Smith

Take a moment to think about a typical organization.  You have an employee.  That employee has a supervisor who is also an employee who has a supervisor (who is also an employee) and so on and so on.   (This is a classic parent-child hierarchy.)

If these employees are in a sales organization, sales quota may have been assigned.  Those employees that are supervisors are responsible for the sales quotas of the employees below them but may also have sales quotas of their own.

Let's say you have an employee in a sales organization named Amy.  She supervises three other employees, Jae, Rachel, and Ranjit.  Amy is responsible for making sure these guys make their sales quotas of $13M, $3M, and $7M, respectively.  Amy also has a personal sales target of $1M.  We can breakdown Amy's sales quota like this:

  • Amy $24M Total
    • Amy (personal) $1M
    • Jae $13M
    • Rachel $3M
    • Ranjit $7M


 

The $24M figure represents the total sales for which Amy has responsibility while she is only directly responsible for $1M of that.  The remaining $23M represents the sales quotas she manages through her supervisees.

Let's now look at how SSAS 2005 handles all this. Open an MDX query window in SQL Server Management Studio, connect to the Adventure Works DW database, and execute the following query:

select

     [Measures].[Sales Amount Quota] on 0,

     DESCENDANTS([Employee].[Employees].[Amy E. Alberts], 1, SELF_AND_BEFORE) on 1

from [Adventure Works];

 

Sales Amount Quota

Amy E. Alberts

$24,202,000.00

Jae B. Pak

$12,547,100.00

Rachel B. Valdez

$3,269,800.00

Ranjit R. Varkey Chudukatil

$7,260,700.00


 

Through the DESCENDANTS
function, you asked SSAS to return the Sales Amount Quota for those employees one level below Amy E. Alberts and Amy herself. If you total the values for Jae, Rachel, and Ranjit, we come up with $23,077,600.00.  That's $1,124,400.00 short of the total. 

From these results, you can deduce the approximately $1M deficit represents Amy's personal quota.  But how do you get SSAS to explicitly present this?  You do it with the DataMember function:

with
member [Measures].[Personal Quota] as

     ([Employee].[Employees].DataMember, [Measures].[Sales Amount Quota]),

     format="Currency"

select

     {[Measures].[Sales Amount Quota],[Measures].[Personal Quota]} on 0,

     DESCENDANTS([Employee].[Employees].[Amy E. Alberts],1,SELF_AND_BEFORE) on 1

from [Adventure Works];

 

Sales Amount Quota

Personal Quota

Amy E. Alberts

$24,202,000.00

$1,124,400.00

Jae B. Pak

$12,547,100.00

$12,547,100.00

Rachel B. Valdez

$3,269,800.00

$3,269,800.00

Ranjit R. Varkey Chudukatil

$7,260,700.00

$7,260,700.00


 

Your results now show definitively that Amy owns a $1M personal sales quota which contributes to the $24M overall quota she is responsible for delivering.  You also see that Jae, Rachel, and Ranjit, Amy's supervisees, are solely responsible for their own quotas.  You cannot say Jae, Rachel, and Ranjit have no supervisees of their own (without submitting additional queries), but you can say none of these three guys is managing quota values from any supervisees they may have.

So, when you request a value for a member in a parent-child hierarchy, such as the Employees hierarchy of the Employee dimension, you need to be aware SSAS combines those values directly tied to that member with values associated with that member's children. You need to use the DataMember function to get at the value directly tied to a member.

It needs to be pointed out that you can tell SSAS to display a member's directly attributable data a little differently when that member also has children with values. In my next blog entry, we'll revisit Amy's sales team and take a look at those settings and how they impact these results.

IgnoreUnrelatedDimensions

By Bryan C. Smith

Imagine you have a highly simplified Adventure Works cube. In this cube, you have two measure groups, Reseller Sales and Internet Sales. Reseller Sales has relationships with the Geography and Date dimensions and has one measure, Reseller Sales Amount. Internet Sales has a relationship with just the Date dimension and a single measure, Internet Sales Amount. To simplify things even further, imagine the Geography dimension has a single attribute hierarchy, Country, and Date has three attribute hierarchies, Calendar Year, Calendar Quarter, and Date.

Every reference to data within this cube is based on a coordinate system. Each attribute hierarchy in the cube represents an axis in the coordinate. For our purposes, let's say the coordinates are in the format of ([Geography].[Country], [Date].[Calendar Year], [Date].[Calendar Quarter], [Date].[Date]). (For the sake of brevity, I've removed the attribute hierarchy from the Measures dimension.) Our MDX SELECT statements then assemble coordinates which SSAS resolve to return a cellset:


select


[Measures].[Reseller Sales Amount] on 0,


Geography.Country.Members
on 1


from [Adventure Works]


where ([Date].[Calendar Year].[CY 2003])

;

Reseller Sales Amount

Coordinates

All Geographies

$32,202,669.43

?

('All Geographies', 'CY 2003', 'All Times', 'All Times')

Australia

$847,430.96

?

('Australia', 'CY 2003', 'All Times', 'All Times')

Canada

$5,651,305.43

?

('Canada', 'CY 2003', 'All Times', 'All Times')

France

$2,373,804.04

?

('France', 'CY 2003', 'All Times', 'All Times')

Germany

$1,098,866.68

?

('Germany', 'CY 2003', 'All Times', 'All Times')

United Kingdom

$2,160,145.83

?

('United Kingdom', 'CY 2003', 'All Times', 'All Times')

United States

$20,071,116.48

?

('United States', 'CY 2003', 'All Times', 'All Times')


As was mentioned at the top of this entry, the Internet Sales Amount measure group does not have a relationship with the Geography dimension. Still, the cube's coordinate structure, ([Geography].[Country], [Date].[Calendar Year], [Date].[Calendar Quarter], [Date].[Date]), must be employed. With the measure group's IgnoreUnrelatedDimensions property set to True, which is the default setting for this property, the All member is substituted in place of any specified members from unrelated dimensions:

select

[Measures].[Internet Sales Amount] on 0,

Geography.Country.Members
on 1

from [Adventure Works]

where ([Date].[Calendar Year].[CY 2003])

;

Internet Sales Amount

Coordinates

All Geographies

$9,791,060.30

?

('All Geographies', 'CY 2003', , 'All Times', 'All Times')

Australia

$9,791,060.30

?

('All Geographies', 'CY 2003', , 'All Times', 'All Times')

Canada

$9,791,060.30

?

('All Geographies', 'CY 2003', , 'All Times', 'All Times')

France

$9,791,060.30

?

('All Geographies', 'CY 2003', , 'All Times', 'All Times')

Germany

$9,791,060.30

?

('All Geographies', 'CY 2003', , 'All Times', 'All Times')

United Kingdom

$9,791,060.30

?

('All Geographies', 'CY 2003', , 'All Times', 'All Times')

United States

$9,791,060.30

?

('All Geographies', 'CY 2003', , 'All Times', 'All Times')


When the IgnoreUnrelatedDimensions property is set to False, SSAS does not perform any substitutions:

select

[Measures].[Internet Sales Amount] on 0,

Geography.Country.Members
on 1

from [Adventure Works]

where ([Date].[Calendar Year].[CY 2003])

;

Internet Sales Amount

Coordinates

All Geographies

$9,791,060.30

?

('All Geographies', 'CY 2003', , 'All Times', 'All Times')

Australia

(null)

?

('Australia', 'CY 2003', , 'All Times', 'All Times')

Canada

(null)

?

('Canada', 'CY 2003', , 'All Times', 'All Times')

France

(null)

?

('France', 'CY 2003', , 'All Times', 'All Times')

Germany

(null)

?

('Germany', 'CY 2003', , 'All Times', 'All Times')

United Kingdom

(null)

?

('United Kingdom', 'CY 2003', , 'All Times', 'All Times')

United States

(null)

?

('United States', 'CY 2003', , 'All Times', 'All Times')


So, what about the situation where a measure group is only partially related to the attribute hierarchies within a dimension? Let's expand this simple cube to include a Sales Targets measure group. The Sales Targets measure group has a single measure, Sales Amount Quota, and has a relationship to just the Date dimension. Unlike the other measure groups which are related to the Date dimension at the Date attribute level, the Sales Targets measure group is related at the Calendar Quarter attribute level.

With IgnoreUnrelatedDimensions set to False on the Sales Targets measure group, we see the following behavior. Note, the number of rows has been limited with the HEAD function for display purposes only:

select

[Measures].[Sales Amount Quota] on 0,

HEAD([Date].[Date].Members,10) on 1

from [Adventure Works]

where ([Date].[Calendar Year].[CY 2003])

;

Sales Amount Quota

All Periods

$38,782,000.00

January 1, 2003

(null)

January 2, 2003

(null)

January 3, 2003

(null)

January 4, 2003

(null)

January 5, 2003

(null)

January 6, 2003

(null)

January 7, 2003

(null)

January 8, 2003

(null)

January 9, 2003

(null)


Executing the same query with IngoreUnrelatedDimensions set to True, returns the following:

Sales Amount Quota

All Periods

$38,782,000.00

January 1, 2003

$38,782,000.00

January 2, 2003

$38,782,000.00

January 3, 2003

$38,782,000.00

January 4, 2003

$38,782,000.00

January 5, 2003

$38,782,000.00

January 6, 2003

$38,782,000.00

January 7, 2003

$38,782,000.00

January 8, 2003

$38,782,000.00

January 9, 2003

$38,782,000.00


Retrieving data at the Calendar Quarter level at which the relationship exists returns the following:

select

[Measures].[Sales Amount Quota] on 0,

HEAD([Date].[Calendar Quarter].Members,10) on 1

from [Adventure Works]

where ([Date].[Calendar Year].[CY 2003])

;

Sales Amount Quota

All Periods

$38,782,000.00

Q1 CY 2003

$5,913,000.00

Q2 CY 2003

$8,039,000.00

Q3 CY 2003

$13,733,000.00

Q4 CY 2003

$11,097,000.00


What we can take from this is that when IgnoreUnrelatedDimensions is set to True, axis members associated with specific attribute hierarchies with which the measure group does not have a relationship are converted to the All member.

Monday, July 23, 2007

Greetings!

We have had a blog at SQL Junkies for a few years, but have not posted for a long time. With Word 2007's new post-to-blog feature, we decided to move to a new blog space. Blogger is pretty simple, so we're moving here.

We've accumulated a number of topics we want to blog about, and hopefully this will make it easier for us to post more actively. If we can figure out an easy way to do it, we may re-post some of our favorite entries from the old SQL Junkies blog.

Watch for more soon!

The Hitachi Consulting Microsoft BI Blog Team

Wednesday, July 4, 2007

The 'Currency' number format style is Evil [Reed]


I always used to use Currency formatting for currency values, but it's totally wrong in a database environment. Currency displays using the current locale's currency setting. But the values in your database have a pre-known, specific currency. If the values in your database are in Euros, then you don't want somebody from California to see them showing up as dollars. The origin of the Currency style was to simplify life for people using tools like Excel to input numbers. But when used with a database, they can cause rediculous restatements. Once I thought about it, Currency format is so rediculous that I can't believe I used to use it all the time. Hence the blog entry.

Even when doing currency conversions--you want to be in control of which currency format the values take. You NEVER want to be at the mercy of the current user's control panel whims.

Cheers. Happy 4th of July to all us Americans. [Reed]

Monday, November 6, 2006

More info on RS and AS [Reed]


A couple more ways the RS MDX Builder is smarter than I thought. I always wondered about some of the complexity.

- When you use a parameter, there’s no way around using StrToSet to turn the parameter into an MDX set. That leaves an opening for inserting malicious code. The auto-generated MDX adds the ,CONSTRAINED flag to StrToSet, which says that the only acceptable contents of the string are literal MDX names, so it won’t run any MDX functions (such as .Children), and it won’t run any inserted code.
- In AS2005, anytime you use a subcube subquery to filter down to a single date, there’s a problem with date-relative calculations because the “current member” in the report is still the “All” member of the date hierarchy. This is a serious problem right now with Excel 2007 PivotTables and MDX. The RS MDX builder adds a funny-looking WHERE clause that basically says, if you single-selected a date for the parameter, make it the current member so that date-relative calculations will work properly.”

Reed

Friday, October 13, 2006

Combining Dimensions [Reed]


The way AS2005 manages attributes in a dimension changes the design dynamic a bit: Which attributes should be combined into a single dimension? I've run into this issue from two sides.

On the one hand, we had a Project dimension with a lot of changing attributes. For example, the Project Manager would change frequently. To avoid dealing with creating lots of Type II instances of the Project, we decided to pull the Project Manager off as a separate key in the fact, making it a "Rapidly Changing Dimension". In 2000, this was often a very effective strategy. It's simple and easy to implement, but in 2005, we found that query performance was very bad. Even thougy the PM to Project relationship is very sparse, each crosstab query had to do a cartesian product and find the empty intersections. When we moved the PM back into the Project dimension, AutoExist suddenly made it really fast to find the projects for a given manager, or the managers for a given project.

Conversely, I had a client with lots of customers and lots of products, but where the relationship between customers and products was very stable and very sparse. It seemed bizarre, but we explored putting both customers and products into a single dimension, with the root key as the Customer/SKU combination. Again, the query performance gain was substantial, especially when looking for which customers buy which products, etc. In 2000, I would never have considered putting Customer and Product into a single dimension, but given the high cardinality, stable relationship, and inherent sparsity, it seems to be a really good idea in 2005.

- Reed

ByAccount aggregation bug workaround [Joe Kasprzak]


When working with Account type dimensions and semi-additive Measures across a variety of account types with different aggregation functions, you can use the ByAccount aggregation function to set the proper aggregation behavior.

There is a bug that sometimes occurs to the finance measure where the ByAccount option doesn’t properly set various aggregation rules for each account type. This can be manually fixed by the following process:

* Right click the solution in the Solution Explorer window and click “Edit Database” to display the current settings for the finance measures by account type.

The ByAccount aggregation function should have set for each account type to either sum for additive or lastnonempty for accounts which are semi-additive.

* Click the Aggregation Function in the Account Type Mapping box and change the aggregation function from Sum to Lastnonempty for the Flow, Liability, Balance, Asset and Statistical account types.

- Joe