Cross join same hierarchy columns - sql

I have a SQL data cube with following hierarchy
I want to cross join Warehouse division and Code warehouse Desc. I wrote a MDX as follows
SELECT NON EMPTY
{ [Measures].[Total Value]}
DIMENSION PROPERTIES CHILDREN_CARDINALITY,
PARENT_UNIQUE_NAME ON COLUMNS,
NON EMPTY
{
[Combined].[Drill Down Path 4].[Warehouse Division].MEMBERS* [Combined].[Drill Down Path 4].[Code Warehouse Desc].MEMBERS
}
DIMENSION PROPERTIES MEMBER_CAPTION ON ROWS FROM [InventoryAge]
WHERE ( [Calendar].[Report Days].[All Members].&[All].&[WantInReport].& [2].&[20141031] )
It gives me an error as follows
Query (13, 8) The Drill Down Path 4 hierarchy is used more than once in the Crossjoin function.
Can any body suggests a better way to do this
Please find the calender hierarchy

You don't need to crossjoin hierarchy (this is impossible) to do what you need. Just query the lowest level of it, you will get all parents also
SELECT NON EMPTY
{ [Measures].[Total Value]}
DIMENSION PROPERTIES CHILDREN_CARDINALITY,
PARENT_UNIQUE_NAME ON COLUMNS,
NON EMPTY
{
[Combined].[Drill Down Path 4].[Code Warehouse Desc].allMEMBERS
}
DIMENSION PROPERTIES MEMBER_CAPTION ON ROWS FROM [InventoryAge]
WHERE ( [Calendar].[Report Days].[All Members].&[All].&[WantInReport].& [2].&[20141031] )
PS. You might not be able to see them in SSMS query result viewer, but they will appear if you use query in cube browser or as dataset in SSRS/other tool

You can pass last date in your set by using Tail(Existing [Calendar].[Report Days].[All Members].&[All].&[WantInReport].members, 1).item(0)
Or you can use Format(Now()), which gives you string representation of current system date. However, it depends on locale, so you probably need to remove dots/slashes. See here

Related

MDX/SSRS - how do you get the “ALL” label to display in a report then add additional dimensions

I recently posted a question on how to get the ALL label to appear in a SSRS report. "whytheq" was kind enough to post an example that works. Below is the MDX with an added dimension that if I uncomment it will give me an error:
Query (8, 5) Two sets specified in the function have different dimensionality.
WITH
MEMBER [Due Date].[Calendar Month].[All].[YTD] AS
[Due Date].[Calendar Month].[All]
SELECT
NON EMPTY
{[Measures].[Freight]} ON COLUMNS
,NON EMPTY
{
//[Product].[Color].[Color].MEMBERS*
[Due Date].[Calendar Month].[All].[YTD],
[Due Date].[Calendar Month].[Calendar Month].MEMBERS
} ON ROWS
FROM [Adventure Works Cube];
I get that I have a Dimension at the .MEMBERS level and I've also added the .[YTD] dimension.
I did noted that if I comment out the .[YTD] and uncomment the [Product] dimension this works but I have to use an * instead of a comma like so.
[Product].[Color].[Color].MEMBERS*
//[Due Date].[Calendar Month].[All].[YTD],
[Due Date].[Calendar Month].[Calendar Month].MEMBERS
Is it possible to have the Product dimension include in the rows along with the YTD and Calendar Month.MEMEBERS?
Greg's answer to your question is fine, I just wanted to show you the nuts and bolts of your query so maybe you get a better understanding of how you use an MDX query and a cube's structure to produce the result you want.
First, here's the basic structure of most MDX queries:
SELECT
Set_Expression ON COLUMNS
,
Set_Expression ON ROWS
FROM CUBE;
(There's a bit more to MDX, but this is probably 80-90%.)
A set expression is an expression that returns a set - so as you can see it's pretty important that understand what a set is and how to build a valid expression that returns the right set.
So first, what is a set, again? A set is -drum roll- .... a set of tuples1. But not just any tuples - only tuples which have the same dimensional hierarchical structure (also called dimensionality). Let's look at some examples of potential set expressions, and I'll tell you if they're valid or not.
A set with one tuple? VALID.
{ Product.Product.Laptop }
A set with two tuples from the same dimension hierarchy? VALID.
{ Product.Product.Laptop , Product.Product.Desktop }
A set with two tuples from the same dimension hierarchy but different levels? VALID.
{ Date.DateHierarchy.Year.2008 , Date.DateHierarchy.Quarter.2009Q1 }
A set with two tuples from the same dimension, but a different hierarchy? INVALID.
{ Product.Product.[Laptop] , Product.Category.[Hardware] }
Why is this invalid? Because the two tuples have different dimensionalities. If you consider back to the literal metaphor of a "cube", each dimension hierarchy is a "face" and each tuple is a slice of the cube along the faces that make up its dimensionality (and any dimension hierarchy that isn't part of the tuple's explicit dimensionality is treated as an implicit "All members" at execution time) So in order to combine two or more tuples into a set - so you can extract them from the cube in a single slice - they must all come from the same set of faces - the same dimensionality.
A set with a tuple and a set function (an MDX function which returns a set of tuples) from the same dimensional hierarchy? VALID.
{
Tail ( Order ( Product.Product.Members, Measures.Profit, BASC ), 5),
Product.Product.All
}
A set with two set functions from the same dimensional hierarchy? VALID
{ Subset(Product.Product.Members,0,5), Subset(Product.Product,Members,6,5) }
A set with a tuple with two dimensions? VALID but! you have to put parentheses around the tuple. (You can put parentheses around tuples with just one dimension, but it's not necessary.)
{ ( Product.Product.Laptop, Date.Year.2015 ) }
A set with two tuples each with two dimensions at the same respective hiearchical levels? VALID
{
( Product.Product.Laptop, Date.Year.2015 ),
( Product.Product.Tablet, Date.Year.2013 )
}
(Note the members don't have to be the same at all, just the hierarchies. Hopefully this is the point where you recognize what "dimensionality" means.)
A set with two tuples each with two dimensions at the same respective hiearchical levels but not in the same order within the tuples? INVALID
{
( Product.Product.Laptop, Date.Year.2015 ),
( Date.Year.2013, Product.Product.Tablet )
}
(Compare to UNION in SQL - you have to have consistent ordering so MDX can build the set up to perform other tasks, such as nesting each member of a tuple correctly on a query axis.)
And finally, if we want to combine two sets from different dimensional hierarchies into a single set, we use the handy dandy CrossJoin function:
CrossJoin ( { Product.Product.Laptop } , Date.Year.Members )
As you've discovered, you can also use an asterisk (*) to perform a cross join of two sets
Product.Product.Laptop * Date.Year.Members
Now it's important to understand what's going on here. I said "combine sets" earlier, but as you've seen with all the examples, you can't just throw various sets of tuples with different dimensionality together into a single set.
So what's a CrossJoin doing?
Well, given a CrossJoin of
{Set With Tuple Dimensionality (A)} * {Set with Tuple Dimensionality (B)}
The resulting set has a tuple dimensionality of (A, B).
So looking back at our CrossJoin example, our left-hand side has just one tuple, and its dimensionality is (Product.Product). And our right-hand side has many tuples (every member of the Date.Year hierarchy) with the dimensionality of (Date.Year). So our final set of tuples will have the dimensionality of (Product.Product, Date.Year).
So say we had every year from 2010-2015 in our cube, our final set would be 6 tuples of
{
( Product.Product.Laptop, Date.Year.2010),
( Product.Product.Laptop, Date.Year.2011),
( Product.Product.Laptop, Date.Year.2012),
( Product.Product.Laptop, Date.Year.2013),
( Product.Product.Laptop, Date.Year.2014),
( Product.Product.Laptop, Date.Year.2015)
}
So you could in theory tack on more tuples to this set as long as they're simpatico with the same dimensionality - say, (Product.Product.Tablet, Date.Year.2013)
So this would also be a VALID set expression:
{
Product.Product.Laptop * Date.Year.Members,
(Product.Product.Tablet, Date.Year.2013)`
}
So now going back to your problem, if we uncomment your broken line of code we see you've got
{
[Product].[Color].[Color].MEMBERS *
[Due Date].[Calendar Month].[All].[YTD],
[Due Date].[Calendar Month].[Calendar Month].MEMBERS
}
So, here you've got a set of tuples
[Product].[Color].[Color].MEMBERS *
[Due Date].[Calendar Month].[All].[YTD]
whose tuples have the dimensionality of
(Product.Color, Due Date.Calendar Month)
And then you're trying to tack on another set of tuples to this set
[Due Date].[Calendar Month].[Calendar Month].MEMBERS
whose tuples have a dimensionality of
(Due Date.Calendar Month)
Now do you understand the error message you got?
And this leads me to why I wrote this very long post at 1 in the morning: your error is an error of fundamentally misunderstanding the concept of a set in MDX, not simply an error of syntax.
Obviously you want your final set's tuple's dimensional hierarchy structure to be
(Product.Color, Due Date.Calendar Month)
And again, Greg's answer achieves this. So really I hope my post has explained how Greg's answer achieves this!
Also, here's two alternate syntactical ways to achieve the same set result you want on the COLUMNS axis:
NON EMPTY { ([Product].[Color].[Color].Members, [Due Date].[Calendar Month].Members ) }
and
NONEMPTY ( [Product].[Color].[Color].Members, [Due Date].[Calendar Month].Members )
Basically, the (set, set) syntax also performs a crossjoin, as does the NONEMPTY(set1, set2) function. Just so you can see that there are multiple syntactical ways to achieve the same concept.
Quick refresher on tuples: Each cell in your cube represents a unique set of dimension hierarchy members. A tuple is a set of dimension hierarchy members which constitute a set of cells (or coordinate space) in your cube. (N.B.: Any dimension hierarchy not explicitly named in a tuple expression uses its default member, which if not explicitly set in an SSAS cube model is the All member.)
So the tuple expression (Product.Product.Laptop) points to the cellset in the cube where the member of the Product.Product dimension hierarchy is Product.Product.Laptop, and for all the other dimension hierarchies the member is the default member (if it's the All member, the cube basically doesn't do any work, since no slicing is required on that dimension hierarchy.)
In the image below, you can see how the tuple expression (Time.[2nd half], Source.nonground.air) is applied to the cube to produce its coordinate space.
Okay, back to the top.
Try this:
NON EMPTY
{
[Product].[Color].[Color].MEMBERS*
{
[Due Date].[Calendar Month].[All].[YTD],
[Due Date].[Calendar Month].[Calendar Month].MEMBERS
}
} ON ROWS

MDX query dynamic where clause

SELECT NON EMPTY { [Measures].[Total Value],[Measures].[Value less than 30],
[Measures].[Value less than 60],[Measures].[Value less than 90],[Measures].[Value less than 150],
[Measures].[Value less than 180],[Measures].[Value less than 365],[Measures].[Value more than 365]}
DIMENSION PROPERTIES CHILDREN_CARDINALITY, PARENT_UNIQUE_NAME ON COLUMNS,
NON EMPTY {[Combined].[Drill Down Path 4].[Supplier Name].ALLMEMBERS }
DIMENSION PROPERTIES MEMBER_CAPTION ON ROWS FROM [InventoryAge]
WHERE ( [Calendar].[Report Days].[All Members].&[All].&[WantInReport].&[2].&[20141031] )
for the where clause I want to get the last element of my calender dimension. The calender dimension is as follows
What is the best way to achieve this
Try something like this:
WHERE ( [Calendar].[Report Days].[All Members].[All].[WantInReport].[Last Days].LastChild )
You should be able to use the name of your members (but with removing & in front of them), that's why .&[WantInReport]. become .&[WantInReport]..
I'd rather use .[Last Days]. than .&[2]., easier to understand when you look later at the query.
Finally using .LastChild gives you last item of your selected branch.

Easiest way to programmatically generate MDX rowcount query?

Right now I'm dealing with a program that can generate and return SQL or MDX queries (depending on the source database of the queries). I'm working on adding a feature that counts all the rows returned by a given query.
Now, I have some small background with SQL, so I was able to parse table names and generate a rowcount. However, MDX is a completely new beast for me.
In SQL, I'm creating:
SELECT
COUNT(SUM)
AS ROWS
FROM
(
COUNT(*) AS COUNT FROM TABLE1
UNION ALL
COUNT(*) AS COUNT FROM TABLE2
UNION ALL
COUNT(*) AS COUNT FROM TABLE3
ETC...
)
Now, what I'm wondering is, how would I do something similar with MDX? I've done some reading on MDX, and from what I gathered the basic notation is
[Dimension].[Hierarchy].[Level]
Now with SQL, I parsed the table names out of a larger generated query and simply inserted them into a new programmatically generated query. What would I have to grab from a larger MDX query to generate my own rowcounting query and sending it off to run? A simpler example of the MDX I'm dealing with would be:
WITH
MEMBER [BUSINESS1].[XQE_RS_CM1] AS '([BUSINESS1].[COMPANY_H].[all])', SOLVE_ORDER = 8
MEMBER [BUSINESS2].[XQE_RS_CM0] AS '([BUSINESS2].[all])', SOLVE_ORDER = 4
SELECT
NON EMPTY {[BUSINESS2].[ALL_TIME_H].[CALENDAR_YEAR_L].MEMBERS AS [XQE_SA1] , HEAD({[BUSINESS2].[XQE_RS_CM0]}, COUNT(HEAD([XQE_SA1]), INCLUDEEMPTY))} DIMENSION PROPERTIES PARENT_LEVEL, PARENT_UNIQUE_NAME ON AXIS(0),
NON EMPTY {[BUSINESS1].[COMPANY_H].[COMPANY_CD__L].MEMBERS AS [XQE_SA0] , HEAD({[BUSINESS1].[XQE_RS_CM1]}, COUNT(HEAD([XQE_SA0]), INCLUDEEMPTY))} DIMENSION PROPERTIES PARENT_LEVEL, PARENT_UNIQUE_NAME ON AXIS(1),
NON EMPTY {[Measures].[Measures].[BUSINESS3]} DIMENSION PROPERTIES PARENT_LEVEL, PARENT_UNIQUE_NAME ON AXIS(2)
FROM
[SOURCE] CELL PROPERTIES CELL_ORDINAL, FORMAT_STRING, VALUE
Any insight would be awesome, thanks.
At first glance your script looks reasonable then after unravelling it becomes a bit(!) more complex.
The main difference between this and other scripts is its use of axis(2). In a sub-select extra dimensions are often used but this is a little odd as most clients can't handle 3 dimensional cellsets - so I'm intrigued by what is consuming this info?
Also the member [BUSINESS1].[XQE_RS_CM1] is a single member as is [BUSINESS2].[XQE_RS_CM0] so what is the point of the sections HEAD... ?
WITH
MEMBER [BUSINESS1].[XQE_RS_CM1] AS
([BUSINESS1].[COMPANY_H].[all]), SOLVE_ORDER = 8
MEMBER [BUSINESS2].[XQE_RS_CM0] AS
([BUSINESS2].[all]), SOLVE_ORDER = 4
SELECT
NON EMPTY
{[BUSINESS2].[ALL_TIME_H].[CALENDAR_YEAR_L].MEMBERS AS [XQE_SA1]
,HEAD(
{[BUSINESS2].[XQE_RS_CM0]},
COUNT(
HEAD([XQE_SA1])
,INCLUDEEMPTY
)
)}
ON AXIS(0),
NON EMPTY
{[BUSINESS1].[COMPANY_H].[COMPANY_CD__L].MEMBERS AS [XQE_SA0]
,HEAD(
{[BUSINESS1].[XQE_RS_CM1]},
COUNT(
HEAD([XQE_SA0])
,INCLUDEEMPTY
)
)}
ON AXIS(1),
NON EMPTY
{
[Measures].[Measures].[BUSINESS3]
}
ON AXIS(2)
FROM
[SOURCE]
Does the following return the same data as the original script?
SELECT
NON EMPTY
{
[BUSINESS2].[ALL_TIME_H].[CALENDAR_YEAR_L].MEMBERS
,[BUSINESS2].[all]
}
ON 0,
NON EMPTY
{
[BUSINESS1].[COMPANY_H].[COMPANY_CD__L].MEMBERS
,[BUSINESS1].[COMPANY_H].[all]
}
ON 1
FROM [SOURCE]
WHERE [Measures].[Measures].[BUSINESS3];
All you need to calculate then is the count of members returned in the following set on the rows:
{
[BUSINESS1].[COMPANY_H].[COMPANY_CD__L].MEMBERS
,[BUSINESS1].[COMPANY_H].[all]
}

MDX - Is it possible to have two unrelated dimension members in one row?

I need to create the table of the following structure in MDX (to be used in SSRS report):
For that I have 2 dimensions and one measure:
Option dimension, with option type and option value attributes
Standard dimension, with IsStandard flag
Price measure
In first column I need to show all option type attributes,
in second all value attributes where IsStandard flag is set to [Y],
in third values chosen by user in parameters and
in fourth prices for components selected by user.
Is it possible to do the above in single MDX? Or will I be better off creating 2 distinct queries and creating 2 tables for them?
EDIT: Since my updates won't fit into the comment, I will add some thoughts here.
EXISTS function from answer below does not filter the result set, I don't get standard values but all possible values concatenated. When I issue the following code:
SELECT
[Measures].[Price] ON 0,
NON EMPTY [Option].[Option Type].children
*
[Option].[Option Value].children ON 1
FROM [Cube]
WHERE
(
[Standard].[IsStandard].&[Y],
[Configurations].[Configuration].&[conf1]
)
It returns the default values correctly, but if I use
SELECT
[Measures].[Price] ON 0,
[Option].[Option Type].children
*
EXISTS(
[Option].[Option Value].[Option Value].members
,([Standard].[IsStandard].&[Y],[Configurations].[Configuration].&[conf1])
) ON 1
FROM [Cube]
It does not filter the results.
If you can accept a slightly different order of columns, then this can be done in MDX, using a calculated measure which is actually a string (as you want to see a list of attributes values in column). This avoids having the same attribute twice in the rows:
WITH Member Measures.[Standard Value] AS
Generate(NonEmpty([Option].[Option Type].[Option Type].Members,
{([Standard].[IsStandard].&[Y],
Measure‌​s.[Price]
)}
),
[Option].[Option value].CurrentMember.Name,
", "
)
SELECT { Measures.[Standard Value], Measures.[Price] }
ON COLUMNS,
NON EMPTY
[Option].[Option Type].[Option Type].Members
*
{ #chosenValues } // the parameters value should be a comma separated list like "[Option].[Option value].[AMD], [Option].[Option value].[INTEL]"
ON ROWS
FROM [Your Cube]
WHERE [Configurations].[Configuration].&[conf1]
You can adapt the list separator (the last argument of the Generate function) to anything you like.
And in case there is more than one measure group that is related to the dimensions [Option], [Standard], and [Configurations], you should add the name of the measure group to use for determining the relationship as additional last parameter to the Exists, so that you and not the engine determines that. Just use the name of the measure group in either single or double quotes.
Yes it is, dimension will just be ignored. This is assuming you've all in the same schema / cube.
Note, depending on the OLAP Server you're using it's possible you've to change a flag that sends an error if you're using a dimensions that is not defined at Measure Group level.

MDX Query - return a member contained in the filter

I have an MDX query that is filtering on a particular member, but I need it to return the actual member value as well.
For example:
SELECT NON EMPTY { [Measures].[__No measures defined] } ON COLUMNS, NON EMPTY { ([Archive].[SiteId].[SteId] }ON ROWS FROM [Model] WHERE ( {[Archive].[SiteId].&[{e7672ff4-7f0c-4806-8453-744a17bde4ca}],[Archive].[SiteId].&[{bb7d8f41-c88a-4bcb-ade8-d0533190185a}],[Archive].[SiteId].&[{04cd27b6-e239-4d27-bc58-27f0a8733193}]} )
so in SQL it would basically be -
Select SiteId from Model where SiteId In .....
However this won't work because it says the SiteId Member is already contained in the filter and so appear twice in the query!
So how can filter on SiteId AND return the SiteId?
Thanks!
I decided to use DAX instead. In fact DAX enabled me to perform some better manipulation given my data that sped up the overall application.