I have the following problem. If I query values with a keyfigure which is a function I can't specify multiple values of the same dimension restriction, but if it is not a function it works.
So this works:
SELECT {[Measures].[Netto]} on columns FROM TDC where
({NonEmpty([Time].[Month].[Month].&[2008-03-01T00:00:00]),
NonEmpty([Time].[Month].[Month].&[2008-04-01T00:00:00])})
But this doesn't:
SELECT {[Measures].[CalculatedFunction]} on columns FROM TDC where
({NonEmpty([Time].[Month].[Month].&[2008-03-01T00:00:00]),
NonEmpty([Time].[Month].[Month].&[2008-04-01T00:00:00])})
And this also works:
SELECT {[Measures].[CalculatedFunction]} on columns FROM TDC where
({NonEmpty([Time].[Month].[Month].&[2008-03-01T00:00:00])})
I guess the solution is something like adding the where clause to the header but I really like this solution because it's so simple.
The Calucated function is:
CREATE MEMBER CURRENTCUBE.[MEASURES].Ultimo
AS (iif ((not [Time].[Year - Month - Date].currentmember is [Time].[Year - Month - Date].defaultmember),
IIF(NOT ([Measures].[LagerStk] = 0),
Sum([Time].[Year - Month - Date].[Date].members(0):
ClosingPeriod([Time].[Year - Month - Date].[Date]),
[Measures].[LagerStk]), NULL)
,
IIF(NOT ([Measures].[LagerStk] = 0),
Sum([Time].[Year - Week - Date].[Date].members(0):
ClosingPeriod([Time].[Year - Week - Date].[Date]),
[Measures].[LagerStk]), NULL))),
VISIBLE = 1;
The code is inspired from this and modified for two hierarchies in the time dimension: http://www.sqlserveranalysisservices.com/OLAPPapers/InventoryManagement%20in%20AS2005v2.htm
This is on SQL server 2005 Enterprise edition.
Ok, this works:
WITH MEMBER [Time].[Month].a AGGREGATE
({[Time].[Month].[Month].&[2008-03-01T00:00:00],
[Time].[Month].[Month].&[2008-04-01T00:00:00]})
SELECT {[Measures].[CalculatedFunction]} on columns FROM TDC where a
The problem is in your calculated measure. You are using .CurrentMember and ClosingPeriod without a specific member reference which implies a call to .CurrentMember. When you have set in the WHERE clause there is no "Current" member - there are multiple current members. Re-writting your MDX to something like the following should allow it to work with multiple members in the WHERE clause.
CREATE
MEMBER CURRENTCUBE.[MEASURES].Ultimo AS NULL;
SCOPE ([MEASURES].Ultimo);
SCOPE ([Time].[Year - Month - Date].[All]);
this = IIF
(
(NOT
[Measures].[LagerStk] = 0)
,Sum
(
NULL:Tail(Existing [Time].[Year - Week - Date].[Date],1).item(0).item(0)
,[Measures].[LagerStk]
)
,NULL
);
END SCOPE;
SCOPE ([Time].[Year - Week - Date].[All]);
this = IIF
(
(NOT
[Measures].[LagerStk] = 0)
,Sum
(
NULL:Tail(Existing [Time].[Year - Month - Date].[Date],1).Item(0).Item(0)
,[Measures].[LagerStk]
)
,NULL
)
);
END SCOPE;
END SCOPE;
I am using SCOPE on the All members of the two dimensions, this should be fast that the out IIF and will also avoid one reference to .CurrentMember. I then replaced the ClosingPeriod() call with Tail(Existing [Time].[Year - Week - Date].[Date],1).item(0).item(0) what this does is to get the set of date members that exist in the current context, the Tail() call then gets the last on of these as a single member set and the .Item(0).Item(0) calls get the first member from the first tuple of that set.
Obviously, not having access to your cube, I can't test any of this. The issue you reported in your comment could relate to either an incorrect reference to the All members (I may have a different naming format to the one in your cube) or it may be related to the IIF() statement. I'm not sure that the check for 0 is being evaluated in the correct context
You could try testing without the IIF()
CREATE
MEMBER CURRENTCUBE.[MEASURES].Ultimo AS NULL;
SCOPE ([MEASURES].Ultimo);
SCOPE ([Time].[Year - Month - Date].[All]);
this = Sum
(
NULL:Tail(Existing [Time].[Year - Week - Date].[Date],1).item(0).item(0)
,[Measures].[LagerStk]
);
END SCOPE;
SCOPE ([Time].[Year - Week - Date].[All]);
this = Sum
(
NULL:Tail(Existing [Time].[Year - Month - Date].[Date],1).Item(0).Item(0)
,[Measures].[LagerStk]
);
END SCOPE;
END SCOPE;
Related
How can I filter using values from two dimension in MDX?
The required result should include records where [Purchase Date].[Date] is before today minus number of years from [Accounting Date].[Year]. So, the result should include YoY records from today based on [Purchase Date].[Date] for each [Accounting Date].[Year] member.
I would like something like the following:
SELECT NON EMPTY [Measures].[Amount] ON 0,
NON EMPTY [Accounting Date].[Year].[Year].ALLMEMBERS ON 1
FROM [Tabular_Model]
WHERE (
NULL :
STRTOMEMBER("[Purchase Date].[Date].&["+ Format(DateAdd("YYYY", [Accounting Date].[Year].CURRENTMEMBER.MEMBER_VALUE - 2020, Now()),"yyyy-MM-ddT00:00:00") + "]")
)
But it fails with error: Execution of the managed stored procedure DateAdd failed with the following error: Microsoft::AnalysisServices::AdomdServer::AdomdException.
The syntax for 'All' is incorrect. (All).
Why CURRENTMEMBER.MEMBER_VALUE works for HAVING but not in my WHERE clause? What is the right way?
Try the following measure and query:
WITH
MEMBER [Measures].[Trailing Amount] as SUM({NULL :
STRTOMEMBER("[Purchase Date].[Date].&["+ Format(DateAdd("YYYY", [Accounting Date].[Year].CURRENTMEMBER.MEMBER_VALUE - 2020, Now()),"yyyy-MM-ddT00:00:00") + "]")}, [Measures].[Amount])
SELECT [Measures].[Trailing Amount] ON 0,
NON EMPTY [Accounting Date].[Year].[Year].MEMBERS ON 1
FROM [Tabular_Model]
If MDX doesn't perform as well as you hope, then you might consider adding the following DAX measure into your Tabular model. The following DAX query illustrates how to use it, but if you put this DAX measure into your model, you can query it with MDX queries and it should likely perform better than an MDX calculation:
define
measure 'Your Table Name Here'[Trailing Sales] =
VAR YearOffset = SELECTEDVALUE('Accounting Date'[Year]) - 2020
VAR NowDate = NOW()
VAR EndDate = DATE(YEAR(NowDate)+YearOffset,MONTH(NowDate),DAY(NowDate))
RETURN CALCULATE([Amount], 'Purchase Date'[Date] <= EndDate)
evaluate ADDCOLUMNS(ALL('Accounting Date'[Year]),"Trailing Sales",[Trailing Sales])
I already posted this problem many times, but unfortunately nobody could understand, I m sorry for my poor english :(
I reformulate ...
I have following fact table
I want to get records that match with a particular date and day of week (JOUR) in all dates range (DATE_DEB, DATE_FIN)
I can do that in SQL like this:
SELECT DATE_DEB,
DATE_FIN,
ID_HOR,
to_char(HR_DEB,'hh24:mi:ss') as HR_DEB,
to_char(HR_FIN,'hh24:mi:ss') as HR_FIN,
JOUR
FROM GRP_HOR HOR, GRP
WHERE GRP.ID_ACTIV_GRP = HOR.ID_ACTIV_GRP
AND TO_DATE('1998-01-08', 'YYYY-MM-DD') between DATE_DEB and DATE_FIN
AND 1 + TRUNC(TO_DATE('1998-01-08', 'YYYY-MM-DD')) - TRUNC(TO_DATE('1998-01-08', 'YYYY-MM-DD'), 'IW') = JOUR
So, I'll get 29 records (see below) which included in each range and match with the day of week (JOUR ), after that I want to enlarge them by hours (HR_DEB, HR_FIN).
The problem is, what's how the best way to do this ?
Create 2 date dimension and link them with DATE_DEB, DATE_FIN.
Create 2 Time dimension and link them with HR_DEB, HR_FIN.
How can I implement the between SQL clause in MDX ? Or geater than or Less than ?
Thank you in advance.
OUTPUT :
To specify a range of dates in MDX you just use the colon operator :
Here is the documentation on MSDN: https://learn.microsoft.com/en-us/sql/mdx/range-mdx?view=sql-server-2017
This is the example they give:
With Member [Measures].[Freight Per Customer] as
(
[Measures].[Internet Freight Cost]
/
[Measures].[Customer Count]
)
SELECT
{[Ship Date].[Calendar].[Month].&[2004]&[1] : [Ship Date].[Calendar].[Month].&[2004]&[3]} ON 0,
[Product].[Category].[Category].Members ON 1
FROM
[Adventure Works]
WHERE
([Measures].[Freight Per Customer])
I have a situation where I need to get active account count on a specific date.
I have start date end date in SQL it looks like
select count(account) where start_date <=#date and end_date >=#date
In MDX, I tried below one
AGGREGATE(
{
NULL:LINKMEMBER
(
[DATE].[YQMWD].CURRENTMEMBER
, [StartDate].[YQMWD]
)
}
*
{
LINKMEMBER
(
[DATE].[YQMWD].CURRENTMEMBER
, [EndDate].[YQMWD]
):NULL
}
, ([Measures].[AccountCount])
)
This Code is returning me perfect on date level only but once I am traversing to Week or Month it is considering the whole month of the active value. But during the selected month period many account getting deactivate.
If anybody faced this kind of measure before like
"Active value on specific date" .
I thought about the following but then noticed that you are dealing with two distinct dimensions [StartDate] and [EndDate] so using the range operator : is incorrect:
AGGREGATE(
UNION(
{
NULL
:
LINKMEMBER(
[DATE].[YQMWD].CURRENTMEMBER
, [StartDate].[YQMWD]
)
}
,{
LINKMEMBER(
[DATE].[YQMWD].CURRENTMEMBER
, [EndDate].[YQMWD]
)
:
NULL
}
)
, ([Measures].[AccountCount])
)
If we look in the definition of LINKMEMBER then it says the following:
https://msdn.microsoft.com/en-us/library/ms146058.aspx
The LinkMember function returns the member from the specified
hierarchy that matches the key values at each level of the specified
member in a related hierarchy. Attributes at each level must have the
same key cardinality and data type. In unnatural hierarchies, if there
is more than one match for an attribute's key value, the result will
be an error or indeterminate.
So it makes sense the behaviour you are encountering - if [DATE].[YQMWD].CURRENTMEMBER is a week, then the key of this member is not going to map across to either [StartDate].[YQMWD] or [EndDate].[YQMWD].
Moreover if a week is selected then how do you interpret this sql logic where start_date <=#date and end_date >=#date as a week is made up of 7 days so #date has 7 possibilities?
Maybe you could use EXISTS function inside the LINKMEMBER function so that only Date level members are used. Then we have this problem that Exists will return a set type but the first arg of LINKMEMBER needs to be of type member. So get hold of the first and last members using Tail and Head like so:
AGGREGATE(
{
NULL
:
LINKMEMBER(
HEAD(
EXISTS(
[DATE].[YQMWD].CURRENTMEMBER
,[DATE].[YQMWD].[Date] ////<<if CURRENTMEMBER is a week then Exists will return a set of 7 Date members
)
,1 ////<<if CURRENTMEMBER is a week then Head(..1) will return a set of 1 Date member - the first date in the week
).ITEM(0).ITEM(0) ////<<this converts from single member set to a member
, [StartDate].[YQMWD]
)
}
*
{
LINKMEMBER(
TAIL(
EXISTS(
[DATE].[YQMWD].CURRENTMEMBER
,[DATE].[YQMWD].[Date]
)
,1
).ITEM(0).ITEM(0)
, [EndDate].[YQMWD]
)
:NULL
}
, ([Measures].[AccountCount])
)
How about first checking for the type of input and then giving results? In this case CoalesceEmpty will check that and return the results. Used FirstChild function to fetch the first date always from the range.
CoalesceEmpty
(
AGGREGATE
(
{NULL:LINKMEMBER([DATE].[YQMWD].CURRENTMEMBER.FIRSTCHILD.FIRSTCHILD.FIRSTCHILD.FIRSTCHILD, [StartDate].[YQMWD])}
*
{LINKMEMBER([DATE].[YQMWD].CURRENTMEMBER.FIRSTCHILD.FIRSTCHILD.FIRSTCHILD.FIRSTCHILD, [EndDate].[YQMWD]):NULL}
, ([Measures].[AccountCount])
)//--If it is a year
,
AGGREGATE
(
{NULL:LINKMEMBER([DATE].[YQMWD].CURRENTMEMBER.FIRSTCHILD.FIRSTCHILD.FIRSTCHILD, [StartDate].[YQMWD])}
*
{LINKMEMBER([DATE].[YQMWD].CURRENTMEMBER.FIRSTCHILD.FIRSTCHILD.FIRSTCHILD, [EndDate].[YQMWD]):NULL}
, ([Measures].[AccountCount])
)//--If it is a quarter
,
AGGREGATE
(
{NULL:LINKMEMBER([DATE].[YQMWD].CURRENTMEMBER.FIRSTCHILD.FIRSTCHILD, [StartDate].[YQMWD])}
*
{LINKMEMBER([DATE].[YQMWD].CURRENTMEMBER.FIRSTCHILD.FIRSTCHILD, [EndDate].[YQMWD]):NULL}
, ([Measures].[AccountCount])
)//--If it is a month
,
AGGREGATE
(
{NULL:LINKMEMBER([DATE].[YQMWD].CURRENTMEMBER.FIRSTCHILD, [StartDate].[YQMWD])}
*
{LINKMEMBER([DATE].[YQMWD].CURRENTMEMBER.FIRSTCHILD, [EndDate].[YQMWD]):NULL}
, ([Measures].[AccountCount])
)//--If it is a week
,
AGGREGATE
(
{NULL:LINKMEMBER([DATE].[YQMWD].CURRENTMEMBER, [StartDate].[YQMWD])}
*
{LINKMEMBER([DATE].[YQMWD].CURRENTMEMBER, [EndDate].[YQMWD]):NULL}
, ([Measures].[AccountCount])
)//--If it is a date
)
In the following I successfully find the last complete month and the billable income related to that month. Then I'd like to target the month before and find the income for that month. Thought that Lag would be a good function to use but this doesn't seem to work. No errors just no data returned for the second column [Measures].[PrevMth_BillInc]:
WITH
MEMBER [Date].[Date - Calendar Month].[LastMth] AS
IIF(
Day(Now()) = 1,
TAIL([Date].[Date - Calendar Month].[Calendar Month],2).Item(1),
TAIL([Date].[Date - Calendar Month].[Calendar Month],2).Item(0)
)
MEMBER [Measures].[LastMth_BillInc] AS
SUM(
[Date].[Date - Calendar Month].[LastMth],
[Measures].[BillableIncome]
)
MEMBER [Measures].[PrevMth_BillInc] AS
SUM(
[Date].[LastMth].Lag(1),
[Measures].[BillableIncome]
)
SELECT
NON EMPTY
{
[Measures].[LastMth_BillInc],
[Measures].[PrevMth_BillInc] //<<<<<this returns nothing
}
ON COLUMNS,
NON EMPTY
ORDER(
[Customer_Dim].[Customer_Hier].[Customer_Level].Members,
[Date].[Date - Calendar Month].[LastMth],
BDESC
)
ON ROWS
FROM [ourCube]
By defining [LastMth] as a calculated member, you define a new member of the [Date].[Date - Calendar Month], not an alias to an existing one. Hence Lag does not work, as it is applied to the new member - which is somewhere in the hierarchy alongside the original member. And as Lag is a function which operates on the hierarchy structure, it is not to the original member used in its definition.
You could get around that by defining LastMth as a set which would contain the original, non calculated member (to be exact, it would contain a tuple containing the original member) as follows:
WITH
SET [LastTwo] AS
TAIL([Date].[Date - Calendar Month].[Calendar Month],2)
SET [LastMonth] AS
IIF(
Day(Now()) = 1,
Subset([LastTwo], 1, 1),
Subset([LastTwo], 0, 1)
)
MEMBER [Measures].[LastMth_BillInc] AS
(
[LastMth].Item(0).Item(0),
[Measures].[BillableIncome]
)
MEMBER [Measures].[PrevMth_BillInc] AS
(
[LastMth].ITEM(0).Item(0).Lag(2),
[Measures].[BillableIncome]
)
SELECT
NON EMPTY
{
[Measures].[LastMth_BillInc],
[Measures].[PrevMth_BillInc]
}
ON COLUMNS,
NON EMPTY
ORDER(
[Customer_Dim].[Customer_Hier].[Customer_Level].Members,
[Date].[Date - Calendar Month].[LastMth],
BDESC
)
ON ROWS
FROM [ourCube]
I think you do not need sum, you can just use tuples as I did above, as there is only one member and not a set of several to be summed.
In my Date Dimension I have 2 hierarchies:
[Year - Week - Date] and [Year - Month - Date]
I want to check in a Cube Calculation if the current level of the Date Dimension is [Date] (the lowest level) or something higher.
How can I achieve this?
Background: I need this to calculate how many working days there were in a period, for an employee.
I currently have this code (untested) that should do the trick for 1 hierarchy, but I guess this will fail when users are using the [Year - Week - Date] hierarchy.
CASE WHEN
[Date].[Year - Month - Date].CURRENTMEMBER.Level
IS
[Date].[Year - Month - Date].[Date]
THEN
//at day level,
//if there is any duration booked, this is a working day
IIF([Measures].[Duration] = 0,0,1)
ELSE
//at higher than day level,
//count days
COUNT(
// where duration > 0 (employee work day)
FILTER(
Descendants([Date].[Year - Month - Date].CURRENTMEMBER, [Date].[Year - Month - Date].[Date]),
[Measures].[Duration] > 0
)
)
END
tl;dr how do I make the above code also work for [Year - Week - Date] hierarchy in the cleanest way possible.
Let's assume the hierarchy (aka attribute) [Date].[Date] exists. If this is the case you can simplify :
COUNT(
FILTER( Existing [Date].[Date].members, [Measures].[Duration] > 0 )
)
The Existing will force to apply an autoexists on the [Date] dimension. This is mainly a performance improvement as it's avoid to evaluate (fact-vise) all tuples.
Once the former examples is clear, we can merge it with your version for the fastest solution (no need for the first iif) :
COUNT(
FILTER( Existing
Descendants([Date].[Year - Month - Date].CURRENTMEMBER, [Date].[Year - Month - Date].[Date],self)
, [Measures].[Duration] > 0 )
)
Further improvements may be possible adding a new measure with a different aggregation type or adding an iif to change which of the two hierarchies is used for descendants (e.g. the one where the currentmember and the defaulmember is not equal)
If you are looking for the cleanest way to calculate how many working days there were in a period, you have to emulate the behavior of regular measure. Otherwise you will get error or bad data in the case of a multiselect on [Date]. In addition, it is desirable to get rid of the Count(Filter(...)) expression to keep the block computation mode (see Optimizing Count(Filter(...)) expressions in MDX). To do this, follow these steps:
Go to the data source view.
Create a new named calculation next to the [Duration] column (in the same fact table).
Column name is "Working days", expression is "null".
Create a new regular measure based on "Working days" column. Aggregation function is Sum.
Write in the MDX script:
(
[Date].[Date].[Date].Members,
[Measures].[Working days]
) = Iif( [Measures].[Duration] > 0, 1, null );
IsLeaf() will tell you if a member is at the bottom level.
Look at SCOPE commands in your calculation script, I do something similar for my YMD calender and my YWD calendar:
CREATE MEMBER CurrentCube.Measures.Example AS NULL //Dummy will be calc'd later
SCOPE (DESCENDANTS([Date].[Calender Y M D],,AFTER));
Measures.Example = 1; //Fill in for monthly calcs
END SCOPE
SCOPE (DESCENDANTS([Date].[Calender Y W D],,AFTER));
Measures.Example = 2; //Fill in for weekly calcs
END SCOPE
The syntax with the ,,AFTER is to exclude the All member if I remember rightly, I'm trying to dif the link up but can't find it. Alternatively, if the calculation works well with the ALL member, just use SCOPE([Date].[Calender Y W D])