Intro To MDX Aso
Intro To MDX Aso
Intro To MDX Aso
#Kscope
Like the best, most advanced Essbase conference there ever could be Advanced content Good practices Written by some of the most well known Essbase developers Source code at www.developingessbasebook.com You should buy it
#Kscope
What is ASO?
ASO was introduced in version 7 Initially ASO was seen as an alternative to large block storage applications with sparse data sets With enhancements over the last few releases, in many cases, ASO is now the default storage option choice for new applications
#Kscope
Aggregate storage applications store data in tablespaces. Each application directory contains directories for four tablespaces:
default: Contains database data structure and database values (After data is loaded, the tablespace location cannot be changed.) log: contains a binary transactional log of all the updates that are made in the default tablespace metadata: contains information about the file locations, files, and objects contained in the database temp: Provides a temporary workspace to be used during operations such as data loads, aggregations, and retrievals
#Kscope
#Kscope
ASO Hierarchies
Stored or Dynamic
Different concept than BSO
#Kscope
Lets clarify
A level 0 member with no formula, even though it is in a dynamic hierarchy, is still a stored member.
All level 0 members that do not have formulas are stored in ASO.
Upper level members of dynamic hierarchies are pure dynamic (i.e. calculated on retrieval).
Upper level members of stored hierarchies are candidates to have their values pre-aggregated and stored in a view.
#Kscope
Stored Hierarchies
Stored hierarchies are the default hierarchy setting and generally best performing.
Whenever possible, hierarchies should be set to Stored.
The limitations of stored hierarchies are that all members must be set to (+ or ~) for the consolidation operator.
If the consolidation operator is set to (~), the parent member must be set to Label Only.
You cannot have members with member formulas in a stored hierarchy, and you cannot have shared members in a stored hierarchy (unless the stored hierarchy is within a multiple hierarchies enabled dimension). There are also two limitations on label only members.
1) All dimension members at the same level as the member must be label only and 2) the parents of the member must be label only.
#Kscope
Invalid
Valid
#Kscope
Dynamic Hierarchies
When we talk about dynamic hierarchies, we are talking about the way Essbase stores and optimizes the data
To evaluate a dynamic hierarchy, Essbase calculates, rather than aggregates, the members and formulas
The order in which members and formulas are evaluated is defined by the solve order property. #Kscope
Dynamic Hierarchies
At the time of retrieval, Essbase calculates the required member combinations and calculates any required outline member formulas. Because dynamic hierarchies are calculated, the data retrieval time may be longer than for data retrieved from stored hierarchies. However, when you design your database, dynamic hierarchies provide the following advantages:
1) They can contain any consolidation operator.
(+, -, *, /, %, ~)
#Kscope
For best query performance, try to make as many hierarchies stored as possible
Stored hierarchies become candidates for pre-aggregation Queries will be much faster Can leverage query tracking to optimize aggregations
#Kscope
Multiple Hierarchies
Multiple hierarchies is a setting at the dimension level that allows for having both stored and dynamic hierarchies within the same dimension.
#Kscope
#Kscope
By default, it is also set as the compression dimension This is not always a good thing!
Aggregate ASO applications do support Solve Order. storage Solve Order works in conjunction with applications do the MDX query language to determine calculation order when processing not support twomember formulas. pass calculations,
#Kscope
#Kscope
The alternate hierarchy has shared members that refer to non-shared members of previous hierarchies in the outline. The shared members roll up according to a different hierarchy from the non-shared members to which they refer. Shared members on dynamic hierarchies can have formulas.
#Kscope
The following restrictions apply when creating alternate hierarchies in aggregate storage outlines:
The nonshared instance of the member must occur in the outline before any shared instances of the member. The first hierarchy in a dimension where multiple hierarchies are enabled cannot contain a shared member. Stored hierarchy dimensions cannot have shared members.
Stored hierarchies within a multiple hierarchies dimension can have shared members.
To ensure that values are not double-counted, a stored hierarchy cannot contain multiple copies of the same shared member. Nonshared instances of a member must be in the same dimension as the shared member (same for block storage outlines). A stored hierarchy cannot contain a nonshared instance and a shared instance of the same member. A stored hierarchy can contain a shared instance of a dynamic hierarchy member only if the dynamic hierarchy member is a level 0 member without a formula.
#Kscope
Attribute Dimensions
Level 0: You can associate attributes with any level 0 member of a dynamic or stored hierarchy that does not have a formula. Non-level 0: You can associate attributes only to upper level members in the primary stored hierarchy.
#Kscope
Attribute Dimensions
Calculation Differences
#Kscope
Database Calculation
Database calculations are done in a block storage application by executing calculation scripts, as well as member formulas, and outline consolidation operators. The ability to materialize data via MDX scripts has been recently introduced in v 11.1.2 ASO database calculations are based on outline operators and member formulas. Aggregate storage applications are fully dynamic
data can be loaded into the database at level 0 and retrieved at upper levels with calculated results being returned.
To improve query performance, the database can be preaggregated by defining aggregate views.
#Kscope
Formulas
BSO applications leverage Essbase calculation script functions Aggregate storage databases utilize MDX functions.
There is no support for BSO functions in ASO, they must be converted to MDX. Refer to the Essbase technical reference
(http://download.oracle.com/docs/cd/E12825_01/epm.111/esb_techref/frameset. htm?launch.htm)
#Kscope
The attribute calculations dimension in block storage applications supports Sum, Count, Min, Max, and Average by default.
The attribute calculations dimension in aggregate storage applications only supports Sum.
#Kscope
#Kscope
#Kscope
ASO: When loading data, you can replace the contents of the database or the contents of all incremental data slices in the database. BSO: Not supported
#Kscope
Data slices
ASO: Full support for parent-child build method. Duplicate generation (DUPGEN) build method limited to building alternate hierarchies up to generation 2 Dimension build (DUPGEN2). for shared BSO: Support for all build methods members
#Kscope
ASO: In a date-time dimension, you can load data into level-0 members using supported date-format strings instead of member names.
#Kscope
Query Differences
#Kscope
Query Differences
Report Writer
ASO: Supported, except for commands related to sparsity and density of data
Spreadsheet Add-in
ASO: Supported, with limited ability to change data (write-back)
#Kscope
Query Differences
API
ASO: Supported BSO: Supported
Export
ASO: Support with the following restrictions: Export of level 0 data only (no upper-level export) No columnar export BSO: Supported
#Kscope
Query Differences
ASO: Supported BSO: Supported
MDX queries
ASO: Returns values for descendants of the non-level 0 member. BSO: Returns #MISSING for descendants of the non-level 0 member
#Kscope
Query Differences
Queries on attribute members and shared members ASO: A shared member automatically shares the attribute associations of its nonshared member BSO: A shared member does not share the attribute associations of its nonshared member
Query logging
ASO: Not supported BSO: Supported
#Kscope
Query Differences
Query performance
ASO: Considerations when querying data from a dimension that has multiple hierarchies. BSO: Hierarchies not relevant
#Kscope
Feature Differences
#Kscope
Feature Differences
Aliases
Currency conversion
ASO: Not supported BSO: Supported
ASO: Supported
BSO: Supported
#Kscope
Feature Differences
Data mining
ASO: Not supported BSO: Supported
Hybrid analysis
ASO: Support with the following restriction:
queries that contain a relational member and an Essbase member with a formula in the same query are not supported.
For example, if California is a relational member, and the member Profit has a formula, the following report script returns an error: Jan California Profit !
BSO: Supported
#Kscope
Feature Differences
ASO: Supported
BSO: Supported ASO: Not supported
LROs
Feature Differences
ASO: Support with the following restrictions: Skip Zeros is not supported Time dimension must contain at least one stored hierarchy Shared members must be at level zero BSO: Supported
ASO: After-update triggers supported BSO: On-update triggers and after-update triggers supported
#Kscope
Feature Differences
Unicode
ASO: Supported BSO: Supported
Variance reporting
ASO: Not supported Can work around with UDA and conditional MDX functions BSO: Supported
#Kscope
Feature Differences
Date-time dimension type and linked attribute dimensions
ASO: Supported
ASO:
#Kscope
#Kscope
#Kscope
Processing Window
3 to 4 hours full overnight refresh 1 hour intraday update Cube is offline during this window Only update current year
#Kscope
ASO Challenges
Too Slow!
#Kscope
ASO Solutions
Tag Debit /Credit members with UDA flags Leverage load rules to FLIP values for tagged members upon data load Accounting View
default load member with values in Debit/Credit format Calculated member that uses MDX formula to FLIP sign on Credit members
Add a new Beginning Balance member to Time dimension Train users to pull Balances using YTD values
#Kscope
#Kscope
Full intraday
Processing Window
Full overnight refresh 3-4 Hours Intraday update 1 Hour Cube is offline during this window Only update current year
#Kscope
What is MDX?
#Kscope
MDX
Pros
Understands hierarchical relationships and member properties Can perform robust calculations utilizing many predefined functions
MultiDimensional Expression
#Kscope
Cons
Lacks formatting Can be problematic on large data sets Not well suited for general use by end users
There are two primary ways, at this time, to utilize MDX with Essbase.
#Kscope
MDX Access
#Kscope
Proper Syntax
SELECT { [Year].Children } ON AXIS(0), { [Product].Children } ON AXIS(1), { [Market].Children } ON AXIS(2) FROM [Sample.Basic]; WHERE ( [Measures].[Sales],[Scenario].[Actual] )
#Kscope
#Kscope
SQL
SELECT Product, Sales, Margin FROM dbo.Transactions WHERE Year = Jan
MDX
SELECT {[Measures].[Sales], [Measures].[Margin]} ON COLUMNS, {[Product].levels(0).members} ON ROWS FROM [SAMPASO].[BASIC] Must Define axis WHERE [Year].[Jan]
specification to render multi-dimensional in a report.
#Kscope
#Kscope
Naming Conventions
The most obvious way to identify a member is to start with the name of the dimension and work downwards, specifying the members at each level in the hierarchy until we reach the required members - Fast Track to MDX, 2004, Whitehorn, Zare, Pasumansky [Jan] [Year].[Jan] [Year].[Qtr1].[Jan] * No difference in performance * Particularly important with Duplicate Member names * No quotes - Jan vs. [Jan]
#Kscope
Tuples
A tuple is defined as an intersection of exactly a single member from each dimension in the cube. For each dimension that is not explicitly referenced, the current member is implicitly added to the tuple definition. A tuple always identifies (or has the potential to identify) a single cell in the multi-dimensional matrix. That could be an aggregate or a leaf level cell, but nevertheless one cell and only one cell is ever implied by a tuple. - Fast Track to MDX, 2004, Whitehorn, Zare, Pasumansky ([Year].[Jan], [Measures].[Sales], [Product].[Cola], [Market].[East]) ([Year].[Jan], [Measures].[Sales]) ([Year].[Jan])
* Wrap tuples in parenthesis ( ) * Similar to a cross-dimensional operator (Jan->Sales) vs. ([Year].[Jan], [Measures].[Sales])
#Kscope
Sets
A set is a collection of tuples with the same dimensionality. It may have more than one tuple, but it can also have only one tuple, or even have zero tuples, in which case it is an empty set. - Fast Track to MDX, 2004, Whitehorn, Zare, Pasumansky {([Scenario].[Actual],[Measures].[Sales]), ([Scenario].[Actual],[Measures].[Margin])} {[Year].levels(0).members} {[Measures].[Sales]} Wrap sets in { }
#Kscope
#Kscope
@CHILDREN Calculator: @CHILDREN(Market); MDX: Children([Market]) OR [Market].Children @ICHILDREN Calculator: @ICHILDREN(Market); MDX: Union({[Market]},{[Market].children}) @CURRMBR Calculator: @CURRMBR(Product); MDX: CurrentMember([Product]) or [Product].CurrentMember @LEVMBRS Calculator: @LEVMBRS(Product,0); MDX: [Product].levels(0).Members
#Kscope
@LSIBLINGS Calculator: @LSIBLINGS(Qtr4); MDX: MemberRange([Qtr4].FirstSibling, [Qtr4].Lag(1)) @RSIBLINGS Calculator: @RSIBLINGS(Qtr1); MDX: MemberRange([Qtr1].Lead(1), [Qtr1].LastSibling) @PARENTVAL Calculator: @PARENTVAL(Market, Sales); MDX: ([Sales], [Market].CurrentMember.Parent).Value @REMOVE Calculator: @REMOVE(@CHILDREN(East),@LIST(New York,Connectic MDX: Except({[East].Children}, {[New York], [Connecticut]}) @COUNT Calculator: @COUNT(SKIPMISSING, @RANGE(Sales, Children(Product MDX:NonEmptyCount(CrossJoin({[Sales]},{[Product].Children})) For SKIPNONE Count([Product].Children)
#Kscope
@IDESCENDANTS Calculator: @IDESCENDANTS(Market); MDX: Descendants([Market]) @DESCENDANTS Calculator: @DESCENDANTS(Market,0); MDX: Descendants([Market], [Market].levels(0)) OR
#Kscope
Time Functionality
Leverage Analytic Dimensions
#Kscope
[QTD] = SUM( PeriodsToDate( [Year].Generations(2), [Year].CurrentMember ), [View].[Per] ) [YTD] = SUM( PeriodsToDate( [Year].Generations(1), [Year].CurrentMember ), [View].[Per] )
#Kscope
Time Balancing
CASE WHEN IsUDA([Measures].CurrentMember, "TB_Last") THEN IIF(IsLeaf([Year].CurrentMember), [View].[Per], (ClosingPeriod ([Year].Levels(0), [Year].CurrentMember), [View].[Per]) ELSE [View].[Per] END
Before
After
#Kscope
MDX Solutions, 2006, Spofford, Harinath, Webb, Huang, Civardi Multiple members can have the same solve order
- Dimension solve order sets the default solve order for all members in the dimension, you can still edit individual members order if required - In addition to calculating the correct value, Solve Order can yield huge performance gains for calculated members
#Kscope
Recap MDX is a powerful query language, similar to SQL, that is used with multi-dimensional databases. MDX can be used to query both ASO and BSO Essbase cubes, although it is primarily used for member expressions in ASO models. Most Essbase calculator functions can be converted to MDX expressions refer to the technical document. You can leverage MDX expression in ASO cubes to simulate Dynamic Time Series (Period To Date) and Time Balancing functionality. Solve Order is extremely important, both for calculating accurate results and performance.
#Kscope
Provides a framework for administrators to analyze meta-data. Easily generates member lists based on parameters to filter results Apply complex logic to find answers to questions that can be used to troubleshoot or optimize an outline
#Kscope
#Kscope
SELECT {} ON AXIS(0),
[Year].Members ON AXIS(1)
FROM [Sample.Basic];
/*OR*/
#Kscope
SELECT {} ON AXIS(0),
{[Year].[Qtr1].Children} ON AXIS(1) FROM [Sample.Basic];
/*OR*/
#Kscope
/*Beware of Duplicates*/
#Kscope
/*Removes Duplicates*/ SELECT {} ON AXIS(0), Distinct({[Year].Members, [Year].Children}) ON AXIS(1) FROM [Sample.Basic]; /*OR*/ SELECT {} ON AXIS(0), Union({[Year].Members}, {[Year].Children}) ON AXIS(1) FROM [Sample.Basic];
/* ALL keyword will keep duplicates*/ SELECT {} ON AXIS(0), Union({[Year].Members}, {[Year].Children}, ALL) ON AXIS(1) FROM [Sample.Basic];
Axis-1 +---------(Year) (Qtr1) (Jan) (Feb) (Mar) (Qtr2) (Apr) (May) (Jun) (Qtr3) (Jul) (Aug) (Sep) (Qtr4) (Oct) (Nov) (Dec)
#Kscope
Axis-1 +------(Year) (Qtr1) (Jan) (Feb) (Mar) (Qtr2) (Apr) (May) (Jun) (Qtr3) (Jul) (Aug) (Sep) (Qtr4) (Oct) (Nov) (Dec) (Qtr1) (Qtr2) (Qtr3) (Qtr4)
#Kscope
#Kscope
FROM [Sample.Basic];
#Kscope
#Kscope
#Kscope
#Kscope
#Kscope
Axis-1 +-------(Jan)
#Kscope
#Kscope
Cousins
/*Returns a child member at the same position as a member from another ancestor*/
Axis-1 +---------(May)
#Kscope
Level 0 members
/*all level 0 members of dimension*/
Axis-1 +------(Jan) (Feb) (Mar) (Apr) (May) (Jun) (Jul) (Aug) (Sep) (Oct) (Nov) (Dec)
#Kscope
FROM [ASOSamp.Sample];
/*OR*/
#Kscope
What Level ?
/*list of all members with levels*/ SELECT {} ON AXIS(0), [Time].members DIMENSION PROPERTIES [Time].[LEVEL_NUMBER] ON AXIS(1) FROM [ASOsamp.Sample];
Axis-1 Axis-1.properties +-------------------------------------------------+------------------------------------------------(Time) (LEVEL_NUMBER = 4, type: ULONG, ) (MTD) (LEVEL_NUMBER = 3, type: ULONG, ) (1st Half) (LEVEL_NUMBER = 2, type: ULONG, ) (Qtr1) (LEVEL_NUMBER = 1, type: ULONG, ) (Jan) (LEVEL_NUMBER = 0, type: ULONG, ) (Feb) (LEVEL_NUMBER = 0, type: ULONG, ) (Mar) (LEVEL_NUMBER = 0, type: ULONG, ) (Qtr2) (LEVEL_NUMBER = 1, type: ULONG, ) (Apr) (LEVEL_NUMBER = 0, type: ULONG, ) (May) (LEVEL_NUMBER = 0, type: ULONG, ) (Jun) (LEVEL_NUMBER = 0, type: ULONG, ) (2nd Half) (LEVEL_NUMBER = 2, type: ULONG, ) (Qtr3) (LEVEL_NUMBER = 1, type: ULONG, ) (Jul) (LEVEL_NUMBER = 0, type: ULONG, ) (Aug) (LEVEL_NUMBER = 0, type: ULONG, ) (Sep) (LEVEL_NUMBER = 0, type: ULONG, ) (Qtr4) (LEVEL_NUMBER = 1, type: ULONG, ) (Oct) (LEVEL_NUMBER = 0, type: ULONG, ) (Nov) (LEVEL_NUMBER = 0, type: ULONG, ) (Dec) (LEVEL_NUMBER = 0, type: ULONG, )
#Kscope
What Level ?
#Kscope
#Kscope
What Generation ?
/*list of all members with Generation*/ SELECT {} ON AXIS(0), [Time].members DIMENSION PROPERTIES [Time].[GEN_NUMBER] ON AXIS(1) FROM [ASOsamp.Sample];
Axis-1 Axis-1.properties +-------------------------------------------------+------------------------------------------------(Time) (GEN_NUMBER = 1, type: ULONG, ) (MTD) (GEN_NUMBER = 2, type: ULONG, ) (1st Half) (GEN_NUMBER = 3, type: ULONG, ) (Qtr1) (GEN_NUMBER = 4, type: ULONG, ) (Jan) (GEN_NUMBER = 5, type: ULONG, ) (Feb) (GEN_NUMBER = 5, type: ULONG, ) (Mar) (GEN_NUMBER = 5, type: ULONG, ) (Qtr2) (GEN_NUMBER = 4, type: ULONG, ) (Apr) (GEN_NUMBER = 5, type: ULONG, ) (May) (GEN_NUMBER = 5, type: ULONG, ) (Jun) (GEN_NUMBER = 5, type: ULONG, ) (2nd Half) (GEN_NUMBER = 3, type: ULONG, ) (Qtr3) (GEN_NUMBER = 4, type: ULONG, ) (Jul) (GEN_NUMBER = 5, type: ULONG, ) (Aug) (GEN_NUMBER = 5, type: ULONG, ) (Sep) (GEN_NUMBER = 5, type: ULONG, ) (Qtr4) (GEN_NUMBER = 4, type: ULONG, ) (Oct) (GEN_NUMBER = 5, type: ULONG, ) (Nov) (GEN_NUMBER = 5, type: ULONG, ) (Dec) (GEN_NUMBER = 5, type: ULONG, )
#Kscope
What Generation ?
/*Specific member with Generation*/ SELECT {} ON AXIS(0), {[Qtr1]} DIMENSION PROPERTIES [Time].[GEN_NUMBER] ON AXIS(1) FROM [ASOsamp.Sample];
#Kscope
#Kscope
Counts
/*Count level 0 member*/ WITH MEMBER [Year].[YearMemberCount] AS 'Count([Year].Levels(0).members)' SELECT {[Measures]} ON AXIS(0), {[Year].[YearMemberCount]} on AXIS(1) FROM Sample.Basic;
Axis-1 (Measures) +-------------------------------------------------+------------------------------------------------(YearMemberCount) 12
#Kscope
Count children
#Kscope
Count descendants
/*Count descendants*/ WITH MEMBER [Year].[YearDescCount] AS 'Count(Descendants([Year]))' SELECT {[Measures]} ON AXIS(0), {[Year].[YearDescCount]} on AXIS(1) FROM Sample.Basic;
#Kscope
#Kscope
Axis-1 +--------(CO) (KS) (NE) (IA) (IL) (IN) (MI) (MN) (MO) (OH) (WI) (MA) (ME) (NJ) (NY) (PA) (AL) (AR) (FL) (GA) (KY) (LA) (MD) (MS) (NC) (SC) (TN) (VA) (WV) (OK) (TX) (CA) (OR) (WA)
#Kscope
Axis-1 +----------(80101) (80107) (80154) (80116) (80126) (80117) (80118) (80124) (80160) (80161) (80163) (80165) (80135)
#Kscope
#Kscope
/*Products with word "Digital"*/ SELECT {} ON AXIS(0), {FILTER({[Products].members}, InStr(1, [Products].CurrentMember.Member_Name, "Digital", 1) > 0)} ON AXIS(1) FROM [ASOsamp.Sample]; /*Distinct Products with word "Digital", doesn't work, Shared members are not duplicates!*/ SELECT {} ON AXIS(0), Distinct({FILTER({[Products].members}, InStr(1, [Products].CurrentMember.Member_Name, "Digital", 1) > 0)}) ON AXIS(1) FROM [ASOsamp.Sample];
#Kscope
Using Distinct
/*An example where Distinct works*/ SELECT {} ON AXIS(0), {[Time].levels(0).members, [Qtr1].Children} ON AXIS(1) FROM [ASOsamp.Sample]; /*vs*/ SELECT {} ON AXIS(0), Distinct({[Time].levels(0).members, [Qtr1].Children}) ON AXIS(1) FROM [ASOsamp.Sample];
Axis-1 +---------(Jan) (Feb) (Mar) (Apr) (May) (Jun) (Jul) (Aug) (Sep) (Oct) (Nov) (Dec) (Jan) (Feb) (Mar)
Axis-1 +---------(Jan) (Feb) (Mar) (Apr) (May) (Jun) (Jul) (Aug) (Sep) (Oct) (Nov) (Dec)
#Kscope
Member Properties
/* 0 1 2 3
= = = =
Axis-1 Axis-1.properties +-------------------------------------------------+------------------------------------------------(Original Price) (MEMBER_TYPE = 3, type: ULONG, ) (Price Paid) (MEMBER_TYPE = 3, type: ULONG, ) (Returns) (MEMBER_TYPE = 3, type: ULONG, ) (Units) (MEMBER_TYPE = 3, type: ULONG, ) (Transactions) (MEMBER_TYPE = 3, type: ULONG, ) (Avg Units/Transaction) (MEMBER_TYPE = 2, type: ULONG, ) (% of Total) (MEMBER_TYPE = 2, type: ULONG, ) (Test) (MEMBER_TYPE = 2, type: ULONG, )
#Kscope
#Kscope
Member Properties
/*Expense Members*/ SELECT {} ON AXIS(0), [Measures].members DIMENSION PROPERTIES [Measures].[IS_EXPENSE] ON AXIS(1) FROM Sample.Basic;
Axis-1 Axis-1.properties +-------------------------------------------------+------------------------------------------------(Measures) (IS_EXPENSE = FALSE, type: BOOL, ) (Profit) (IS_EXPENSE = FALSE, type: BOOL, ) (Margin) (IS_EXPENSE = FALSE, type: BOOL, ) (Sales) (IS_EXPENSE = FALSE, type: BOOL, ) (COGS) (IS_EXPENSE = TRUE, type: BOOL, ) (Total Expenses) (IS_EXPENSE = TRUE, type: BOOL, ) (Marketing) (IS_EXPENSE = TRUE, type: BOOL, ) (Payroll) (IS_EXPENSE = TRUE, type: BOOL, ) (Misc) (IS_EXPENSE = TRUE, type: BOOL, ) (Inventory) (IS_EXPENSE = FALSE, type: BOOL, ) (Opening Inventory) (IS_EXPENSE = TRUE, type: BOOL, ) (Additions) (IS_EXPENSE = TRUE, type: BOOL, ) (Ending Inventory) (IS_EXPENSE = TRUE, type: BOOL, ) (Ratios) (IS_EXPENSE = FALSE, type: BOOL, ) (Margin %) (IS_EXPENSE = FALSE, type: BOOL, ) (Profit %) (IS_EXPENSE = FALSE, type: BOOL, ) (Profit per Ounce) (IS_EXPENSE = FALSE, type: BOOL, ) (Test) (IS_EXPENSE = FALSE, type: BOOL, ) (var) (IS_EXPENSE = FALSE, type: BOOL, ) (Flag) (IS_EXPENSE = FALSE, type: BOOL, ) (Test2) (IS_EXPENSE = FALSE, type: BOOL, )
#Kscope
/*Expense members*/
Axis-1 +----------------(COGS) (Total Expenses) (Marketing) (Payroll) (Misc) (Opening Inventory) (Additions) (Ending Inventory)
#Kscope
/* OR */
Axis-1 +-----------(100-30) (200-10) (200-20) (200-30) (200-40) (300-10) (300-20) (400-10) (400-20) (400-30) Axis-1 +-----------(100-10) (100-20) (300-30)
SELECT {} ON AXIS(0),
#Kscope
/* Boolean Attribute */ SELECT {} ON AXIS(0), {FILTER({[Product].members}, [Product].CurrentMember.[Caffeinated])} ON AXIS(1) FROM [Sample.Basic]; /*OR*/ SELECT {} ON AXIS(0), Withattr([Caffeinated], "==", "TRUE") ON AXIS(1) FROM [Sample.Basic]; /*Opposite*/ SELECT {} ON AXIS(0), Withattr([Caffeinated], "==", "FALSE") ON AXIS(1) FROM [Sample.Basic];
#Kscope
/* this won't work */ SELECT {} ON AXIS(0), Attribute([True]) ON AXIS(1) FROM [Sample.Basic]; /*OR*/ SELECT {} ON AXIS(0), Attribute([Caffeinated].[True]) ON AXIS(1)
FROM [Sample.Basic];
#Kscope
/* Date attribute */ /*This won't work*/ SELECT {} ON AXIS(0), Withattr([Intro Date], "==", "04-01-1996") ON AXIS(1) FROM [Sample.Basic];
/*This is correct*/
SELECT {} ON AXIS(0),
Withattr([Intro Date], "==", Todate("mm-dd-yyyy", "04-01-1996")) ON AXIS(1) FROM [Sample.Basic];
#Kscope
/* Numeric Attribute */
#Kscope
Filter UDA
/*OR*/
Axis-1 +--------------(East) (New York) (Massachusetts) (Florida) (California) (Texas) (Central) (Illinois) (Ohio) (Colorado)
#Kscope
Filter UDA
SELECT {} ON AXIS(0), {FILTER({Uda([Market], "Major Market")}, Substring( [Market].CurrentMember.Member_Name, 1, 3) = "New")} ON AXIS(1) FROM [Sample.Basic];
#Kscope
Count UDA
/* count members with a UDA */ WITH MEMBER [Market].[MajorMarketCount] AS 'Count(UDA([Market], "Major Market")) SELECT {[Measures]} ON AXIS(0), {[Market].[MajorMarketCount]} on AXIS(1) FROM Sample.Basic;
#Kscope
Count Attribute
/* Count members with specific attribute */ WITH MEMBER [Geography].[AreaCode719Count] AS 'Count(Withattr([Area Code], "==", "719")) SELECT {[Measures]} ON AXIS(0), {[Geography].[AreaCode719Count]} on AXIS(1) FROM ASOSamp.Sample;
#Kscope
Count Attribute
/* Count mebers with attribute and members without attribute */ WITH MEMBER [Geography].[NoAttribute] AS 'Count(Filter( [Geography].levels(0).members, NOT IsValid([Geography].CurrentMember.[Area Code]))) MEMBER [Geography].[HasAttribute] AS 'Count(Filter( [Geography].levels(0).members, IsValid([Geography].CurrentMember.[Area Code]))) MEMBER [Geography].[Lev0Count] AS 'Count([Geography].Levels(0).members) SELECT {[Measures]} ON AXIS(0), {[Geography].[NoAttribute], [Geography].[HasAttribute], [Geography].[Lev0Count]} on AXIS(1) FROM ASOSamp.Sample;
#Kscope
SELECT {} ON AXIS(0), {Filter( [Geography].levels(0).members, NOT IsValid([Geography].CurrentMember.[Area Code]))} on AXIS(1) FROM ASOSamp.Sample;
Axis-1 +--------(80101)
#Kscope
WITH MEMBER [Measures].[Products_SharedMembers] AS 'Count( Generate({[Products].CurrentMember} AS [var1], Generate(Filter([Products].Levels(0).Members, [Products].CurrentMember.[MEMBER_NAME] = [var1].Item(0).Item(0).[MEMBER_NAME]), Filter ({[Products].CurrentMember}, IsAncestor( [Products], [Products].CurrentMember )))))' SELECT {[Measures].[Products_SharedMembers]} ON AXIS(0), [Products].Levels(0).Members ON AXIS(1) FROM [ASOSamp.Sample];
(Digital Cameras) (Camcorders) (Photo Printers) (Handhelds) (Memory) (Other Accessories) (Boomboxes) (Radios) (Direct View) (Projection TVs) (Flat Panel) (HDTV) (Stands) (Home Theater) (HiFi Systems) (Digital Recorders) (DVD) (Desktops) (Notebooks) (Displays) (CD/DVD drives) (Flat Panel) (HDTV) (Digital Recorders) (Notebooks)
1 1 1 1 1 1 1 1 1 1 2 2 1 1 1 2 1 1 2 1 1 2 2 2 2
#Kscope
WITH MEMBER [Measures].[Products_SharedMembers] AS 'Count( Generate({[Products].CurrentMember} AS [var1], Generate(Filter([Products].Levels(0).Members, [Products].CurrentMember.[MEMBER_NAME] = [var1].Item(0).Item(0).[MEMBER_NAME]), Filter({[Products].CurrentMember}, IsAncestor([Products], [Products].CurrentMember)))))' SELECT {} ON AXIS(0), Filter( Except([Products].Levels(0).Members, Descendants([All Merchandise])), [Measures].[Products_SharedMembers] > 1) ON AXIS(1) FROM [ASOSamp.Sample];
#Kscope
Conventional wisdom has been to order outline from smallest sparse dimension to largest sparse dimension. Edward Roske has said
The smallest to largest sparse is a total misnomer. What you actually want to do is make sure the number of blocks grows as slowly as possible (so make them in order of lowest ratio of parents:children)
#Kscope
Conventional wisdom says that the Product Dimension would come before the Market dimension in outline order in order to adhere to the hour glass.
#Kscope
To keep things simple, lets assume that all members are stored and there is one primary hierarchy
(Ive removed the Diet alternate rollup)
Then we can construct an MDX query to calculate the parent to child ratio
#Kscope
#Kscope
However the Parent:Child ratio of Market is smaller than the Parent:Child ratio of Product
.21 vs. .29
In order to optimize calc time, in theory, Market should come before Product in the outline Youre mileage may vary!
#Kscope
Questions