mdx a practical approuch
DESCRIPTION
advanced MDX tipsTRANSCRIPT
-
MDX A Practical View
Glenn Schwartzberg
-
These slides the opinions of the presenter and do not constitute official positions of
Oracle or any other organization. This material has not been peer reviewed
and is presented here in spite of the better judgment of the presenter
Disclaimer
-
Why this presentation?
This presentation will attempt to demystify MDX because
I sit through most presentations and at the end say Huh? (No offence Gary, most MDX presentations are over my head)
Because of that
I will attempt to show the basics with live practical examples.
The examples are simple enough that even I can understand them and use them.
-
Agenda
MDX Basics
What the heck are we talking about
Members
CurrentMember
Tuples
Sets
CrossJoins
Emptyset
(NonEmpty)
Properties
-
Agenda -
Continued
Examples
How to get Descendants
Solve Order
Rank
ISValid
(is it important?)
Time Balance
MTD/YTD functionality
Lag/lead beginning period
Rolling Average(single
and multiple time dims)
-
MDX?
A common Multidimensional Expression language that is common to OLAP products including:
Microsoft Analysis Services
Cognos
Oracle BI
Essbase
Its a query language -
Not a fully formatted
reporting language
Need to have some sort of front end program to format it.
-
Two uses with Essbase
Query data from a database
Block storage or Aggregate storage
Select {[Sales],[COGS]} on Axis(0){[Market].Levels(0).members} on axis(1)From ASOSamp.BasicWhere [Jan]
Create Member formulas
Aggregate storage only[Sales]/[Cogs]
-
A Few Basics
Members
Just like in Essbase a single item in a hierarchy
Represented as [MemberName] or
[Parent].[MemberName] or
[Dimension].[Parent].[MemberName] etc.
Note braces instead of double quotes
Last option necessary if you allow duplicate member names
-
Currentmember
Very powerful
One of the most important concepts in MDX
Only use it if necessary
[Jan] is more efficient than [Jan].Currentmember
Determines location for a starting point of a calculation Product.currentmember
Determines relative location for other functions Product.currentmember.parent
Can be used to loop through members, but for a calculation a single member is considered
-
Tuples
A combination of one or more dimensions to form a distinct intersection
(like Cross dim ->
in calc scripts)
Only one member from a dimension allowed
Every cell in the database is represented by a single tuple
with all of the dimensions represented
Enclosed in Parens
Examples: ([Sales],[New York],[100-10], [Actual],[Jan]) ([Profit],[Markets],Products])
-
Sets
Multiple members with the same dimensionality
Can include 0, 1, or multiple Tuples
Wrap in curly braces {} {[Period].levels(0).members} {([Scenario].[Actual],[Measures].[Sales]), ([Scenario].[Actual],[Measures].[Margin])}
Why 0 tuples?
Good for getting lists
-
Crossjoins
Combines multiple sets together to create a superset
of all the sets
Enclosed in Parens(CrossJoin({[Jan],[Feb]}, {[Y-2009]}))
= ([Jan], [Y-2009]) and ([Feb], [Y-2009])
You can nest cross joins by using commas and parens
Crossjoin(CrossJoin({[Jan],[Feb]}, {[Curr Year]}) ,{[Measures].[Units],[Measures].[Returns]})
-
Crossjoin
Example
SELECTCrossjoin(CrossJoin({[Jan],[Feb]},{[Curr Year]}) ,{[Measures].[Units], [Measures].[Returns]}) on Axis(0),
{[Payment Type].levels(0).members} ON axis(1)from ASOSamp.Sample
-
Properties
Used to get alternate member information
Member_Alias
(Default)
Member_Name
Gen_Number
Level_Number
Member_Unique_Name
IS_Expense
Comments (Hmmm -
cant do this with other tools)
-
Properties
Member names and Aliases
Return is pretty ugly
({[Geography].Levels(2).members})PROPERTIES [MEMBER_Name] ON axis(1)
-
Removing Empty Rows
We add the Non Empty directive to remove missing rowsNON EMPTY ({[Geography].Levels(2).members})PROPERTIES
[MEMBER_Name] ON axis(1)
Does not work well if you have multiple axis (I find you need to
cross join them instead)
-
Another way
Use a filter (which is probably better)
Like Where
syntax (just like SQL)
Different from MDX Where clause
Can be a member
Or a comparison
Can be crossjoins
Filter({[Geography].Levels(2).members},[Measures]. [Units] > missing)
-
More on NonEmpty
Version 11 offers new commands
NonEmptyMember
NonEmptyTuple
NonEmptySubset
Use for Formulas (same dimension(s) as in formula)
Can significantly speed up calculations especially complex calculations
Use NonEmptyTuple
if it takes more than one dimension to determine missing
-
Getting Descendants
In Calc formulas you could use @Descendants
There is not a direct equivalent in MDX
Instead we use Ancestors
Sounds confusing
How can we get descendants
looking at ancestors?
It is because
The order of the members in the statement makes a difference
-
IsAncestor([stores].currentmember,[Great Buys]) (If the current member is an ancestor of Great Buys)DOES NOT EQUALIsAncestor([Great Buys],[stores].currentmember) (If Great Buys is an Ancestor of the current member)
Lets take a look at a simple example:Case When IsAncestor([stores].currentmember,[Great Buys] ) Then 1 Else 2 EndCase When IsAncestor([Great Buys],[stores].currentmember ) Then
1 Else 2 End
-
Solve Order
Two Pass on steroids
Allows you to set the order of calculation
Best way to think of it is
this happens before that
Multiple types of Solve order
Dimension
Member
WARNING
-
The order of precedence is different
in different Essbase versions
In 7X-
the first equal wins in 9X and 11X the last
wins
-
Rank
No I dont mean the person sitting nearby that has not showered (or am I?)
Use a With clause to create the member set to compare
WITH MEMBER [Measures].[Sales_Rank] AS 'Rank(Product.CurrentMember, Product.levels(0).members)' SELECT {Sales, COGS, [Measures].[Sales_Rank]} ON COLUMNS, { Product.levels(0).members} ON ROWS FROM Sample.Basic;
-
ISValid
Checks for valid intersections
Using Prevmember, NextMember, Parent,Children
etc
Is this important?
Only if you want correct answers. Example:
Jan = 200, Feb = 300
Current month
prior month
Easy for Feb 300
200 = 100
What about Jan
Jan -
??? = 200??
Time.currentmember.firstchild
What if you are at the lowest level already?
-
ISValid
([Time].currentmember,[Units]) -
([time].currentmember.prevmember,[Units])
CASE WHEN (ISValid([Time].currentmember.prevmember)) Then([Time].currentmember,[Units]) -([time].currentmember.prevmember,[Units]) ELSE Missing END
-
Time Balance
In 11.X can do it natively, so why discuss it
Gary will tell you in his ASO presentation about not making accounts the accounts dim.
You will need another way to do it.
Tag Time balance members with UDAs
You can use similar logic for expense reporting or other special processing)
-
QTD/YTD functionality
Not built into ASO cubes
You could do this with alternate rollups
Downside. Problems if
You are using Time Balance (Periods needs to be stored)
You want to use certain functions like ranges
So how do you do it?
-
QTD/YTD functionality
Create a View or Analytic dimension
Must be a Dynamic dimension
Store the first member
Other members are calculated off of it
Does it matter if the members are tagged with + or ~
It depends. If dimension is label only then no, if not then yes
-
QTD/YTD Continued
QTD=SUM(PeriodsToDate([Periods].Generation s(2), [Periods].CurrentMember), [View].[Periodic])
YTD=SUM(PeriodsToDate([Periods].Generation s(1), [Periods].CurrentMember), [View].[Periodic])
Note
In Version 11, If you use timebalance use Aggregate instead of sum
-
Other COOL manipulations
Lead,Lag,Firstchild,Lastchild, FirstSibling, LastSibling, ParallelPeriod.ClosingPeriod, etc.
Good for ANY dimension
Really good for time dimensions.
Lead and Lag are like @Next and @prior or @PrevSibling or @NextSibling
(New calcscript commands in 11X)
No easy equivalent for Firstchild and Lastchild in Calcscripts.
Lead and lag less efficient than FirstChild, ParallelPeriod, etc
-
Example
SELECT{Sales} on axis(0) ,{[Jan],[Jan].lead(1),closingperiod([year].levels(0))} on Axis(1)FROM [Sample.basic]
-
Rolling Averages (One time dim)
Easiest scenario
Just have to lead or lag a range(dont forget ISVALID)
with member [year].[rollavg] as 'AVG({[Jan]:[Jan].lead(11)}) 'SELECT{[sales]} on axis(0) ,{[year].[rollavg]} on Axis(1)FROM [Sample.basic]
-
Rolling Averages (two time dims)
Documentation says it skips missing values, but in this case did not
Use Avg and TupleRange to get members
Use Case (or IIF) to make sure you are at proper level (if required) Necessary in my case as I hard coded end points.
Hard coded end points is more efficient than using relative positioning (faster querying)
-
Case when (IsLeaf([time].currentmember) and not (IsEmpty([time].currentmember))) then
AVG ({TUPLERANGE(
([years].currentmember.lag(1), [Time].currentmember.NextMember), ([Years].currentmember.lag(1), [Dec])), TUPLERANGE(
([Years].currentmember, [Jan]), ([Years].currentmember, [Time].currentmember))}
, [Measures].[Units]) Else
MissingEND
-
Head or Tail
Used to get a subset of a range (another way to get range)
For example
I want the a 14 month rolling average with two time dimensions.
Needs to be dynamic based on the selected time period
Could span 3 years depending on what period I start with
Will not work well if you have alternate rollups in periods.
-
Head Example
SELECT {} ON AXIS(0),{HEAD(([FY2010], [Jan]) : ([Years].LastChild, ClosingPeriod([Periods].Levels(0))), 14)} ON AXIS(1)
FROM [KScope.Test]
To make dynamic change
[FY2010], [Jan] to
[Years].currentmember,
[periods].currentmember
-
Flow Members
New in 11X
A Flow tag can be assigned to Accounts dimension members bearing formulas
Assume you have Sales and Additions figures for all 12 months. You want to perform an aggregation to populate each months beginning inventory.
Sales Additions InventoryJan 5 1 50Feb 6 3 46Mar 4 2 43Apr 7 0 41
-
Could do Inventory = SUM(MemberRange(Jan:Time.CurrentMember), (Additions - Sales)) + Beg_Inventory
OR
(Addition Sales), and tag the member as Flow
-
Restrictions on Flow
Restrictions on Alternate Hierarchies
If alternate hierarchies are used in the aggregate storage time dimension, the following restrictions apply when using Flow and TB tags on the Accounts dimension:
The shared level among alternate time hierarchies must be level 0.
The order of members at shared level among alternate time hierarchies must be the same in all alternate hierarchies.
-
MDX A Practical ViewGlenn Schwartzberg These slides the opinions of the presenter and do not constitute official positions of Oracle or any other organization.This material has not been peer reviewed and is presented here in spite of the better judgment of the presenterWhy this presentation? AgendaAgenda - ContinuedMDX?Two uses with EssbaseA Few BasicsCurrentmemberTuplesSetsCrossjoinsCrossjoin ExamplePropertiesPropertiesRemoving Empty RowsAnother wayMore on NonEmptyGetting DescendantsSlide Number 20Solve OrderRankISValidISValidSlide Number 25Time BalanceQTD/YTD functionalityQTD/YTD functionality QTD/YTD ContinuedOther COOL manipulationsExample Rolling Averages (One time dim)Rolling Averages (two time dims)Slide Number 34Head or TailHead ExampleFlow MembersSlide Number 38Restrictions on FlowSlide Number 40