How To Get All Items Created or Still Open For A Given Time - ssas

I am working with a system were items are created (postDate dimension) and closed (endDate dimension). The endDate column is always populated with the last time the item was seen. An item is considered closed in a certain time if its last seen date is before the date you are querying. Each row in the fact table has the item, postDate, endDate, locationID, and some other dimensions used for aggregations. What I am trying to accomplish is getting all items still active for a given time frame. For example I want to know all items posted in November 2008 or before November 2008 that has not yet closed. In SQL it would look something like:
SELECT C.geoCountyArea,TM.CalendarYear,COUNT(DISTINCT a.itemid)
FROM [dbo].[factTable] a
JOIN dbo.dimDate AS TM
ON TM.DateKey BETWEEN postDate AND endDate
JOIN [dbo].[dim_geography] C
ON A.geographyID=C.geographyID
WHERE C.geoCountyArea = '1204000057'
AND TM.CalendarYear = 2008 AND TM.MonthNumberOfYear = 11
GROUP BY C.geoCountyArea,TM.CalendarYear
ORDER BY C.geoCountyArea,TM.CalendarYear
This returns 27,715 which is expected. Now, in MDX this looks like:
WITH MEMBER Measures.[itemCount] AS
AGGREGATE(
{NULL:[PostDate].[Month Name].&[2008]&[11]} * {[EndDate].[Month Name].&[2008]&[11]:NULL},
[Measures].[Fact_itemCount]
)
SELECT NON EMPTY (
Measures.[itemCount]
) ON 0,
NON EMPTY (
{[PostDate].[Month Name].&[2008]&[11]},
{[Geography].[Geo County Area].&[1204000057]}
)ON 1
FROM [Cube];
This returns 27,717 - which is 2 more than the SQL version that could be due to items with no end Date posted. Now, the complication comes when I want to get more than one explicit time - for example item count for all months in 2008 or item count for all years. I looked up methods to link a given param to another one via roll playing dimensions and came across this link. I altered my script so it looks like:
WITH MEMBER Measures.[itemCount] AS
AGGREGATE(
{NULL:LINKMEMBER([DATE].[Calendar].CURRENTMEMBER
,[PostDate].[Calendar])}
* {LINKMEMBER([DATE].[Calendar].CURRENTMEMBER
, [EndDate].[Calendar]):NULL}
, [Measures].[Fact_itemCount]
)
SELECT {Measures.[jobCount]} ON 0,
NON EMPTY (
{[DATE].[Month Name].&[2008]&[11]},
{[Geography].[Geo County Area].&[1204000057]}
)ON 1
FROM [Cube];
This, however, returns only the items created in November 2008 - value of 14,884. If I add in other months I do get individual counts for each month but, again, these are just the items created in those months.
How do I get the "active" item count for a given month/year/quarter without having do explicitly declare the time values in the AGGREGATE?

Can you use NonEmpty?
WITH MEMBER Measures.[itemCount] AS
AGGREGATE(
{NULL:
NONEMPTY(
[PostDate].[Month Name].MEMBERS //<<AMEND TO EXACT STRUCTURE USED IN YOUR CUBE
,[DATE].[Calendar].CURRENTMEMBER
).ITEM(0).ITEM(0)}
* {NONEMPTY(
[EndDate].[Month Name].MEMBERS //<<AMEND TO EXACT STRUCTURE USED IN YOUR CUBE
,[DATE].[Calendar].CURRENTMEMBER
).ITEM(0).ITEM(0): NULL}
, [Measures].[Fact_itemCount]
)
...

This ended up being the solution that provided valid results (tested against SQL calls against the warehouse tables):
WITH MEMBER Measures.[itemCount] AS
AGGREGATE(
{NULL:LINKMEMBER([Post Date].[Calendar],
[Post Date].[Calendar])}
* {LINKMEMBER([Post Date].[Calendar],
[End Date].[Calendar]):NULL},
[Measures].[Fact_itemCount]
)
SELECT {Measures.[itemCount]} ON 0,
NON EMPTY (
{[Post Date].[Month Name].Children},
{[Geography].[Geo County Area].&[1204000057]}
)
FROM [Cube]
Not that I am doing LINKMEMBER against the post and end dates - not against the global Date measure.

Related

TopCount filtered on a date dimension value

I'm attempting to get the top 10 countries by value from any measures where the discount rate is -20, and for a specific date. I've created a simple dynamic set defined as:
CREATE DYNAMIC SET CURRENTCUBE.[Top10CountryBy20Disc]
AS
TopCount
( [Customer].[Country].members,
10, [Discount].[Discount].&[-20%])
;
This gives me the top 10 customers summed by the overall measure value, but I need to filter to a specific date. The overall top 10 won't be the same as the top 10 on a particular date, so I need to include some date context. Currently the numbers I get back for each country are correct, but it is the list of countries (the top 10 list) that is incorrect.
I use the set [Latest] listed below in a lot of other reports, and this works just fine, but am struggling to combine this when using the above set.
CREATE DYNAMIC SET CURRENTCUBE.[Latest]
AS FILTER(
[Date].[Date Key].[Date Key].members, IsDate([Date].[Date Key].currentmember.membervalue)
AND (
DateDiff("d", [Date].[Date Key].currentmember.membervalue, IIF(Weekday(Now()) = 2, 3, 1)
);
Logically I am trying to do something along the lines of the below, which is invalid syntax:
TopCount
( [Customer].[Country].members,
10, ([Discount].[Discount].&[-20%], [Latest]))
;
Would be grateful for some suggestions/direction on this.
This approach solved my issue:
GENERATE ( [Latest],
{TopCount
( descendants([Customer].[Country],,AFTER),
10,
VBA!Abs(([Discount].[Discount].&[-20%], ([Measures].[MeasureName],[Date].[Date Key].currentmember)))
)
}
);

Number or Results and WITH MEMBER Calculation interacting weird

In our MDX Queries, we use a template like the following WITH MEMBER . SELECT SUBSET . FROM (SELECT . FROM .) a lot.
What this particular query should do (and does!) is list all product variants matching a filter, and give the sales amount for it for a month in time. On the same row, we have the sales total of all variants of this product.
We need only a subset of the result (for paging), and the results need to ordered to allow paging of course. Then there is a non empty, so only product variants that were sold in the time period qualify.
This works just fine. Paging also works: putting 30, 60, 90 for Start in SUBSET is gives the correct results.
However, if I put a 1 as Count (SUBSET(..., x, 1)) the result of the "Price all" goes to (null) for each and any row I select with x. If I return two or more rows it works just fine, and all rows have their correct values.
Two results and the correct value in the rightmost column (in case you are wondering how the total of all variants, 67, can be lower than the sales of one particular variant (280): I picked a bad example - there was a return (so a negative sale) for another variant of this product, for 213. The 67 is correct and desired!)
One Row and a (null) result
I do not understand how or why the SUBSET count would interfere with the Tuple I select in the WITH MEMBER. If it does it for 30 rows at a time, why not for one?
*edit
I just tried, what would happen if I filter in a way that only one result matches (and SUBSET paging is set to "working values" of 0, 30): The same as in the 2nd screenshot, for one result row the WITH MEMBER evaluates to (null).
So it is not that SUBSET is the problem, but more general: When there is only one result row!
Could this be a bug in our SQL Server Version? It is SQL 2012 with SP4 and all latest security patches.
WITH
MEMBER [DIM Date].[HI Year_Month_Day].[CURRENT_MONTH] AS
[DIM Date].[HI Year_Month_Day].&[20201215].Parent /* Select Month of December */
MEMBER [MEASURES].[Price all] AS (
(
[DIM Products].[HI Products].Currentmember.Parent
, [DIM Products].[HI Subproduct].[Total]
)
, [MEASURES].[Price]
), FORMAT_STRING = '#,0' /* Get the parent product total next to every subproduct */
SELECT {
([DIM Datum].[HI Year_Month_Day].[CURRENT_MONTH], [MEASURES].[Price])
, ([DIM Datum].[HI Year_Month_Day].[CURRENT_MONTH], [MEASURES].[Price All])
} ON COLUMNS
, {
SUBSET(
ORDER(
NONEMPTY(
([DIM Products].[HI Produt Subproduct].[Subproduct])
, {([DIM Date].[HI Year_Month_Day].[CURRENT_MONTH], [MEASURES].[Price])}
)
, ([DIM Date].[HI Year_Month_Day].[CURRENT_MONTH], [MEASURES].[Price])
, BDESC
)
, 0
, 30
)
} ON ROWS
FROM (
SELECT
{[DIM StoreLocation].[HI Country].[Country].&[US]}
* {[DIM Productgroup].[HI Productgroup].[Productgroup].&[Bikes]}
ON COLUMNS
FROM [Cube]
)

MDX: Linkmember > Descendants offsets with currentmember context

I'm self taught in MDX and am struggling with a problem.
I need to be able to isolate a set of products according to a window around their release date and then calculate their sales by the current rows date context. The 'window' is a set of streetdates where member is between or equal to currentmember -27 and currentmember + 7 (as we sometimes see early sales/preshipping).
Final output should look something like (forward days blank):
NR Sales
2018-03-25 117.12
2018-03-26 130.01
2018-03-27 2,827.63
2018-03-28
2018-03-29
2018-03-30
2018-03-31
My cube has a street/release date hierarchy which is a mirror of my fiscal date hierarchy. It pulls correct values with a slightly altered query looking at a single day in the where clause.
The below query returns values, but is slightly incorrect as i think the descendants call isn't working properly.
WITH
SET [StreetDateSet] AS
LinkMember
(
Descendants
(
[Date].[Yr-Qtr-Mo-Wk-Day].CurrentMember
,[Date].[Yr-Qtr-Mo-Wk-Day].[Day]
).Item(0)
,[Street Date].[StreetDate-Yr-Mo-Wk-Dy]
).Lag(27)
:
LinkMember
(
Descendants
(
[Date].[Yr-Qtr-Mo-Wk-Day].CurrentMember
,[Date].[Yr-Qtr-Mo-Wk-Day].[Day]
).Item(0)
,[Street Date].[StreetDate-Yr-Mo-Wk-Dy]
).Lag(-7)
MEMBER [NR Sales] AS
Sum
(
[StreetDateSet]
,[Measures].[Sales Value]
)
SELECT
NON EMPTY
[NR Sales] ON 0
,[Date].[Date].Children ON 1
FROM [CUBE]
WHERE
(
[Date].[Yr-Qtr-Mo-Wk-Day].[Week].&[2018]&[2]&[4]
,[Item].[New Used].[New]
,{
[Item].[Category Code].[220]
,[Item].[Category Code].[230]
,[Item].[Category Code].[210]
}
);
I suspect there's a few problems here but am not sure how to address them. Descendants is likely impacted by the date hierarchy in the where clause. I've tried to get it into the 1 axis, but i get 'The Yr-Qtr-Mo-Wk-Day hierarchy already appears in the Axis1 axis.' and am unsure how to address.
Thanks for any advice/guidance on where i'm going wrong.
Turns out that linkmember wasn't required and ended up finding a solution via MSDN that uses a filter/Properties function:
Set iteration in calculation
Solution involves a member which iterates a NULL * CurrentMember defined set and I slightly adopted the statement to generate a dynamic set which matches off the complementary hierarchies.
WITH
MEMBER [Measures].[SameDay] AS
NULL * Count([Date].[Yr-Mo-Wk-Day].CurrentMember AS MySet)
+
Sum
(
{
Filter
(
[Street Date].[StreetDate-Yr-Mo-Wk-Dy].MEMBERS AS MySet2
,
MySet2.Current.Properties("Key") = MySet.Item(0).Properties("Key")
).Item(0).Lag(28)
:
Filter
(
[Street Date].[StreetDate-Yr-Mo-Wk-Dy].MEMBERS AS MySet2
,
MySet2.Current.Properties("Key") = MySet.Item(0).Properties("Key")
).Item(0).Lead(7)
}
,[Measures].[Sales Value]
)

Calculated SSAS Member based on multiple dimension attributes

I'm attempting to create a new Calculated Measure that is based on 2 different attributes. I can query the data directly to see that the values are there, but when I create the Calculated Member, it always returns null.
Here is what I have so far:
CREATE MEMBER CURRENTCUBE.[Measures].[Absorption]
AS sum
(
Filter([Expense].MEMBERS, [Expense].[Amount Category] = "OS"
AND ([Expense].[Account Number] >= 51000
AND [Expense].[Account Number] < 52000))
,
[Measures].[Amount - Expense]
),
VISIBLE = 1 , ASSOCIATED_MEASURE_GROUP = 'Expense';
Ultimately, I need to repeat this same pattern many times. A particular accounting "type" (Absorption, Selling & Marketing, Adminstrative, R&D, etc.) is based on a combination of the Category and a range of Account Numbers.
I've tried several combinations of Sum, Aggregate, Filter, IIF, etc. with no luck, the value is always null.
However, if I don't use Filter and just create a Tuple with 2 values, it does give me the data I'd expect, like this:
CREATE MEMBER CURRENTCUBE.[Measures].[Absorption]
AS sum
(
{( [Expense].[Amount Category].&[OS], [Expense].[Account Number].&[51400] )}
,
[Measures].[Amount - Expense]
),
VISIBLE = 1 , ASSOCIATED_MEASURE_GROUP = 'Expense';
But, I need to specify multiple account numbers, not just one.
In general, you should only use the FILTER function when you need to filter your fact table based on the value of some measure (for instance, all Sales Orders where Sales Amount > 10.000). It is not intended to filter members based on dimension properties (although it could probably work, but the performance would likely suffer).
If you want to filter by members of one or more dimension attributes, use tuples and sets to express the filtering:
CREATE MEMBER CURRENTCUBE.[Measures].[Absorption]
AS
Sum(
{[Expense].[Account Number].&[51000]:[Expense].[Account Number].&[52000].lag(1)} *
[Expense].[Amount Category].&[OS],
[Measures].[Amount - Expense]
),
VISIBLE = 1 , ASSOCIATED_MEASURE_GROUP = 'Expense';
Here, I've used the range operator : to construct a set consisting of all [Account Number] members greater than or equal to 51000 and less than 52000. I then cross-join * this set with the relevant [Amount Category] attribute, to get the relevant set of members that I want to sum my measure over.
Note that this only works if you actually have a member with the account number 51000 and 52000 in your Expense dimension (see comments).
An entirely different approach, would be to perform this logic in your ETL process. For example you could have a table of account-number ranges that map to a particular accounting type (Absorption, Selling & Marketing, etc.). You could then add a new attribute to your Expense-dimension, holding the accounting type for each account, and populate it using dynamic SQL and the aforementioned mapping table.
I don't go near cube scripts but do you not need to create some context via the currentmember function and also return some values for correct evaluation against the inequality operators (e.g.>) via the use of say the membervalue function ?
CREATE MEMBER CURRENTCUBE.[Measures].[Absorption]
AS sum
(
[Expense].[Amount Category].&[OS]
*
Filter(
[Expense].[Account Number].MEMBERS,
[Expense].[Account Number].currentmember.membervalue >= 51000
AND
[Expense].[Account Number].currentmember.membervalue < 52000
)
,
[Measures].[Amount - Expense]
),
VISIBLE = 1 , ASSOCIATED_MEASURE_GROUP = 'Expense';
EDIT
Dan has used the range operator :. Please make sure your hierarchy is ordered correctly and that the members you use with this operator actually exist. If they do not exist then they will be evaluated as null:
Against the AdvWks cube:
SELECT
{} ON 0
,{
[Date].[Calendar].[Month].&[2008]&[4]
:
[Date].[Calendar].[Month].&[2009]&[2]
} ON 1
FROM [Adventure Works];
Returns the following:
If the left hand member does not exist in the cube then it is evaluated as null and therefore open ended on that side:
SELECT
{} ON 0
,{
[Date].[Calendar].[Month].&[2008]&[4]
:
[Date].[Calendar].[Month].&[1066]&[2] //<<year 1066 obviously not in our cube
} ON 1
FROM [Adventure Works];
Returns:

How to filter a measure based on another measure

I have a dimension table called DimUser that has 1 row per user. I have a measure based on this dimension called "USER COUNT" which is count of rows of the DimUser table. I also have a calculated measure called "ACTIVE DAYS" which returns no of days a user is active.
Now, I want to create another calculated measure based on this 2 measures, where I count only users who are active more than 5 days. I have total 5 users in my user table and 2 users in my fact table who are active more than 5 days. My MDX expression should return 2.
This is what I wrote
FILTER([Measures].[USER COUNT], [Measures].[ACTIVE DAYS] > 5)
But this gives me 5 as the answer instead of 2. What am I doing wrong?
Next I tried this, but this fails compilation saying two measures in the expression are not allowed
([Measures].[USER COUNT], [Measures].[ACTIVE DAYS] > 5)
Next, I tried creating a new calculated member called [IsActive] that returns true or false if active days > 5 and then use that as filter on the [Measures].[USER COUNT].
How do I go about doing this?
First of all, I don't think it is even possible to filter one measure on another measure, because measures are numeric values, not a set. The concept of filtering applies to a set. So filtering a measure based on another measure is absurd. What can be done though, in terms of MDX, is to create a set of users with the first filter applied(i.e. [Measures].[ACTIVE DAYS] > 5) and then getting a COUNT out of it.
Something like this(NOT TESTED):
WITH SET [ActiveUsersMoreThan5Days] AS
FILTER([User].[UserId].CHILDREN, [Measures].[ACTIVE DAYS] > 5)
//Assuming the User dimension and hierarchy here..
//Fill it in with actual..
MEMBER [Measures].CountActiveUsersMoreThan5Days AS
COUNT(NONEMPTY([ActiveUsersMoreThan5Days], [Measures].[<<Some other measure>>]))
SELECT [Measures].CountActiveUsersMoreThan5Days on 0
//,[Dimension].Hierarchy.CHILDREN ON 1
FROM [Cube]
To avoid using Filter which is iterative and not the best performing function. Also to create a measure that is context aware then the following might help:
WITH
MEMBER [Measures].[CntUsersActive5DaysOrMore] AS
Sum
(
[UserId].[UserId] //<<< or [UserId].[UserId].[UserId]
,IIF
(
[Measures].[ACTIVE DAYS] > 5
,1
,0
)
)
SELECT
[Measures].[CntUsersActive5DaysOrMore] ON 0
FROM [YourCube];