Intro to Mdx Aso
-
Upload
sandeep-kulkarni -
Category
Documents
-
view
100 -
download
0
Transcript of Intro to Mdx Aso
#Kscope
Intro to MDX + ASO
Gary Crisci
Oracle Ace
#Kscope
Developing Essbase Applications
● 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 stands for Aggregate Storage Option● Essbase applications have two distinct
architecture/storage options● Aggregate Storage (ASO)● Block Storage (BSO)
● 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
Differences Between ASO and BSO
● 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
Differences Between ASO and BSO
BSO has Dense
and Sparse
dimensions
ASO has Dynamic
and Stored
hierarchies
#Kscope
ASO Hierarchies
Stored or Dynamic• Different concept than BSO
Cross use of terms causes confusion• “Everything in ASO is Dynamic”• “For best performance make
everything stored”
#Kscope
Stored vs. Dynamic
Let’s clarifyA 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 are all "dynamic".
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
Stored Hierarchy - Label Only
● Invalid
● Valid
#Kscope
Dynamic Hierarchies
The order in which members and formulas are evaluated is defined by the solve order property.
To evaluate a dynamic hierarchy, Essbase calculates, rather than aggregates, the members and formulas
When we talk about dynamic hierarchies, we are talking about the way Essbase stores and optimizes the data
#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.● (+, -, *, /, %, ~)
● (^) Not Supported● 2) They can have formulas.
#Kscope
Stored vs. Dynamic
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
Accounts Dimension and Members on Dynamic Hierarchies
In Block Storage applications, “Accounts” dimension, has added functionality.
• Accounts dimensions are enabled for Expense reporting, and Time Balancing.
Aggregate Storage applications do not support expense reporting.
• Can simulate with UDA and conditional MDX statements (CASE and/or IF).
ASO Account dimension supports Time Balancing
• There are limitations• Often will need to consider alternative approach
• MDX Time Balancing• Aggregating solutions using periodic balances instead of time balances
#Kscope
•By default, it is also set as the compression dimension•This is
not always a good thing!
ASO Account dimensions are
dynamic
•ASO applications do support Solve Order. •Solve
Order works in conjunction with the MDX query language to determine calculation order when processing member formulas.
Aggregate storage applications do not support two-pass
calculations,
Accounts Dimension and Members on Dynamic Hierarchies
#Kscope
Shared Members and Alternate Hierarchies
ASO has two methods to support alternate hierarchies
Use of an attribute
dimension
Hierarchy of shared
members.
#Kscope
Shared Member and Alternate Hierarchies
● 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
Shared Member and Alternate Hierarchies
● 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● Attribute dimensions in aggregate storage
databases:● Only the addition (+) consolidation operator is available for
attribute dimensions.● For a given attribute dimension, all associations must be
with one level of the base dimension. ● The following restrictions apply to attribute associations:
● 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
Essbase considers queries on attribute dimensions when using query tracking• May include attribute dimension
members in aggregate view selections.
#Kscope
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 pre-
aggregated 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
) ● Contents Section: MDX->Aggregate Storage and MDX->MDX
Outline Formulas.
#Kscope
Attribute Calculations DimensionThe 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
Data Load Differences
#Kscope
Data Load Differences
Cells loaded through data loads• ASO : Only level 0 cells whose values do not depend on
formulas in the outline are loaded• BSO: Cells at all levels can be loaded (except Dynamic Calc
and Label Only members)
Update of database values• ASO: At the end of a data load, if an aggregation exists, the
values in the aggregation are recalculated• BSO: No automatic update of values. To update data values,
you must execute all necessary calculation scripts.
#Kscope
Data Load Differences•ASO: The
loading of multiple data sources into aggregate storage databases is managed through temporary data load buffers.
•BSO: Not supported
Data load buffers
•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
Atomic replacement of the contents of
a database
#Kscope
Data Load Differences
Data slices
•ASO: Databases can contain multiple slices of data. Data slices can be merged.
•BSO: Not supported
Dimension build for
shared memb
ers
•ASO: Full support for parent-child build method. Duplicate generation (DUPGEN) build method limited to building alternate hierarchies up to generation 2 (DUPGEN2).
•BSO: Support for all build methods
#Kscope
Data Load Differences
Loading data mapped to
dates
ASO: In a date-time dimension, you can load
data into level-0 members using
supported date-format strings instead of member names.
BSO: Date-time dimension type is not
supported.
#Kscope
Query Differences
#Kscope
Query Differences
Report Writer
ASO: Supported, except for
commands related to sparsity and density of data
BSO: Fully supported
Spreadsheet Add-in
ASO: Supported, with limited ability to change data (write-
back)
BSO: Fully supported
#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
MDX queries
•ASO: Supported
•BSO: Supported
Queries on
attribute members that are
associated with
non-level 0
members
•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
• 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
Queries on attribute members and shared members
• ASO: Not supported• BSO: Supported
Query logging
#Kscope
Query Differences
Query performanceASO:
Considerations when querying data from a dimension that has multiple
hierarchies.
BSO: Hierarchies not relevant
#Kscope
Feature Differences
#Kscope
Feature Differences
Aliases
ASO: Supported
BSO: Supported
Currency conversion
ASO: Not 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
Incremental data load
ASO: Supported
BSO: Supported
LROs
ASO: Not supported
BSO: Supported
#Kscope
Feature Differences
Time balance reporting
Triggers
• 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
ASO: Supported
BSO: Not supported
Date-time dimension type and
linked attribute dimensions
BSO: Fully supported
ASO:• User write back
to level 0, via data slices, is supported
User ability to change data (write-
back)
#Kscope
Case Study: Converting a large BSO application to ASO
#Kscope
Existing BSO Model
#Kscope
Existing BSO Model
26 GB database• 4 GB total .ind files• 22 GB total .pag files
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● Large “Account” dimension with (+) and (–) operators
● To keep as is, would need to be Dynamic hierarchy● Too Slow!
● Only way to make it feasible is for Account dim to be Stored● Limited to (+) and (~) only
● “Account” dimension includes P&L and Balance Sheet● Balance Sheet values are time balanced
● Overall functionality has to be same as existing model● Query time, “look and feel”● Ability to query YTD values
#Kscope
ASO Solutions● Convert Account dimension to Stored Hierarchy
● Remove “Account” tag● Set all members to (+)● Use accounting principles to convert accounts into Debit/Credit
format● Tag Debit /Credit members with UDA flags● Leverage load rules to “FLIP” values for tagged members upon data
load● Add a View dimension with two members
● Accounting View ● default load member with values in Debit/Credit format
● Management Reporting View● Calculated member that uses MDX formula to “FLIP” sign on Credit
members● Load Periodic Balance Sheet values
● Add a new “Beginning Balance” member to Time dimension● Train users to pull Balances using YTD values
#Kscope
New ASO Model
#Kscope
New ASO Model vs. Existing BSO
•4 GB ind files•22 GB pag files
26 GB application
•Full overnight refresh•3-4 Hours
•Intraday update•1 Hour•Cube is offline
during this window
•Only update current year
Processing Window
• 2 GB application• Processing Window– Full overnight • ~15 minutes– Full intraday • ~3-4 minutes– No down time
#Kscope
What is MDX?
#Kscope
MDX
Cons•Lacks formatting•Can be problematic on large data sets•Not well suited for general use by end users
Pros•Understands hierarchical relationships and member properties
•Can perform robust calculations utilizing many predefined functions
“Standardized” query language for OLAP
Multi-Dimensional Expression
#Kscope
How do we use MDX with Essbase?
There are two primary ways, at
this time, to utilize MDX with
Essbase.
Query data from an Essbase database• Aggregate Storage
or Block Storage!
Construct member formulas• Aggregate Storage
only
#Kscope
MDX Access
Users can execute MDX queries in variety of ways
MaxL
EAS console
Third Party tools
#Kscope
MDX Basic Terms
● AXIS● SLICER● CUBE● SET● WITH● DIMENSION
● LAYER● MEMBER● TUPLE● FUNCTIONS● PROPERTIES
#Kscope
MDX Basic Query
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
MDX Basic Query
Where Slicer
AXIS(0)“Columns”
AXIS(1)“Rows”
AXIS(2)“Pages”
#Kscope
SQLSELECT Product, Sales, MarginFROM dbo.TransactionsWHERE Year = ‘Jan’
MDXSELECT {[Measures].[Sales], [Measures].[Margin]} ON COLUMNS,{[Product].levels(0).members} ON ROWSFROM [SAMPASO].[BASIC]WHERE [Year].[Jan] Must Define axis
specification to render multi-dimensional in a report.
#Kscope
Nesting rows using AXISSELECT {[Measures].[Sales], [Measures].[Margin]} ON AXIS(0),{[Product].levels(0).members} ON AXIS(1),{[Scenario].children} ON AXIS(2)FROM [SAMPASO].[BASIC]WHERE [Year].[Jan]
Nesting columns is a little differentSELECT {CrossJoin({[Measures].[Sales], [Measures].[Margin]},
{[Scenario].children})} ON axis(0),{[Product].levels(0).members} ON axis(1)FROM [SAMPASO].[BASIC]WHERE [Year].[Jan]
#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
We use MDX Expressions to define member formulas in the Essbase
outline
Same concept as member formulas in BSO
Many functions can be translated from Essbase calc language to MDX• In EAS go to Help -> Information Map ->
Technical Reference• Go to MDX -> Aggregate Storage Topics ->
MDX Outline Formulas• For version 7X go to MAXL -> MDX
#Kscope
● @AVG● Calculator: @AVG(SKIPMISSING, @CHILDREN(East)); ● MDX: Avg([East].Children)
● For SKIPNONE - Avg([East].Children,IncludeEmpty)● @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”,Connecticut));● 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
● Leaves([Market]) –New in Sys 9● @ISICHILD
● Calculator: @ISICHILD(South);● MDX: IIF(Is([Market].CurrentMember,[South]) OR
IsChild([Market].CurrentMember,[South]), <True>,<False>)
#Kscope
● Time Functionality● Leverage Analytic Dimensions
#Kscope
● Period To Date Functions[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") THENIIF(IsLeaf([Year].CurrentMember), [View].[Per],
(ClosingPeriod ([Year].Levels(0), [Year].CurrentMember), [View].[Per])ELSE [View].[Per] END
Before
After
#Kscope
● Formula Precedence (Solve Order)● The mechanism that standard MDX uses for dealing with dimensional
formula precedence is called solve order. Every calculated member has an associated solve order number, which is an integer that says what the calculation priority of the member is. When calculated members overlap on a cell, the member with the highest solve order number “wins” and is used to calculate the cell.
- MDX Solutions, 2006, Spofford, Harinath, Webb, Huang, Civardi
- Default value is 0, maximum value is 127- 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 member’s order if required
- In addition to calculating the correct value, Solve Order can yield huge performance gains for calculated members
- Test, Test, Test!!!
#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
MDX as Administrator Tool
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
Set dml_output options
#Kscope
Return All Members
alter session set dml_output alias off;set column_width 50;
SELECT {} ON AXIS(0),
[Year].Members ON AXIS(1)
FROM [Sample.Basic];
/*OR*/
SELECT {} ON AXIS(0),
Members([Year]) 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
Return All Children
SELECT {} ON AXIS(0),
{[Year].[Qtr1].Children} ON AXIS(1)
FROM [Sample.Basic];
/*OR*/
SELECT {} ON AXIS(0),
{[Qtr1].Children} ON AXIS(1)
FROM [Sample.Basic];
Axis-1
+--------------- (Jan)
(Feb)
(Mar)
#Kscope
Combining two sets
SELECT {} ON AXIS(0),
{[Qtr1].Children, [Qtr2].Children} ON AXIS(1)
FROM [Sample.Basic];
/*Beware of Duplicates*/
SELECT {} ON AXIS(0),
{[Year].Members, [Year].Children} ON AXIS(1)
FROM [Sample.Basic];
Axis-1 +--------------------------------------- (Jan) (Feb) (Mar) (Apr) (May) (Jun)
Axis-1 +------------------------------------------------- (Year) (Qtr1) (Jan) (Feb) (Mar) (Qtr2) (Apr) (May) (Jun) (Qtr3) (Jul) (Aug) (Sep) (Qtr4) (Oct) (Nov) (Dec) (Qtr1) (Qtr2) (Qtr3) (Qtr4)
#Kscope
Combining two sets
/*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)
Axis-1
+------- (Year)
(Qtr1)
(Jan)
(Feb)
(Mar)
(Qtr2)
(Apr)
(May)
(Jun)
(Qtr3)
(Jul)
(Aug)
(Sep)
(Qtr4)
(Oct)
(Nov)
(Dec)
(Qtr1)
(Qtr2)
(Qtr3)
(Qtr4)
#Kscope
Descendants including member
/*Descendants, including member*/
SELECT {} ON AXIS(0),
{Descendants([Year])} 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
Descendants excluding member
/*Descendants, excluding member*/
SELECT {} ON AXIS(0),
Except({Descendants([Year])}, {[Year]}) ON AXIS(1)
FROM [Sample.Basic];
Axis-1
+---------------------------------------
(Qtr1)
(Jan)
(Feb)
(Mar)
(Qtr2)
(Apr)
(May)
(Jun)
(Qtr3)
(Jul)
(Aug)
(Sep)
(Qtr4)
(Oct)
(Nov)
(Dec)
#Kscope
Descendants of a member mid-dimension
/*Descendants of a member mid-dimension*/
SELECT {} ON AXIS(0),
{Descendants([Qtr1])} ON AXIS(1)
FROM [Sample.Basic];
Axis-1
+---------- (Qtr1)
(Jan)
(Feb)
(Mar)
#Kscope
Descendants – Level 1 only
/*Only level 1 descendants*/
SELECT {} ON AXIS(0),
{Descendants([Year], 1)} ON AXIS(1)
FROM [Sample.Basic];
Axis-1
+----------- (Qtr1)
(Qtr2)
(Qtr3)
(Qtr4)
#Kscope
Descendants – Level 0 only
/*Only level 0 descendants*/
SELECT {} ON AXIS(0),
{Descendants([Year], Levels([Year],0))} ON AXIS(1)
FROM [Sample.Basic];
Axis-1
+------- (Jan)
(Feb)
(Mar)
(Apr)
(May)
(Jun)
(Jul)
(Aug)
(Sep)
(Oct)
(Nov)
(Dec)
#Kscope
Ancestors - up two levels
/*Ancestors, up 2 levels*/
SELECT {} ON AXIS(0),
{Ancestors([Jan], 2)} ON AXIS(1)
FROM [Sample.Basic];
Axis-1
+-------- (Year)
(Qtr1)
(Jan)
#Kscope
Ancestors - up one level
/*Ancestors, up 1 level*/
SELECT {} ON AXIS(0),
{Ancestors([Jan], 1)} ON AXIS(1)
FROM [Sample.Basic];
Axis-1
+------- (Qtr1)
(Jan)
#Kscope
Ancestors - up zero levels
/*Ancestors up 0 levels*//*Only returns the member, but it is
valid*/
SELECT {} ON AXIS(0),
{Ancestors([Jan], 0)} ON AXIS(1)
FROM [Sample.Basic];
Axis-1
+-------- (Jan)
#Kscope
Ancestors - up 100 levels
/*Ancestors up 100 levels*//*Doesn't matter that there are Less than 100 levels, still valid*/
SELECT {} ON AXIS(0),
{Ancestors([Jan], 100)} ON AXIS(1)
FROM [Sample.Basic];
Axis-1
+-------- (Year)
(Qtr1)
(Jan)
#Kscope
Cousins
/*Returns a child member at the same position as a member from another ancestor*/
SELECT {} ON AXIS(0),
{Cousin([Qtr1].[Feb], [Qtr2])} ON AXIS(1)
FROM [Sample.Basic];
Axis-1
+---------- (May)
#Kscope
Level 0 members
/*all level 0 members of dimension*/
SELECT {} ON AXIS(0),
{[Year].levels(0).members} ON AXIS(1)
FROM [Sample.Basic];
Axis-1
+------- (Jan)
(Feb)
(Mar)
(Apr)
(May)
(Jun)
(Jul)
(Aug)
(Sep)
(Oct)
(Nov)
(Dec)
#Kscope
Level 0 descendants of a member
/*level 0 descendants of a member*/
SELECT {} ON AXIS(0),
{Descendants([Time].[1st Half], [Time].levels(0), SELF)} ON AXIS(1)
FROM [ASOSamp.Sample];
/*OR*/
SELECT {} ON AXIS(0),
{Descendants([Time].[1st Half], 100, LEAVES)} ON AXIS(1)
FROM [ASOSamp.Sample];
Axis-1
+------ (Jan)
(Feb)
(Mar)
(Apr)
(May)
(Jun)
#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 ?
/*Specific member with level*/
SELECT {} ON AXIS(0),{[Qtr1]} DIMENSION PROPERTIES [Time].[LEVEL_NUMBER] ON AXIS(1)FROM [ASOsamp.Sample];
Axis-1 Axis-1.properties +-------------------------------------------------+------------------------------------------------- (Qtr1) (LEVEL_NUMBER = 1, type: ULONG, )
#Kscope
Members on same level
/*Return Members on same level*/
SELECT {} ON AXIS(0),
{[Time].[Qtr1].Level.members} ON AXIS(1)
FROM [ASOsamp.Sample];
Axis-1
+---------- (Qtr1)
(Qtr2)
(Qtr3)
(Qtr4)
#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];
Axis-1 Axis-1.properties +-------------------------------------------------+------------------------------------------------- (Qtr1) (GEN_NUMBER = 4, type: ULONG, )
#Kscope
Members on same generation
/*Return Members on same Generation*/
SELECT {} ON AXIS(0),
{[Time].[Qtr1].Generation.members} ON AXIS(1)
FROM [ASOsamp.Sample];
Axis-1
+--------- (Qtr1)
(Qtr2)
(Qtr3)
(Qtr4)
#Kscope
Counts
/*Count level 0 member*/
WITHMEMBER
[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
/*Count children*/
WITHMEMBER
[Year].[YearChildrenCount] AS 'Count([Year].Children)'
SELECT {[Measures]} ON AXIS(0),
{[Year].[YearChildrenCount]} on AXIS(1)
FROM Sample.Basic;
Axis-1 (Measures) +-------------------------------------------------+------------------------------------------------- (YearChildrenCount) 4
#Kscope
Count descendants
/*Count descendants*/
WITHMEMBER
[Year].[YearDescCount] AS 'Count(Descendants([Year]))'
SELECT {[Measures]} ON AXIS(0),
{[Year].[YearDescCount]} on AXIS(1)
FROM Sample.Basic;
Axis-1 (Measures) +-------------------------------------------------+------------------------------------------------- (YearDescCount) 17
#Kscope
Using Counts with filters
/*Members with one child*/ SELECT {} ON AXIS(0),
{FILTER({[Stores].members},COUNT({[Stores].CurrentMember.Children}, IncludeEmpty) = 1)} ON AXIS(1)
FROM [ASOsamp.Sample];
Axis-1
+---------- (Electronic Essentials)
(Club Electronics)
#Kscope
Using Counts with filters
/*Members with more than 100 children*/
SELECT {} ON AXIS(0),
{FILTER({[Geography].members},COUNT({[Geography].CurrentMember.Children}, IncludeEmpty) > 100)} ON AXIS(1)
FROM [ASOsamp.Sample];
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
Criteria based member search
/*Stores starting with "801"*/ SELECT {} ON AXIS(0),
{FILTER({[Geography].members},Substring([Geography].CurrentMember.Member_Name, 1, 3) = "801")} ON AXIS(1)
FROM [ASOsamp.Sample];
Axis-1
+----------- (80101)
(80107)
(80154)
(80116)
(80126)
(80117)
(80118)
(80124)
(80160)
(80161)
(80163)
(80165)
(80135)
#Kscope
Criteria based member search
/*Area Codes ending with "25"*/ SELECT {} ON AXIS(0),
{FILTER({[Area Code].levels(0).members},Substring([Area Code].CurrentMember.Member_Name,
Len([Area Code].CurrentMember.Member_Name ) - 1) = "25")} ON AXIS(1)
FROM [ASOsamp.Sample];
Axis-1
+-------- (225)
(425)
(925)
#Kscope
Criteria based member search
/*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];
Axis-1
+--------------------------- (Digital Cameras/Camcorders)
(Digital Cameras)
(Digital Recorders)
(Digital Recorders)
#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 = Member (Non-Measure) 1 = Dimension Root member 2 = Member with Formula 3 = Measure */
SELECT {} ON AXIS(0),
[Measures].levels(0).members DIMENSION PROPERTIES MEMBER_TYPE ON AXIS(1) FROM ASOsamp.Sample;
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
Filter member properties
/*Filter by Members with formulas*/
SELECT {} ON AXIS(0),
{FILTER([Measures].levels(0).members, [Measures].CurrentMember.MEMBER_TYPE = 2)} ON AXIS(1) FROM ASOsamp.Sample;
Axis-1
+---------------------- (Avg Units/Transaction)
(% of Total)
(Test)
#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
Filter member properties
/*Expense members*/
SELECT {} ON AXIS(0),
{FILTER([Measures].members, IsAccType([Measures].CurrentMember, Expense))}
ON AXIS(1) FROM Sample.Basic;
Axis-1
+----------------- (COGS)
(Total Expenses)
(Marketing)
(Payroll)
(Misc)
(Opening Inventory)
(Additions)
(Ending Inventory)
#Kscope
Filter Text Attributes
/* Text Attribute */
SELECT {} ON AXIS(0),
Attribute([Bottle]) ON AXIS(1)
FROM [Sample.Basic];
/* OR */
SELECT {} ON AXIS(0),
Withattr([Pkg Type], "==", "Can") ON AXIS(1)
FROM [Sample.Basic];
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)
#Kscope
Filter Boolean Attributes
/* 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];
Axis-1
+----------- (100-10)
(100-20)
(200-10)
(200-20)
(300-10)
(300-20)
(300-30)
Axis-1
+-----------(100-30)
(200-30)
(200-40)
(400-10)
(400-20)
(400-30)
#Kscope
Filter Boolean Attributes
/* 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];
ERROR - 1260046 - Unknown Member True used in query.
#Kscope
Filter Date Attributes
/* 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];
ERROR - 1001078 - Cannot form a valid attribute value from [04-01-1996].
Axis-1
+---------------------------------- (100-20)
(100-30)
#Kscope
Filter Numeric Attributes
/* Numeric Attribute */
SELECT {} ON AXIS(0),
Withattr([Population], ">=", 12000000) ON AXIS(1)
FROM [Sample.Basic];
Axis-1
+-------------- (New York)
(Florida)
(California)
(Texas)
(Illinois)
(Ohio)
#Kscope
Filter UDA
SELECT {} ON AXIS(0),
{Uda([Market], "Major Market")} ON AXIS(1)
FROM [Sample.Basic];
/*OR*/
SELECT {} ON AXIS(0),
{FILTER({[Market].members},IsUda([Market].CurrentMember, "Major Market"))} ON AXIS(1)
FROM [Sample.Basic];
Axis-1
+--------------- (East)
(New York)
(Massachusetts)
(Florida)
(California)
(Texas)
(Central)
(Illinois)
(Ohio)
(Colorado)
#Kscope
Filter UDA
/*Combine Functions to further define filter*/
SELECT {} ON AXIS(0),
{FILTER({Uda([Market], "Major Market")},Substring([Market].CurrentMember.Member_Name, 1, 3) = "New")} ON AXIS(1)
FROM [Sample.Basic];
Axis-1
+------------ (New York)
#Kscope
Count UDA
/* count members with a UDA */
WITHMEMBER
[Market].[MajorMarketCount] AS 'Count(UDA([Market], "Major Market"))‘
SELECT {[Measures]} ON AXIS(0),{[Market].[MajorMarketCount]} on AXIS(1)
FROM Sample.Basic;
Axis-1 (Measures) +-------------------------------------------------+------------------------------------------------- (MajorMarketCount) 10
#Kscope
Count Attribute
/* Count members with specific attribute */
WITHMEMBER
[Geography].[AreaCode719Count] AS 'Count(Withattr([Area Code], "==", "719"))‘
SELECT {[Measures]} ON AXIS(0),{[Geography].[AreaCode719Count]} on AXIS(1)
FROM ASOSamp.Sample;
Axis-1 (Measures) +-------------------------------------------------+------------------------------------------------- (AreaCode719Count) 50
#Kscope
Count Attribute
/* Count mebers with attribute and members without attribute */
WITHMEMBER
[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;
Axis-1 (Measures) +-------------------------------------------------+------------------------------------------------- (NoAttribute) 1 (HasAttribute) 9397 (Lev0Count) 9398
#Kscope
Filter members missing attribute
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
Finding Shared or Duplicate Members
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) 1
(Camcorders) 1
(Photo Printers) 1
(Handhelds) 1
(Memory) 1
(Other Accessories) 1
(Boomboxes) 1
(Radios) 1
(Direct View) 1
(Projection TVs) 1
(Flat Panel) 2
(HDTV) 2
(Stands) 1
(Home Theater) 1
(HiFi Systems) 1
(Digital Recorders) 2
(DVD) 1
(Desktops) 1
(Notebooks) 2
(Displays) 1
(CD/DVD drives) 1
(Flat Panel) 2
(HDTV) 2
(Digital Recorders) 2
(Notebooks) 2*Code adapted from an example given by George Spofford
#Kscope
Finding Shared or Duplicate Members
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];
Axis-1
+-------------(Flat Panel)
(HDTV)
(Digital Recorders)
(Notebooks)
*Code adapted from an example given by George Spofford
#Kscope
Sparse Optimization using MDX
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
Sparse Optimization using MDX
Using Sample.Basic as an example● Market dimension has 25 stored members● Product dimension has 18 stored members
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
Sparse Optimization using MDX
To keep things simple, let’s assume that all members are stored and there is one primary hierarchy ● (I’ve removed the Diet alternate rollup)
If we use the logic that● All members are parent, except for level 0 members
AND● All members are Children, except for the dimension root
member Then we can construct an MDX query to calculate
the parent to child ratio
#Kscope
Sparse Optimization using MDX
WITH MEMBER [Measures].[Market Ratio] AS '(COUNT([Market].members) - COUNT([Market].levels(0).members)) /
(COUNT([Market].members) - 1)'
MEMBER [Measures].[Product Ratio] AS'(COUNT(Except([Product].members, Descendants([Diet]))) -
COUNT(Except([Product].levels(0).members, Descendants([Diet])))) / (COUNT(Except([Product].members, Descendants([Diet]))) - 1)'
SELECT {[Year]} ON AXIS(0),{ [Measures].[Market Ratio], [Measures].[Product Ratio] } ON
AXIS(1)FROM [Sample.Basic];Axis-1----------------------------------------------------------(Market Ratio) 0.208333333333333(Product Ratio) 0.294117647058824
#Kscope
Sparse Optimization using MDX
Market Dimension has more stored members than Product● 25 vs. 18
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
You’re mileage may vary!
#Kscope
Questions
There are three types of answers
GoodFast
Cheap
You can choose two!