MDX - Use Aggregate on a CROSSJOINed multi-dimensional set to return a grand total row - mdx

I raised a ticket on grand totals when filtering ages ago and I used the proposed solution yesterday to get one dimension working:
WITH
SET [FilteredSet] AS
{
FILTER([CDR].[CDR].Members,([CDR].[CDR].CURRENTMEMBER.MEMBERVALUE="70648") OR ([CDR].[CDR].CURRENTMEMBER.MEMBERVALUE="70646"))
}
MEMBER [CDR].[CDR].[Test] AS Aggregate([FilteredSet])
SELECT NON EMPTY {[Measures].[CS01.TenorTotal] } ON COLUMNS, NON EMPTY {[FilteredSet], [CDR].[CDR].[Test]} ON ROWS FROM [TraderCube] WHERE ([Date].[Date].[2022-04-01],[Epoch].[Epoch].[Branch].[master].[17])
(Other ticket with useful info)
MDX - Grand total missing when filtering
And as I expand the test query (to get it all working and I then expand the C# code to add it) I note that this works:
WITH
SET [FilteredSet] AS
{
FILTER(CROSSJOIN(HIERARCHIZE(DRILLDOWNLEVEL([CDR].[CDR].[ALL].[AllMember]),POST),HIERARCHIZE(DRILLDOWNLEVEL([Book].[Book].[ALL].[AllMember]),POST)),(LEFT([CDR].[CDR].CURRENTMEMBER.MEMBERVALUE,2)="66") )
}
SELECT NON EMPTY {[Measures].[CS01.TenorTotal] } ON COLUMNS, NON EMPTY {[FilteredSet]} ON ROWS FROM [TraderCube] WHERE ([Date].[Date].[2022-04-01],[Epoch].[Epoch].[Branch].[master].[17])
But we will often filter on the 2nd (or 3rd, 4th etc) dimension, like so and the total (AllMember) is filtered out from Book as it does match the INSTR comparisons.
How do I get my Aggregate MEMBER to work properly here? I've placed [?] in position as I can get this sort of setup to work on one dimension (E.g. [CDR] as shown in first code block) but here the query returns [CDR] and then the [Book] members that match and I want to 'glue' my Agregate MEMBER [?] of the measure as a grand total.
WITH
SET [FilteredSet] AS
{
FILTER(CROSSJOIN(HIERARCHIZE(DRILLDOWNLEVEL([CDR].[CDR].[ALL].[AllMember]),POST),HIERARCHIZE(DRILLDOWNLEVEL([Book].[Book].[ALL].[AllMember]),POST)),(LEFT([CDR].[CDR].CURRENTMEMBER.MEMBERVALUE,2)="66")
AND (INSTR([Book].[Book].CURRENTMEMBER.MEMBERVALUE,"72")>0 OR INSTR([Book].[Book].CURRENTMEMBER.MEMBERVALUE,"73")>0))
}
MEMBER [?] AS Aggregate([FilteredSet])
SELECT NON EMPTY {[Measures].[CS01.TenorTotal] } ON COLUMNS, NON EMPTY {[FilteredSet], [?]} ON ROWS FROM [TraderCube] WHERE ([Date].[Date].[2022-04-01],[Epoch].[Epoch].[Branch].[master].[17])
Current MDX error back is:
'The 2 objects do not have the same dimensionality: [[CDR], [Book]] and [[Measures]]'
Thanks to more advanced MDX-ers out there! :)
Leigh Tilley
TilleyTech Ltd
P.S. Sorry no screenshots...client browser trackers would detect it and I'd get in trouble!

Related

MDX Filter based on a Dimension field and Measure field not returning expected result

I'm using SSAS 2019 and have the following MDX query to determine data errors - i.e. in this case where a given score ([Measures].[Score]) is above what the maximum score ([PerformanceLevel].[MaxScore]) is
SELECT NON EMPTY {
[Measures].[FactTestScoreCount]
} ON COLUMNS,
NON EMPTY
{(
[TestEvent].[Key].[Key].Members
)} ON ROWS
FROM [TestScore]
WHERE
(
FILTER(
[PerformanceLevel].[MaxScore].[MaxScore].Members,
[PerformanceLevel].[MaxScore].CurrentMember.MemberValue < [Measures].[Score]
)
)
...you'll note it uses a Measure value to compare against a Dimension value in the filter
However the above query incorrectly returns all rows (it should return zero rows as we have no scores in the current database that are above the maximum score)
I have tried using a HAVING and also tried to add the filter into the FROM however I get the same result. I have also tried converting the data-type to int on both sides of the expression but to no avail. I tried temporarily changing the condition to hard-code (numeric) values and this seems to narrow down the issue as being the [Measures].[Score] in the FILTER - i.e. as far as I can see putting the Measure within the FILTER doesn't seem to be working as expected but I can't work out why
Any ideas would be much appreciated
I didn't fully understand what the first parameter of the Filter function represented - specifically the granularity, otherwise it (depending on your aggregation type will SUM the measure). Also moving the Filter up to the select allows more flexibility when joining/displaying rows - so I came up with the below which works well
SELECT NON EMPTY {
[Measures].[FactTestScoreCount]
} ON COLUMNS,
Filter(
NonEmpty(
{(
[TestEvent].[Key].[Key].members
)}
), [Measures].[Score] > [PerformanceLevel.[MaxScore].CurrentMember.MemberValue
) ON ROWS
FROM [TestScore]

MDX Result Count

I am a beginner in MDX queries. Can any one tell me how to get the record count that is a result of a MDX query?
The query is following:
select {[Measures].[Employee Department History Count],[Measures].[Rate]} on columns, Non Empty{{Filter([Shift].[Shift ID].[Shift ID].Members, ([Shift].[Shift ID].CurrentMember.name <> "1"))}*{[Employee].[Business Entity ID].[Business Entity ID].Members}} on rows from [Adventure Works2012].
I have tried various methods and I haven't really got a solution for that.
I assume you mean row count when you talk of "record count", as MDX does not know a concept of records, but the result shown from an MDX query is the space built by the tuples on the axes.
I see two possibilities to get the row count:
Just count the rows returned from your above query in the tool from which you call the MDX query.
If you want to count in MDX, then let's state what you want to have:
You want to know the number of members of the set of non empty combinations of [Shift ID]s and [Business Entity ID]s where the Shift ID is not "1" and at least one of the measures [Employee Department History Count] and [Rate] is not null.
To state that different: Let's call the tuples like above for which the first measure is not null "SET1", and the tuples like above for which teh second measure is not null "SET2". Then you you want to know the count of the the tuples which are contained in one of these sets (or in both).
To achieve this, we define these two sets and then a calculated menber (a new measure in our case) containing this calculation in its definition, and then use this calculated member in the select clause to show it:
WITH
SET SET1 AS
NonEmpty({{Filter([Shift].[Shift ID].[Shift ID].Members,
([Shift].[Shift ID].CurrentMember.name <> "1"))}
* {[Employee].[Business Entity ID].[Business Entity ID].Members}},
{[Measures].[Employee Department History Count])
SET SET2 AS
NonEmpty({{Filter([Shift].[Shift ID].[Shift ID].Members,
([Shift].[Shift ID].CurrentMember.name <> "1"))}
* {[Employee].[Business Entity ID].[Business Entity ID].Members}},
{[Measures].[Rate])
MEMBER [Measures].[MyCalculation] AS
COUNT(SET1 + SET 2)
SELECT [Measures].[MyCalculation] ON COLUMNS
FROM [Adventure Works2012]
Please note:
The sets SET1 and SET2 are not absolutely necessary, you could also put the whole calculation in one long and complicated definition of the MyCalculation measure, but splitting it up makes is easier to read. However, the definition of a new member is necessary, as in MDX you can only put members on axes (rows, columns, ...). These members can either already been defined in the cube, or you have to define them in the WITH clause of your query. There is no such thing as putting expressions/calculations on axes in MDX, only members.
The + for sets is a union which removes duplicates, hence this operation gives us the tuples which have an non empty value for at least one of the measures. Alternatively, you could have used the Union function equivalently to the +.
The Nonempty() I used in the definitions of the sets is the NonEmpty function, which is slightly different from the NON EMPTY keyword that you can use on the axes. We use one of the measures as second argument to this function in both set definitions.
I have currently no working SSAS installation available to test my statement, hence there might be a minor error or typo in my above statement, but the idea should work.

icCube MDX on distinct count in relation to same dimension in axis or filter

I have the following MDX statement on the $Monitoring cube:
WITH
MEMBER [Measures].[Unique Users] AS distinctcount(([User].[User].[User L].members
, [Measures].[Open Report Count])),format_string="#,#0"
SELECT
NON EMPTY { {[Measures].[Unique Users],[Measures].[Open Report Count]} } ON COLUMNS,
NON EMPTY { [Time].[Time].[Day L] } ON ROWS
FROM ( SELECT
{ lag(strtomember("[Time].[Time].["+right("0"+str(day(SchemaRefreshTime())),2) +"-"+ right("0"+str(month(SchemaRefreshTime())),2) + "-"+str(year(SchemaRefreshTime()))+ "]"), 6):strtomember("[Time].[Time].["+right("0"+str(day(SchemaRefreshTime())),2) +"-"+ right("0"+str(month(SchemaRefreshTime())),2) + "-"+str(year(SchemaRefreshTime()))+ "]") } ON 0 FROM [$Monitoring])
/*ic3navigation*/
axis 1 NON EMPTY order(nonempty(Descendants([Report].[Report], ,leaves),[Open Report Count]),[Open Report Count],desc)
FILTERBY /*ic3*/ {[Time].[Time].[ALL].&[2015].&[2015-11-27].&[27-11-2015]}
FILTERBY /*ic3*/ {[User].[User].[All Users].&[<user>]}
*) change <user> with the actual user name
*) the ...lag.. formula is used to give the last 7 days based on schema refresh time
***) this MDX query can be run on any $Monitoring cube if you have filled in an existing user
I would expect the distinctcount function to take into account the FILTERBY. So the result should be 1 (there is just one user selected). The strange thing is, that it does not. It shows more than one user, so I assume the FILTERBY on users is not taken into account for the distinctcount.
The same thing happens when I move the FILTER BY to the AXIS or to the ROWS or COLUMNS.
Is this a bug or is this something how MDX/ MDX++ works in icCube?
Please advise.
It's the expected behaviour. Welcome to advanced MDX !
A FilterBy is exactly the same as a sub-select.
Members are not filtered by a subselect or a where clause in a calculated members.
In a calculated member, a tuple that defines a hierarchy will 'overwrite' the one defined in a subquery or where clause.
------ UPDATE ------
If you want to filter the set with the where clause/subselect you've the EXISTING operator.
MEMBER [Measures].[Unique Users] AS count( Existing [User].[User].[User L].members),format_string="#,#0"
if you want only users with data for the cell tuple :
MEMBER [Measures].[Unique Users] AS count( nonempty( Existing [User].[User].[User L].members, [Measures].[Open Report Count])),format_string="#,#0"
If you've a large number of users I'd advise adding a measure [Distinct Users] that is a distinct count on the user id. This way you avoid all complexity that we're facing here.

MDX Query, how to select first and last year of an event's period

I have a publications datamart from teachers of my university, I would like to select the list of teachers with how much books they wrote and in period while them publicated the books.
Example
Teacher - Last Year of pubblication - Number of Books
Teacher A - 2014 - 200 books
I tried to do something like:
WITH MEMBER [Measures].[LastYear] AS
'(ClosingPeriod([Anno].[Anno])
,[Autore].[Nome].CurrentMember)'
SELECT
{[Measures].[Unita (Libri)],[Measures].[LastYear]} ON COLUMNS,
NON EMPTY
[Autore].[Nome].Members ON ROWS
FROM [Pubblicazioni]
but the field last year is alway void, if i try to change the second part of ClosingPeriod with [Measures].[Unita (libri)] it is empty too... How should i implement that query? I'm a bit confused about
EDIT
select [Measures].[Libri] ON COLUMNS,
crossjoin([Autore.default].[Nome].[Fazzinga, Bettina], tail(NonEmptyCrossJoin([Autore.default].[Nome].[Fazzinga, Bettina], [Anno.default].[Anno].Members), 1).Item(0).Item(1)) ON ROWS
from [Pubblicazioni]
This one is working great for the teacher [Fazzinga, Bettina], but the query stops to work while i doing
select [Measures].[Libri] ON COLUMNS,
crossjoin([Autore.default].[Nome].Members, tail(NonEmptyCrossJoin([Autore.default].[Nome].CurrentMember, [Anno.default].[Anno].Members), 1).Item(0).Item(1)) ON ROWS
from [Pubblicazioni]
Measures needs some form of value or string to return, I think that is why your measure is returning void as ClosingPeriod is returning a member rather than value.
The following is not tested:
WITH MEMBER [Measures].[LastYear] AS
'(Tail(
nonempty(
[Anno].[Anno]
,([Autore].[Nome].CurrentMember, [Measures].[Unita (Libri)])
)
).item(0).item(0).MEMBERVALUE)'
SELECT
{[Measures].[Unita (Libri)],[Measures].[LastYear]} ON COLUMNS,
NON EMPTY
[Autore].[Nome].Members ON ROWS
FROM [Pubblicazioni]
Try this one instead:
SELECT
[Measures].[Libri] ON COLUMNS
,Generate
(
[Autore.default].[Nome].MEMBERS
,Tail
(
//NonEmptyCrossJoin <<hopefully nonempty is enough
NonEmpty
(
[Autore.default].[Nome].CurrentMember * [Anno.default].[Anno].MEMBERS
)
,1
) //.Item(0).Item(1) //<<don't believe this is required
) ON ROWS
FROM [Pubblicazioni];
I finally found a way to get it... Natively, every calculated member in the clause "WITH MEMBER" are treated as information of cube. In my case, if i do max("xpression of years when author worked") it will give the number of books he wrote in the relative max year. To avoid this, there is a construct called SetToStr() that will print the year as a String (something like [Hyerarchie].[Level].[Value]). The string can simple cutted with left() and right() condition. Here is the result:
WITH MEMBER [Measures].[Anno prima pubblicazione] AS 'left(right(settostr(head(extract(nonemptycrossjoin([Anno].[Anno].Members, nonemptycrossjoin([Libro].[Titolo].Members,[Autore].[Nome].CurrentMember)),[Anno]),1)),6),4)'
MEMBER [Measures].[Anno ultima pubblicazione] AS 'left(right(settostr(tail(extract(nonemptycrossjoin([Anno].[Anno].Members, nonemptycrossjoin([Libro].[Titolo].Members,[Autore].[Nome].CurrentMember)),[Anno]),1)),6),4)'
SELECT {[Measures].[Anno prima pubblicazione],[Measures].[Anno ultima pubblicazione]} ON COLUMNS,
[Autore].[Nome].Members ON ROWS
FROM [Pubblicazioni]
This query work in this way:
1) extract books for the current author with nonemptycrossjoin([Libro].[Titolo].Members,[Autore].[Nome].CurrentMember) so i get the tuple ;
2) I let the year join the previous tuple with nonemptycrossjoin([Anno].[Anno].Members,);
3) In my case the DM is ordered yet for year, in other case we should use even the function order;
4) we don'n need all that things, so we trash what we doesn't need using extract(,[Anno]), this mean i will maintain only the values of hierarchy [Anno];
5) we just need the first entry of the ordered list of years and we use head() function;
6) we can't use the value yet or MDX will count entries in the fact table for the value of [Anno] we selected, so we transform it as string with setToStr();
7) the value is still dirty, cause the string has hiearchy too, so we clean it using left() and right() function (a year is always composed by 4 chars).
It is a little big function, but this is the only one worked for me :)

Last Available value MDX

I have a requirement where in i am to extract data from a cube, within the SSRS dataset using the query builder ,with the time dimension in the result set, across a range of dates. The conditions are
The measures are to be displayed for each day of the date range.
The sub total row should have the last available measures value for that time range.
There is a time filter (currently a single date filter with a multi select option).
my MDX is as below.
The measure has a 'Sum' as the aggregation type.
I have a calculated measure with the scope defined as below.
SCOPE([MEASURES].[Measure1]);
SCOPE([Date].[Date].MEMBERS);
THIS = TAIL(EXISTING ([Date].[Date].MEMBERS),1).ITEM(0) ;
END SCOPE;
END SCOPE;
This above scope statement works perfectly. however, when i select in more that one date member this query slows WAYYYYYYY down. Performance numbers are
Across 1 date - 4 seconds
Across 2 dates - 22 minutes
Across 3 dates - unknown (in Hours)
This drastic degradation in performance goes away if i remove the scope statement, which makes me thing that there should be a better way to do the same. the final report query is as below.
SELECT
NON EMPTY
{[Measures].[Measure1]} ON COLUMNS
,NON EMPTY
{ [Dimension1].[Dimension1].[Dimension1].ALLMEMBERS*
[Dimension2].[Dimension2].[Dimension2].ALLMEMBERS*
[Dimension3].[Dimension3].[Dimension3].ALLMEMBERS*
[Date].[Date].[Date].ALLMEMBERS
} ON ROWS
FROM (
SELECT {[Date].[Date].&[2014-06-13T00:00:00]
,[Date].[Date].&[2014-06-16T00:00:00] } ON COLUMNS
FROM [Cube]
)
So the question again is, Is there a way to do the last available value part of the scope statement so as to have a better performance? Also, if there is another way to write the final mdx that would help the performance?.
Please let me know if there are anythings unclear regarding the question.
Thanks
Srikanth
The first optimization step would be to change your query to
SELECT
NON EMPTY
{[Measures].[Measure1]} ON COLUMNS
,NON EMPTY
{ [Dimension1].[Dimension1].[Dimension1].ALLMEMBERS*
[Dimension2].[Dimension2].[Dimension2].ALLMEMBERS*
[Dimension3].[Dimension3].[Dimension3].ALLMEMBERS*
{[Date].[Date].&[2014-06-13T00:00:00], [Date].[Date].&[2014-06-16T00:00:00] }
} ON ROWS
FROM [Cube]
Furthermore, I am not sure why you added the SCOPE([Date].[Date].MEMEBER); (probably Date].[Date].MEMBERS, actually). Maybe it helps to omit it and the corresponding END SCOPE.