I am trying to learn MDX. I am an experienced SQL Developer.
I am trying to find an example of an MDX query that has more than two dimensions. Every single webpage that talks about MDX provides simple two dimensional examples link this:
select
{[Measures].[Sales Amount]} on columns,
Customer.fullname.members on rows
from [Adventure Works DW2012]
I am looking for examples that use the following aliases: PAGES (third dimension?), section (forth dimension?) and Chapter (fifth dimension?). I have tried this but I do not think it is correct:
select
{[Measures].[Sales Amount]} on columns,
Customer.fullname.members on rows,
customer.Location.[Customer Geography] as pages
from [Adventure Works DW2012]
I am trying to get this output using an MDX query (this is from AdventureWorks DW2012):
That's not a 3-dimensional resultset in your screenshot, unless there's something cropped from it.
Something like
SELECT [Geography].[Country].Members ON 0,
[Customer].[CustomerName].Members ON 1
FROM [whatever the cube is called]
WHERE [Measures].[Sales Amount]
(dimension/hierarchy/level names may not be exactly right)
would give a resultset like the one in your message.
The beyond 2nd-dimension dimensions and dimension names are not used in any client tool that I know. (Others may know different). They seem to be there in MDX so that MDX can hand >2-dimensional resultsets to clients that can handle them (e.g. an MDX subquery handing its results to the main query).
An often-used trick in MDX is to get the members of two dimensions onto one axis by cross-joining:
SELECT
{[Date].[Calendar Date].[Calendar Year].Members * [Geography].[Country].Members} ON 0,
[something else] ON 1
FROM [Cube]
How about the following - it does not send more than two dimensions back to a flat screen but it uses quite a few dimensions explicitly:
SELECT
[Measures].[Sales Amount] ON O,
[Customer].[fullname].MEMBERS ON 1
FROM
(
SELECT
[Date].[Calendar Month].[Calendar Month].&[February-2012] ON 0,
[Geography].[Country].[Country].&[Canada] ON 1,
[Product].[Product].&[Red Bike] ON 2,
[Customer].[Customer].&[foo bar] ON 3
FROM [Adventure Works DW2012]
)
I've made up the dimension | hierarchy | member combinations as I do not have access to the cube.
Also if we consider implicit dimensions then take the following:
SELECT
[Customer].[Location].[Customer Geography] ON 0,
[Customer].[fullname].[fullname].&[Aaron Flores] ON 1
FROM [Adventure Works DW2012]
WHERE
(
[Measures].[Sales Amount]
);
On the slicer I've used braces (..) which indicate a tuple, but this is actually shorthand for the following:
SELECT
[Customer].[Location].[Customer Geography] ON 0,
[Customer].[fullname].[fullname].&[Aaron Flores] ON 1
FROM [Adventure Works DW2012]
WHERE
(
[Measures].[Sales Amount]
,[Date].[Calendar Month].[Calendar Month].[All],
,[Geography].[Country].[Country].[All],
,[Product].[Product].[All]
,...
,...
....
);
The All member from every dimension in the cube could be included in this slicer without affecting the result.
So the whole nature of mdx is multi-dimensional - yes you do not get more than a 2 dimensional table returned to your screen but the way you get to that cellset could well involve many dimensions.
Related
I was wondering which of the following two queries is more performant?
Query 1:
SELECT NONEMPTY(CROSSJOIN({[Product].[Category].children},
{[Scenario].[Scenario].members}
)
) ON COLUMNS
FROM [Analysis Services Tutorial]
Query 2:
SELECT CROSSJOIN(NONEMPTY({[Product].[Category].children}),
NONEMPTY({[Scenario].[Scenario].members})
) ON COLUMNS
FROM [Analysis Services Tutorial]
I would say query 2 is more performant/optimized because first you take out all the unnecessary members and then crossjoin them. The first query you crossjoin everything and then take out the nulls. That would be my guess but I want somebody who can clear me up.
Edit 1 In response of comments of an answer
Lets say I add a measure as a second parameter, so it does not go to the "default measure". How could second query return values with null? I am specifying to crossjoin between nonempty members. And I just really dont see how the can return different results no matter the dimensions involved. To me they seemed pretty equivalent. What am I not seeing?
Query 1:
SELECT NONEMPTY(CROSSJOIN({[Product].[Category].children},
{[Scenario].[Scenario].members}
), [Total Internet Sales]
) ON COLUMNS
FROM [Analysis Services Tutorial]
Query 2:
SELECT CROSSJOIN(NONEMPTY({[Product].[Category].children},[Total Internet Sales]),
NONEMPTY({[Scenario].[Scenario].members},[Total Internet Sales])
) ON COLUMNS
FROM [Analysis Services Tutorial]
Edit 2
As the answer said the queries are not the same. I realized when #GregGalloway presented other scenario.
I did an excel with sample data so maybe someone can find it useful.
They aren't equivalent since both queries we will return different results. For example, against the real Adventure Works (not some tutorial version) these two queries return different results. Notice that the Clothing/Kentucky column shows null on the second query:
SELECT NONEMPTY(CROSSJOIN({[Product].[Category].children},
{[Customer].[State-Province].[State-Province].Members}
), [Measures].[Internet Sales Amount]
) ON COLUMNS
FROM [Adventure Works]
where [Measures].[Internet Sales Amount]
SELECT CROSSJOIN(NONEMPTY({[Product].[Category].children},[Measures].[Internet Sales Amount]),
NONEMPTY({[Customer].[State-Province].[State-Province].Members},[Measures].[Internet Sales Amount])
) ON COLUMNS
FROM [Adventure Works]
where [Measures].[Internet Sales Amount]
Note that the Scenario dimension doesn't relate to the Internet Sales measure group, I don't think. So that may not be a good example. I chose the Product dimension and the Customer dimension for my example.
As discussed (and as you updated in your question) NonEmpty() should always have a second parameter so it is clear what measure you are doing NonEmpty against. Your query should also mention a measure on one axis or the WHERE clause so that you're not returning some vague "default measure". I've included a WHERE clause with a measure in my examples.
Anyway, to answer your question... assuming the measure is a physical measure or a well optimized calculated measure that runs in block mode I wouldn't be surprised if Query 1 is faster. But it depends on the measure and the size of dimensions and the sparsity of the cube. This question is very theoretical and the two queries don't return equivalent results.
I have a requirement displaying data from same dimension in more than 1 column. For eg. I want to show data Year and Month wise. In my dimension structure, Year and Month belongs to same hierarchy. When I run below query I get error. PFB the query.
Select NON EMPTY {[Measures].[Target Actual Value]} ON 0,
NON EMPTY {[Realization Date].[Hierarchy].[Year Name].Members *
[Realization Date].[Hierarchy].[Month Year]} ON 1
From [Cube_BCG_OLAP]
The error I get is Query (2, 12) The Hierarchy hierarchy is used more than once in the Crossjoin function. I am new to MDX queries. Please help in this regard. Thanks in advance.
Select NON EMPTY {[Measures].[Target Actual Value]} ON 0,
NON EMPTY {[Realization Date].[Hierarchy].[Year Name].Members ,
[Realization Date].[Hierarchy].[Month Year]} ON 1
From [Cube_BCG_OLAP]
Instead of CROSSJOIN have a set as above. In a set, you can put members from same hierarchy
I like Sourav's answer - but it will put the results in one column which is slightly different than the question.
In AdvWorks this is in one column:
SELECT
[State-Province].MEMBERS ON COLUMNS
,{
[Date].[Calendar].[Calendar Year].MEMBERS
,[Date].[Calendar].[Month].MEMBERS
} ON ROWS
FROM [Adventure Works];
It is possible to switch to two columns and use a cross join but you need to find out the details of your Date dimensions Attribute hierarchies (as opposed to User hierarchies):
SELECT
[State-Province].MEMBERS ON COLUMNS
,
[Calendar Year].[All Periods].Children
* [Month].MEMBERS ON ROWS
FROM [Adventure Works];
In your cube maybe something like this:
SELECT
NON EMPTY
{[Measures].[Target Actual Value]} ON 0
,NON EMPTY
[Year Name].MEMBERS
*
[Month Year].MEMBERS ON 1
FROM [Cube_BCG_OLAP];
I am using Performance Point Dashboard Designer 2013 and SharePoint Server 2013 for building dashboards. I am using SSAS2012 for Cube.
I have a scenario similar to the one illustrated by figure below. I am required to find Previous Non-Empty value for purpose of finding Trends.
Measure: [Quota]
Dimension: [Date].[Calendar Date].[Date]
The script ([Measures].[Quota], [Date].[Calendar Date].PrevMember) gives you a previous date. Lets say for date 27-Jan-13 whose Quota value is 87, it returns 26-Jan-13 which has null value. I want it to return 21-Jan-13 that has some Quota value. And for date 21-Jan-13, I want to return 15-Jan-13.
I wonder if this is possible.
Thanks,
Merin
After long searches and hits & trials and so on, I think I invented a solution of my own for myself.
Following is the script for my Calculated Member.
(
[Quota],
Tail
(
Nonempty
( LastPeriods(15, [Date].[Calendar Date].PrevMember)
,[Quota]
)
).Item(0)
)
Explanation
The number 15 means it will look for non-empty measures up to 15 siblings.
Now we know up to how many siblings to traverse back, in this case 15.
Lets find 15 previous siblings (both empty and non-empty) excluding current member.
(LastPeriods(15, [Date].[Calendar Date].PrevMember)
Since it will yield both empty and non-empty members, lets filter out empty members in terms of measure [Quota]. If we don't specify measure here, it will use default measure whatever it is and we may not get desired result.
Nonempty(LastPeriods(15, [Date].[Calendar Date].PrevMember),[Quota])
We may have several members in the output. And we will choose the last one.
Tail
(
Nonempty
( LastPeriods(15, [Date].[Calendar Date].PrevMember)
,[Quota]
)
)
So far, the script above gives previous non-empty member. Now we want to implement this member for our measure [Quota].
Hence we get the script below ready to create a Calculated Member.
(
[Quota],
Tail
(
Nonempty
( LastPeriods(15, [Date].[Calendar Date].PrevMember)
,[Quota]
)
).Item(0)
)
You can use recursion to define this.
The following query delivers something similar for the Adventure Works cube:
WITH member [Measures].[Prev non empty] AS
IIf(IsEmpty(([Date].[Calendar].CurrentMember.PrevMember, [Measures].[Internet Sales Amount])),
([Date].[Calendar].CurrentMember.PrevMember, [Measures].[Prev non empty]),
([Date].[Calendar].CurrentMember.PrevMember, [Measures].[Internet Sales Amount])
), format_String = '$#,##0.00'
SELECT {[Measures].[Internet Sales Amount], [Measures].[Prev non empty]}
ON COLUMNS,
non empty
Descendants([Date].[Calendar].[Month].&[2007]&[12], [Date].[Calendar].[Date])
ON ROWS
FROM [Adventure Works]
WHERE [Customer].[Customer].&[12650]
You would have to replace the name of the date hierarchy, as well as the measure name from Internet Sales Amount to Quota in the recursive definition of the measure Prev non empty.
I'm trying to combine multiple members from a single hierarchy, though this leads to the following error:
Query (11, 3) The Jr-Kw-Mnd-Dag hierarchy is used more than once in the Crossjoin function.
This is a basic version of the Query I'm using:
SELECT
NON EMPTY {
[Measures].[Amount]
} ON COLUMNS
, NON EMPTY {
[Realisatiedatum].[Jr-Kw-Mnd-Dag].[Jaar]
* [Realisatiedatum].[Jr-Kw-Mnd-Dag].[Maand])
} ON ROWS
FROM
[Cube]
Jaar equals year in English, Maand equals month in English. This is what I'm trying to accomplish:
...
november 2013
december 2013
januari 2014
februari 2014
...
Last but not least, the hierarchy:
I would normally create several hierarchies within the Date dimension, such as Calendar, Financial and others that contain just financial year, calendar year, quarters etc.
If you have another hierarchy that contains the month, you could crossjoin with the year of the hierarchy you are using at the moment; then you won't be using the same hierarchy twice in the crossjoin function.
E.g.
, NON EMPTY {
( [Date Dimension].[Financial].[Financial Year]
* [Date Dimension].[Calendar].[Month] ) }
If you want to skip some levels in a user hierarchy, the best is to CrossJoin the corresponding attribute hierarchies of the remaining levels.
Here under I use the attribute hierarchy ([Geography].[City].[City], ..) instead of the user hierachy ([Geography].[Geography].[City],..) form the AW cube:
SELECT
[Measures].[Internet Sales Amount] ON 0,
[Geography].[State-Province].[State-Province] * [Geography].[City].[City] ON 1 FROM [Adventure Works]
Philip,
This issue is related to trying to pull multiple levels from the same hierarchy using a crossjoin and indeed you cannot. As mentioned in other replies, a good work around is to pull the columns you need from places other than the same hierarchy. But that works only if the cube design allows for it.
Your specific problem may be related to the tool you are using to develop the query and which levels in the hierarchy it returns.
For example, the following query when executed in SQL Server Management Studio (versions through V12.0.2000.8) returns only the City level of the hierarchy. But when executed from within design mode in the PowerPivot table import wizard it returns all levels in the hierarchy down to the city level including Country, State-Province and City.
select
[Measures].[Internet Order Count] on columns,
non empty [Customer].[Customer Geography].[City] on rows
from [Adventure Works]
I am developing a query builder application that generates MDX and trying to get customer counts from a cube using the following, which works just fine:
WITH MEMBER MEASURES.X AS (
{ [Customer].[Gender].[Female]},
[Customer].[Customer].Children
).Count
SELECT Measures.X ON 0 FROM [Adventure Works]
However, if the user drags in a dimension that is not related to the customer like:
WITH MEMBER MEASURES.X AS (
{ [Customer].[Gender].[Female]},
{ [Employee].[Status].[Active], [Employee].[Status].[Inactive]},
[Customer].[Customer].Children
).Count
SELECT Measures.X ON 0 FROM [Adventure Works]
the count result obviously becomes incorrect.
Is there a way to determine whether a dimension is related to the customer so that I can exclude it from the generated MDX query?
This information can be retrieved from the cube through AMO. The Cube class contains all the cube metadata you'll need.
Solved the problem by using the Exists( Set_Expression1 , Set_Expression2 [, MeasureGroupName] ) function. No need to manually determine which dimensions are related. The Exists function filters out the unrelated tuples, leaving only the
{ [Customer].[Customer].Children, [Customer].[Gender].[Female]} set to do the count over.
Here is the MDX:
WITH MEMBER MEASURES.X AS Exists(
[Customer].[Customer].Children,
{[Customer].[Gender].[Female]}
*
{[Employee].[Status].[Active], [Employee].[Status].[Inactive]}
).Count
SELECT Measures.X ON 0 FROM [Adventure Works]