MDX Dynamic Set Filter Measuers - mdx

I am new to MDX.
I want to create a Dynamic Set on SSAS (SQL Server 2014) where this Set filters all Measures
where Measure Date is greater than the current selected date.
Is this possible with a Dynamic Set if not which other solutions are possible?
ID|Date |MeasureVal
1 |15.01.2014| 100
2 |16.01.2014| 150
3 |20.02.2014| 500
4 |21.02.2014| 150
So when the User select this Set and the Date Filter is on 16.01.2014 it should show return Measures with Date greater than 16.01.2014.
From the above example it should return Measures with ID 3,4
Here is my Try
CREATE DYNAMIC SET CURRENTCUBE.[MyTestSet]
AS
Filter (
[Production Time].[Time Hierarchy].[Date].AllMembers ,
( [Production Time].[Time Hierarchy].[Date].CurrentMember.member_caption > "Current selected Date?" )
;

Maybe this?
CREATE DYNAMIC SET CURRENTCUBE.[MyTestSet]
AS Filter(
[Production Time].[Time Hierarchy].[Date].Members as dtSet,
dtSet.Item(
dtSet.CurrentOrdinal-1
).member_caption > "Current selected Date?"
);
But I don't think member_caption will return a number so how will > function?
Usually if you want a set of dates greater than a specified member in mdx you use the range operator, which is a colon : couple with a null on one side of the colon. So here are two example:
WITH
SET [GreaterThanX] AS
{
[Production Time].[Time Hierarchy].[Date].[01 Jan 2014]
:
null
}
SET [LessThanX] AS
{
null
:
[Production Time].[Time Hierarchy].[Date].[01 Jan 2014]
}
SELECT
{} ON 0,
[GreaterThanX] ON 1
//[LessThanX] ON 1
FROM myCube;
Try the above - the first should give you all dates after 01 Jan whereas the second all dates less than.
note: when using the : operator it is important that the specified member, in this case [Production Time].[Time Hierarchy].[Date].[01 Jan 2014] exists in the cube - if it doesn't exist no error occur but the expression is evaluated as null:null i.e. all dates will be returned.

Related

Applying advanced filter in Power BI DAX, from a different table

I have the following tables:
Episodes:
Clients:
My DAX calculation sums up [Days_epi] unique values, from Episodes tbl, grouping them by [ProgramID_epi], [EpisodeID_epi], [ClientID_epi].
So, the SUM of [Days_epi] = 3 + 5 + 31 + 8 + 15 + 20 + 10 = 92
Here is my working code for this:
DaysSUM =
CALCULATE (
SUMX (
SUMMARIZE (
'Episodes',
'Episodes'[EpisodeID_epi],
'Episodes'[ProgramID_epi],
'Episodes'[ClientID_epi],
'Episodes'[Days_epi]
),
'Episodes'[Days_epi]
),
FILTER (
'Episodes',
'Episodes'[Category_epi] = "Homeless"
)
)
I need to add two advanced filters to the calculation above:
Filter 1 should ONLY KEEP records in Episodes, if the records in the Clients have the difference between [DischDate_clnt] and [AdmDate_clnt] >= 365.
Filter 1 in SQL statement is
DATEDIFF(DAY, [AdmDate_clnt], [DischDate_clnt]) >= 365)
After that, Filter 2 should ONLY KEEP records in Episodes, if the records in the Clients have
[Date_clnt] >= [AdmDate_clnt] + 12 months. (12 month after the Admission Date)
Filter 2 in SQL statement is
[Date_clnt] <= DATEADD(MONTH, 12, [[AdmDate_clnt])
So, after applying those two filters I expect the records 6 and 10 of the Episodes tbl must be excluded (filtered out), because the records 2 and 3 of the Clients tbl (highlighted in green) are not satisfied my Filter 1 / Filter 2.
Here is the final Episodes dataset I should have (without the 2 records in red):
I was starting to update my DAX code as the following (below).
But keep receiving error "Parameter is not the correct type"
enter
DaysSUM_Filters =
CALCULATE (
SUMX (
SUMMARIZE (
'Episodes',
'Episodes'[EpisodeID_epi],
'Episodes'[ProgramID_epi],
'Episodes'[ClientID_epi],
'Episodes'[Days_epi]
),
'Episodes'[Days_epi]
),
FILTER (
'Episodes',
'Episodes'[Category_epi] = "Homeless"
), TREATAS(DATEDIFF('Clients'[AdmDate_clnt],
'Clients'[DischDate_clnt], DAY)>=365,
'Clients'[Date_clnt])
)
Not exactly sure how to set those 2 filters correctly in DAX Power BI, as I
am relatively new to it.
Please help!
I can't say about all the case. But what is obvious is that you use TREATAS in a wrong way. It works like this TREATAS({"Red", "White", "Blue"}, 'Product'[Color]).
In your case
DATEDIFF('Clients'[AdmDate_clnt],
'Clients'[DischDate_clnt], DAY)>=365
will return TRUE or FALSE value. The first argument of TREATAS - is a column or set of columns not a single value.
You can use the filter like this:
FILTER(
'Clients'
,DATEDIFF(
'Clients'[AdmDate_clnt]
,'Clients'[DischDate_clnt]
,DAY
)>=365
)
This will return you a filtered table.
This may work if your tables are linked.

SSAS MDX Problem with IIF and TAIL() function

Experts,
I am using SQL SSAS Std. 2017 and in the end want to create a calculated member that returns either the last month's day of my data or the current day if the last existing data is of today.
(=> If today is Aug-31 I want to retrieve Aug-31 of my data, otherwise if today is e.g. Aug-30 retrieve Jul-31)
To develop this member I am currently in the process of creating a MDX query in SQL Server. I am having difficulties to understand what is actually a "tuple set expression" (because the TAIL() function shall return a subset (ergo set) according to MSDN) but in fact I am receiving errors when playing around with the .Item(0) function on its result. In MSDN I cannot find information about "tuple sets" and how to make them do, what I want.
My Date Dimension has a Hierarchy JMT (Year | Month | Day | Date Key of type DATE).
To receive the most current date member of the cross product I am using the TAIL(NONEMPTY(Date...Members, { (DimX.&.. , DimY.&.. , DimZ.&..) })) expression which works fine.
But how do I choose between today's or the previous month's date?
My MDX for testing purposes on February (2) is as follows:
SELECT {
IIF(
TAIL(NONEMPTY([DateDim].[JMT].[T].Members, { ([DimX].[X].&[200], [DimY].[Company].&[4499166], [DateDim].[JMT].[M].&[2020]&[2]) })).Item(0) --.Properties('Date Key', TYPED)
> NOW()
, TAIL(NONEMPTY([DateDim].[JMT].[T].Members, { ([DimX].[X].&[200], [DimY].[Company].&[4499166], [DateDim].[JMT].[M].&[2020]&[1]) }))
, TAIL(NONEMPTY([DateDim].[JMT].[T].Members, { ([DimX].[X].&[200], [DimY].[Company].&[4499166], [DateDim].[JMT].[M].&[2020]&[2]) }))
)
-- ,
-- TAIL(NONEMPTY([DateDim].[JMT].[T].Members, { ([DimX].[X].&[200], [DimY].[Company].&[4499166], [DateDim].[JMT].[M].&[2020]&[2]) })) } on columns
, { [Measures].[Turnover] } on rows
FROM [Finance]
Result:
As you can see, the IIF function does not do what I want. It assumes .Item(0) is greater than NOW() therefore returning the "31" member of January (1). Expected: "29" of February.
I guess, it might be a problem with the data types and the actual value returned by .Item(0). But if I want to use the .Properties('Date Key', TYPED) it states "The Date Key Dimension Attribute could not be found. See below picture.
In the image of the DateDim it should be "DateDim.JMT" in the blue area ;-).
Do you have any suggestions?
Thank you, Cordt
If you switch this:
TAIL(NONEMPTY([DateDim].[JMT].[T].Members, { ([DimX].[X].&[200], [DimY].[Company].&[4499166], [DateDim].[JMT].[M].&[2020]&[2]) })).Item(0)
to the following does it help?
Tail
(
NonEmpty
(
[DateDim].[JMT].[T].MEMBERS
,{
(
[DimX].[X].&[200]
,[DimY].[Company].&[4499166]
,[DateDim].[JMT].[M].&[2020]&[2]
)
}
)
).Item(0).Item(0).MemberValue

SQL using the results from the previous row

I'm trying to do an Excel kind of calculation in SQL. This involves using the closing rate (ClRate) from the previous row and using it to calculate values in the next row. The table starts from 1 Jan and has 1000 rows and each row has known data and data which needs to be calculated (shown in [ ])
Date RecQty RecAmt IssQty IssAmt ClQty ClAmt ClRate
1 Jan - - - - 100 $20,000 200
2 Jan +10 +$2100 -5 [ ] [ ] [ ] [ ]
The calculations to generate the desired result are in the table below
Date RecQty RecAmt IssQty IssAmt ClQty ClAmt ClRate
1 Jan 100 $20,000 200
2 Jan +10 +$2100 -5 -[200*5] [100+10-5] [20,000+2100-200*5] [ClAmt/ClQty]
The IssAmt for each day will be calculated by multiplying the IssQty by the previous days' ClRate. The ClQty is computed as previous day ClQty + current day RecQty - current day IssQty. The ClAmt is computed as previous day ClAmt+ current day RecAmt - current day IssAmt. Finally, the ClRate for each day is computed as ClAmt / ClQty
The only ClRate known is the opening inventory row of the table (1 Jan)- thereafter the ClRate for each subsequent row needs to be computed.
In Excel, you would simply do this calculation by linking the required cells of the previous row and copying/pasting the formula to all the rows below.
How would you do this in SQL? I have tried self joining CTEs, loops and LAG- none of these seems to work. The reason is that the ClRate for each row from 2 Jan onwards is not known - and while Excel can handle computing results on the fly which are used in the following row - SQL is unable to do this.
Seeking help to solve this problem. I'm using SQL Server 2017 and SSMS. If required I can provide the code
Table DDL
CREATE TABLE [Auto].[IronOreTbl](
[Id] [int] NULL,
[Counter] [int] NULL,
[TDate] [date] NOT NULL,
[RecQty] [decimal](16, 2) NULL,
[RecAmt] [decimal](16, 2) NULL,
[IssQty] [decimal](16, 2) NULL,
[IssAmt] [decimal](16, 2) NULL,
[ClQty] [decimal](16, 2) NULL,
[ClAmt] [decimal](16, 2) NULL,
[ClRate] [decimal](16, 2) NULL
) ON [PRIMARY]
GO
INSERT INTO [Auto].[IronOreTbl]
([Id],[Counter],[TDate],[RecQty],[RecAmt],[IssQty],[IssAmt],[ClQty],[ClAmt],[ClRate])
VALUES
(1,0,'2019-01-01',NULL,NULL,NULL,NULL,100,20000,200),
(2,1,'2019-01-02',10,2100,5,NULL,105,NULL,NULL),
(3,2,'2019-01-03',8,1600,2,NULL,111,NULL,NULL),
(4,3,'2019-01-04',20,2400,10,NULL,121,NULL,NULL)
CTE attempts
;WITH ClAmtCTE AS
(
SELECT
Id,RecQty,RecAmt,IssQty,ClQty,ClAmt,ClRate
,EffRate = ClRate
,CumHoldVal= ClAmt
--CAST(ClAmt AS decimal(16,2))
,CumClRt=CAST(ClRate AS decimal(16,2))
,[Counter]
FROM
[Auto].IronOreTbl
WHERE
Id=1
UNION ALL
SELECT
C2.Id,C2.RecQty,c2.RecAmt,C2.IssQty,C2.ClQty,C2.ClAmt,c2.ClRate,
EffRate = (SELECT CumClRt WHERE C2.ID=C2.[Counter]+1),
CumRN =
CAST(
(
CumHoldVal+ISNULL(C2.RecAmt,0)-
(EffRate)*ISNULL(C2.IssQty,0)
)
AS decimal(16,2)
),
CumClRt=CAST(CumHoldVal/C2.ClQty AS decimal(16,2)),
C2.[Counter],
FROM
[Auto].IronOreTbl C2
INNER JOIN ClAmtCTE C1 ON C1.Id = C2.[Counter]
The following code achieves the desired result - while I had got close several days ago, it still took all this time to tie up the last bit; of matching the ClRate with the correct row. There was an additional issue encountered where on days where there are no issues, but only receipts, the rate picked up belonged to the wrong row (I still have no clue why and how this was happening - all I know is that the previous code needed revision to resolve this discrepancy and the modified code attends to the same)
;WITH ClAmtCTE AS
(
SELECT
Id,RecQty,RecAmt,IssQty,ClQty,ClAmt,ClRate
,EffRate = ClRate
,CumHoldVal= ClAmt
--CAST(ClAmt AS decimal(16,2))
,CumClRt=CAST(ClRate AS decimal(16,2))
,[Counter]
FROM
[Auto].IronOreTbl
WHERE
Id=1
UNION ALL
SELECT
C2.Id,C2.RecQty,c2.RecAmt,C2.IssQty,C2.ClQty,C2.ClAmt,c2.ClRate,
EffRate = (SELECT CumClRt WHERE C2.ID=C2.[Counter]+1),
CumRN =
CAST(
(
CumHoldVal+ISNULL(C2.RecAmt,0)-
((SELECT CumClRt WHERE C2.ID=C2.[Counter]+1))*ISNULL(C2.IssQty,0)
)
AS decimal(16,2)
),
CumClRt=CAST((CumHoldVal+ISNULL(C2.RecAmt,0)-
((SELECT CumClRt WHERE C2.ID=C2.[Counter]+1))*ISNULL(C2.IssQty,0))/C2.ClQty AS decimal(16,2)),
C2.[Counter]
FROM
[Auto].IronOreTblC2
INNER JOIN ClAmtCTE C1 ON C1.Id = C2.[Counter]
)
UPDATE [Auto].IronOreTbl
SET ClRate = T1.CumClRt,
ClAmt=CumHoldVal
FROM ClAmtCTE T1
INNER JOIN [Auto].IronOreTbl T2
ON T1.ID = T2.Id
OPTION (MAXRECURSION 0)
Attempting and, in what appears to me as, solving it has taught me several things. Primarily these include:
Calculations which you can set up and run in a heartbeat in a spreadsheet are anything but in SQL
Excel has the ability to both calculate on the fly and utilise results calculated on the fly in the dependent calculations. SQL cannot do this. Having this ability in Excel means you do not have to iterate over the data - once to have Excel compute the results and then for Excel to apply the results where they are needed
For all its use and utility as a data housing medium SQL has a long way to go in attending to standard real world calculations - such as this example; multiple loans, changing interest rates and relevant interest calculations etc etc.

Get Next Value within Row Groups in SSRS

I have a problem I'm trying to solve with getting the NextRow value but only within the specific RowGroup.
Here's what I'm trying to do:
if Fields!Interventions.Value is not blank and nextrow(Fields!DateOccurred.Value) within the group is within 4 hours then Y else N
and another column:
if Fields!Interventions.Value is not blank compare currentrow(Fields!DateOccurred.Value) to nextrow(Fields!DateOccurred.Value) and display the differences in hours
But I'm stuck as to how i could do this expression from within SSRS.
I saw the following question:
Get previous,current and Next Record in ssrs report in a page
but the issue with this one is that it doesn't work with Row Groups in SSRS.
Upon further advise, it appears as this isn't possible to do in SSRS, but would be possible in the query itself.
This is the query I'm working with:
SELECT
ROW_NUMBER() OVER(PARTITION BY R.PatientID, PL.ChartLocationID ORDER BY P.DateOccurred ASC) As Row#,
R.NameFirst,
R.NameLast,
P.DateOccurred,
CASE P.Interventions
WHEN 1 Then 'Regular Analgesia'
When 2 Then 'Heat Pack'
When 4 Then 'Cold Pack'
When 8 Then 'Massage'
When 16 Then 'Exercise'
When 32 Then 'Other'
END as Interventions
FROM
ChartEntry P
LEFT JOIN ChartLocation PL on
P.ChartLocationId = PL.ChartLocationId
Inner Join Resident R on
PL.ResidentID = R.PatientID
where
P.DateDeleted is null and
P.DateOccurred >= '01 Jul 2017' and
P.DateOccurred < '01 Aug 2017'
Is it possible to achieve?
Here is some sample data showing the results for the extra two columns:
Easiest and better way is to use LAG/LEAD in your SQL to figure this out.
You can do this in SSRS:
Set your detail value to Previous(Fields!DateOccurred.Value) and the detail row visibility to = RowNumber("yourgroup") = 1 which will hide the first row.
Use one row on the group total, in order to display the last value by setting its value to =Last(Fields!DateOccurred.Value)
The date difference expression check for the detail will be
=Iif(
NOT(Fields!Interventions.Value Is Nothing) AND
DateDiff("h", Previous(Fields!DateOccurred.Value),Fields!DateOccurred.Value)>=4,
"Y",
"N"
)
and for the last line
=Iif(
NOT(Fields!Interventions.Value Is Nothing) AND
DateDiff("h", Last(Fields!DateOccurred.Value),Now())>=4,
"Y",
"N"
)
In the images below, the red line will be hidden with the visibility condition.
Your report will contain only the expected date column, I include the current row date for demonstration.

How to mimic SQL subtraction of results from two different queries in mdx

I wanted to do the trend analysis between the dates. For an instance current date- 30 days
30-60 days and so on.Below is the snippet of comparable sql query but same I wanted to do in MDX.
SQL
SELECT
ROUND
(
(
(
(
SELECT
SUM(del_pri_impr)
FROM
reporting.so_sli_calc_val a,
reporting.user_group_tenant b,
reporting.salesorder c
WHERE
created_on BETWEEN DATE(now()-30) AND DATE(now())
)
-
(
SELECT
SUM(del_pri_impr)
FROM
reporting.so_sli_calc_val a,
reporting.user_group_tenant b,
reporting.salesorder c
WHERE
created_on BETWEEN DATE(now()-60) AND DATE(now()-30)
)
)
/
(
SELECT
SUM(del_pri_impr)
FROM
reporting.so_sli_calc_val a,
reporting.user_group_tenant b,
reporting.salesorder c
WHERE
created_on BETWEEN DATE(now()-60) AND DATE(now()-30)
) *100
)
,
0
) AS trend
MDX:
WITH
SET [~FILTER] AS
{[Created_Date.Created_Hir].[Created_On].[2014-04-01]:[Created_Date.Created_Hir].[Created_On].[2014-04-30]}
SET [~ROWS] AS
{[Sales Order Attributes SO.Sales_order].[Sales Order ID].Members}
SELECT
NON EMPTY {[Measures].[CONT_AMT_GROSS], [Measures].[CONT_AMT_NET]} ON COLUMNS,
NON EMPTY [~ROWS] ON ROWS
FROM [SALES_ORDER]
WHERE [~FILTER]
As of now I have hard coded the dates, that will come from parameters.
I am facing difficulty in creating the second set and how to do subtraction between two sets in MDX.
You already have the logic on how to obtain sets of date corresponding to "last 30 days from now" and "last 60 to last 30 days from now". So, I am going to skip that part.
NOTE - You would have to use the parameter values while building these sets.
What you want to do here is first find the values corresponding to these sets of dates and then perform operations on them.
You can proceed like this -
WITH
SET [~FILTER] AS
{[Created_Date.Created_Hir].[Created_On].[2014-04-01]:[Created_Date.Created_Hir].[Created_On].[2014-04-30]}
SET [~ROWS] AS
{[Sales Order Attributes SO.Sales_order].[Sales Order ID].Members}
SET [Last30Days] AS
...
SET [Last60ToLast30Days] AS
...
MEMBER [~Last30Days - Now] AS
Aggregate
(
[Last30Days],
[Measures].[SomeMeasure]
)
MEMBER [~Last60Days - Last30Days] AS
Aggregate
(
[Last60ToLast30Days],
[Measures].[SomeMeasure]
)
MEMBER [~Measure] AS
([~Last30Days - Now]-[~Last60Days - Last30Days] )/([~Last60Days - Last30Days] * 100), format_string = '#,##0'
SELECT
NON EMPTY {
[Measures].[CONT_AMT_GROSS],
[Measures].[CONT_AMT_NET],
[~Measure]
} ON COLUMNS,
NON EMPTY [~ROWS] ON ROWS
FROM [SALES_ORDER]
Format_String takes care of rounding.
Not sure if I totally agree with Sourav's answer as I think some form of aggregation will be needed; creating tuples with sets in them may raise an exception.
Here is a simple model, against AdvWrks, that is tested and will do a subtraction for you:
WITH
SET [Set1] AS
[Date].[Calendar].[Date].&[20060301]
:
[Date].[Calendar].[Date].&[20070308]
SET [Set2] AS
[Date].[Calendar].[Date].&[20070308]
:
[Date].[Calendar].[Date].&[20080315]
MEMBER [Date].[Calendar].[All].[Set1Agg] AS
aggregate([Set1])
MEMBER [Date].[Calendar].[All].[Set2Agg] AS
aggregate([Set2])
MEMBER [Date].[Calendar].[All].[x] AS
(
[Date].[Calendar].[All].[Set1Agg]
,[Measures].[Internet Sales Amount]
)
MEMBER [Date].[Calendar].[All].[y] AS
(
[Date].[Calendar].[All].[Set2Agg]
,[Measures].[Internet Sales Amount]
)
MEMBER [Date].[Calendar].[All].[x-y] AS
[Date].[Calendar].[All].[x] - [Date].[Calendar].[All].[y]
SELECT
{
[Date].[Calendar].[All].[x]
,[Date].[Calendar].[All].[y]
,[Date].[Calendar].[All].[x-y]
} ON 0
,[Product].[Category].[Category] ON 1
FROM [Adventure Works];
Reflecting against your code maybe something like the following:
WITH
SET [Set1] AS
[Created_Date.Created_Hir].[Created_On].[2014-04-01]
:
[Created_Date.Created_Hir].[Created_On].[2014-04-30]
SET [Set2] AS
[Created_Date.Created_Hir].[Created_On].[2014-03-01]
:
[Created_Date.Created_Hir].[Created_On].[2014-03-31]
MEMBER [Created_Date.Created_Hir].[All].[Set1Agg] AS
Aggregate([Set1])
MEMBER [Created_Date.Created_Hir].[All].[Set2Agg] AS
Aggregate([Set2])
MEMBER [Measures].[~Last30Days - Now] AS
(
[Created_Date.Created_Hir].[All].[Set1Agg]
,[Measures].[SomeMeasure]
)
MEMBER [Measures].[~Last60Days - Last30Days] AS
(
[Created_Date.Created_Hir].[All].[Set2Agg]
,[Measures].[SomeMeasure]
)
MEMBER [Measures].[~Measure] AS
([Measures].[~Last30Days - Now] - [Measures].[~Last60Days - Last30Days])
/
[Measures].[~Last60Days - Last30Days]
* 100
,format_string = '#,##0'
SET [~ROWS] AS
{
[Sales Order Attributes SO.Sales_order].[Sales Order ID].MEMBERS
}
SELECT
NON EMPTY
{
[Measures].[CONT_AMT_GROSS]
,[Measures].[CONT_AMT_NET]
,[Measures].[~Measure]
} ON COLUMNS
,NON EMPTY
[~ROWS] ON ROWS
FROM [SALES_ORDER]
WHERE
[~FILTER];