I have a MDX query below. This query works, the problem is it is slow, takes > 15 secs to return results even though the data set is not huge. I believe the query should execute under 2 secs (its also used on a landing page and the wait time is bothersome). The [Measures].[Star Rating] is causing the slow down because of all the IF THEN ELSE logic. All its doing is based on the [Mean Score], it finds the [Star Rating] from a lookup table based on the range in the lookup table.
For e.g. if [Mean Score] < 86, [Star Rating] = 1
if [Mean Score] >= 86 and <= 90, [Star Rating] = 2
The [Mean Score] is a simple sum/count calculation.
It can change based on the date range being used as parameter.
Can you recommend either an optimization in the existing query below or recommend an alternate way to calculate [Star Rating] ?
The MDX query is below:
WITH
MEMBER [Measures].[MeanScore] AS ([Measures].[Standard Point Assignment - Sum]/[Measures].[Episode Of Care HCAHPS Count])
MEMBER [Measures].[StarRating] AS
CASE
WHEN [HCAHPS Star Rating].[HCAHPS Star Rating ID].CurrentMember IS [HCAHPS Star Rating].[HCAHPS Star Rating ID].[All]
THEN
CASE
WHEN [Measures].[HSR-HCHCAHPS Domain ID] = TAIL([HCAHPS Star Rating].[HCAHPS Star Rating ID].[HCAHPS Star Rating ID]).Item(0).Item(0).Properties('HCHCAHPS Domain ID')
THEN
(
[Measures].[Rating],
Tail([HCAHPS Star Rating].[HCAHPS Star Rating ID].[HCAHPS Star Rating ID]).Item(0).Item(0)
)
ELSE
(
[Measures].[StarRating],
Tail([HCAHPS Star Rating].[HCAHPS Star Rating ID].[HCAHPS Star Rating ID]).Item(0).Item(0).PrevMember
)
END
ELSE
CASE
WHEN [Measures].[MeanScore] > [HCAHPS Star Rating].[HCAHPS Star Rating ID].CurrentMember.Properties('Start', typed)
AND [Measures].[HC-HCAHPS Domain ID] = [HCAHPS Star Rating].[HCAHPS Star Rating ID].CurrentMember.Properties('HCHCAHPS Domain ID', typed)
THEN
(
[Measures].[Rating],
[HCAHPS Star Rating].[HCAHPS Star Rating ID].CurrentMember
)
ELSE
(
[Measures].[StarRating],
[HCAHPS Star Rating].[HCAHPS Star Rating ID].CurrentMember.PrevMember
)
END
END
SELECT
{
[Measures].[Episode Of Care HCAHPS Count]
,[Measures].[Is Top Box]
,[Measures].[CompositeScore]
,[Measures].[PromoterCount]
,[Measures].[PromoterPercent]
,[Measures].[PassiveCount]
,[Measures].[PassivePercent]
,[Measures].[DetractorCount]
,[Measures].[DetractorPercent]
,[Measures].[StarRating]
,[Measures].[MeanScore]
} ON COLUMNS,
NONEMPTYCROSSJOIN
(
{NONEMPTY([HCAHPS Domain].[HCAHPS Survey Methodology ID].[HCAHPS Survey Methodology ID])}
,DESCENDANTS(StrToSet('[Org Hierarchy].[Parent Key].&[118418]'))
,{[HCHCAHPS Domain].[HC Domain Group].[HC Domain Group]}
,{[HCHCAHPS Domain].[HCAHPS Domain Name].[HCAHPS Domain Name]}
,{[HCAHPS Question Answer].[Question Number].AllMembers}
) ON ROWS
FROM [CAHPS]
WHERE
(
StrToMember("[Date].[Date].&[" + FORMAT(NOW()-365,"yyyy-MM-ddT00:00:00") + "]",CONSTRAINED):StrToMember("[Date].[Date].&[" + FORMAT(NOW(),"yyyy-MM-ddT00:00:00") + "]",CONSTRAINED)
)
In general, IIF and CASE statements within Analysis Services MDX could potentially see some performance degradations. While most IIF statements are relatively inexpensive, complex nested conditions (with lots of IIF statements), this would result in the Analysis Services formula engine will end up running the query cell-by-cell.
For performant queries, the goal is to have the query run in subspace mode (or block computation) instead of cell-by-cell mode. To do this within the context if IIF statements, please try to utilize SCOPE statements. A great reference to convert IIF statements to SCOPE is Mosha Pasumansky's blog post is Performance of IIF function in MDX.
By the way, for more information on subspace computation (vs. cell-by-cell), please refer to Section 3.2.1 of the Analysis Services Performance Guide
Related
I trying to write the following SQL code in DAX.
select SUM(amount) from TABLE
where BusinessDate = '2023-02-11'
OR (Segment = 'General' and Days_Without_Payments < 210)
OR (Segment = 'Enterprise' and Days_Without_Payments < 1215)
OR (Segment = 'House' and Days_Without_Payments < 1945)
I got the first part(i-e. SUM) right, but can't get my head around the filters.
What I tried:
Measure = CALCULATE([Total Outstanding],
FILTER(VALUES(BILBL_WO[Date]),BILBL_WO[Date]=MAX(BILBL_WO[Date])),
AND(BILBL_WO[Days_Without_Payments] < 210, BILBL_WO[Segment] = "General") ||
AND(BILBL_WO[Days_Without_Payments] < 1215, BILBL_WO[Segment] = "Enterprise") ||
AND(BILBL_WO[Days_Without_Payments] < 1945, BILBL_WO[Segment] = "House")
)
Where Total Outstanding is another Measure which I created for summation of Amount.
Please help as I couldn't find something useful from internet and I am new at this. Thanks!
In general, when people start learning DAX, most think it is like SQL query, but that is not true because SQL is a structured query language. In contrast, DAX is a formula language used for analysis purposes similar to excel equations.
I will give you a few pointers on how to convert SQL to DAX in simple terms considering you have one table that you imported to Power BI or SSAS tabular model, which are as follow:
You need to segregate your query as follows:
Aggregation function in your example SUM (amount) in SQL query will be in DAX:
Total Outstanding = sum('BILBL_WO'[amount])
Where condition on a date column:
I usually create a date dimension and create a relation with the table that has the date column. You can do that when you go into more details relating to evaluation context in Power BI and Star schema architecture as a start, but in your case, if the column data type is a string, convert it to date type and then use the date column as a filter in the power bi report page, allowing the report users to select different dates, not just ‘2023-02-11'.
For your conditions on Segment and Days_Without_Payments, before converting it to DAX, I think your original query is missing Parenthesis. If I understand it correctly, please let me know if I am wrong.
select SUM(amount)
from TABLE
where BusinessDate = '2023-02-11'
and
(
(Segment = 'General' and Days_Without_Payments < 210)
OR
(Segment = 'Enterprise' and Days_Without_Payments < 1215)
OR
(Segment = 'House' and Days_Without_Payments < 1945)
)
Altered SQL script
If my above assumption is valid and you need to write a DAX measure with the Segment and Days_Without_Payments conditions, you can write it as follow:
Measure = CALCULATE([Total Outstanding] ,
KEEPFILTERS(
FILTER(
ALL(BILBL_WO[Segment] ,BILBL_WO[Days_Without_Payments]),
(BILBL_WO[Segment] = "General" && BILBL_WO[Days_Without_Payments] < 210)
||
(BILBL_WO[Segment] = "Enterprise" && BILBL_WO[Days_Without_Payments] < 1215)
||
(BILBL_WO[Segment] = "House" && BILBL_WO[Days_Without_Payments] < 1945))
)
)
And for more on how to filter DAX using multicolumn, see this YouTube video https://www.youtube.com/watch?v=kQjYG6TJVp8 and follow the SQLBI channel. It will help you a lot.
I hope I helped in a way; if so, please mark this as an answer and vote for it :)
I have a fact Table that contains people & Thier qualifications. The fact table has two dates, the StartStudyDate & EndStudyDate. This represents the period the students were studying.
I then have a Person Dimension, Qualification Dimension, a Grouping Dimension & one Date Dimension.
Im trying to find a count of students who were actively studying on a particular date.
In SQL its relatively simple:
select a.PaygroupDescription, a.[Qualification], count(a.[PersonID])
from (
select distinct p.[PersonID], PaygroupDescription, q.[Qualification]
from [hr].[Appointment Detail] ad
join hr.Paygroup pg on ad.PaygroupID = pg.PaygroupID
join hr.Qualification q on q.QualificationID = ad.QualificationID
join hr.Person p on p.PersonID = ad.PersonID
join dimdate sd on sd.DateID = ad.StartDateID
join dimDate ed on ed.DateID = ad.EndDateID
where sd.date <= 20150101 and ed.date >= 20150101
) as a
group by a.PaygroupDescription, a.[Qualification]
The problem is i cant figure out how to do this in dax.
I started out by adding two columns to the fact table in the TabularModel:
ActualStartDate
=LOOKUPVALUE(
'Date'[Date],
'Date'[DateID],
'Appointment Detail'[StartDateID])
ActualEndDate
=LOOKUPVALUE(
'Date'[Date],
'Date'[DateID],
'Appointment Detail'[EndDateID])
I then wrote the measure that checks if one date is selected from DimDate, it gets all distinct rows where the selectedDate is <= ActualStartDate && >= ActualEndDate.
Problem is that this behaves like an absolute dog. if i try to add any attributes for breaking the data down, i run out of memory (at least in 32bit excel). I know i could try 64 bit excel, but my dataset is small, so memory should not be an issue. This is before i even add filters to the calculation for specific qualifications:
EmployeeCount:=if(HASONEVALUE('Date'[Date]),
CALCULATE
(distinctcount('Appointment Detail'[PersonID]),
DATESBETWEEN('Date'[Date], min('Appointment Detail'[ActualStartDate]),Max( 'Appointment Detail'[ActualEndDate]))
)
,BLANK())
Id appreciate help in understanding the problem correctly as im obviously missing something here regarding the problem & my dax experience is also very light.
I would remove the relationships to DimDate and then use something like the following pattern for your measure:
EmployeeCount := CALCULATE( COUNTROWS ( 'Person' ),
FILTER('Appointment Detail',
'Appointment Detail'[ActualStartDate] <= MAX(DimDate[Date])
&& 'Appointment Detail'[ActualEndDate] >= MIN(DimDate[Date])
))
I have problem with my SSAS Cube.
I have to implement basic calculation to my Cube:
sum(Ammount) where BOOK = "Assets" and CD_PRODUCT_L4 <> "LoanLoss"
My dimmension is in image below:
Dimension attributes: BOOK, CD_PRODUCT_L4, CD_PRODUCT_L5, ..
Hierarchy PROD: CD_PRODUCT_L4 - CD_PRODUCT_L5
CREATE MEMBER CURRENTCUBE.[Measures].[Principal Loans]
AS (
except(
[PLV PRH HDIM CB].[BOOK].&[ASSETS_ON],
[PLV PRH HDIM CB].[COD PRODUCT L4].&[51L4]
),
[Measures].[EOM PRINCIPAL_a])
The biggest thing is that conditions are not from same hierarchy level so I can't use except and I know no other way to implement this particular condition set
Please HELP, Thank you
Product dimmension model
I manage to solve the issue by adding hierarchy to my dimension which allowed me to use except as I wanted before but I'am not satisfied with this solution because it looks more like workaround then solution
CREATE MEMBER CURRENTCUBE.[Measures].[Principal Loans] AS
sum((except([PLV PRH HDIM CB].[Hierarchy BOOK].[BOOK].&[ASSETS_ON].children,[PLV PRH HDIM CB].[Hierarchy BOOK].[COD PRODUCT L4].&[51L4]),[Measures].[EOM PRINCIPAL_a]))
This is probably the closest example that I can provide you to what you need.
This, code below, is just one of about 50 or so Measures we have defined, ranging from Counts, Averages, Sums, Percentages, etc. We take a single DW value and perform at least 2 seperation calculations, most have 4 or more different calculations, from that one field.
CREATE MEMBER CURRENTCUBE.[Measures].[Count Previous Difference]
AS Case
// Test for current coordinate being on (All) member.
When [Date].[YearMonth].CurrentMember.Level Is [Date].[YearMonth].[(All)]
Then "NA"
Else
( [Date].[YearMonth].CurrentMember, [Measures].[Gross Count] )
-
( ParallelPeriod (
[Date].[YearMonth].CurrentMember.Level,
1,
[Date].[YearMonth].CurrentMember
),
[Measures].[Gross Count]
)
End,
FORMAT_STRING = "#,##0.00;-#,##0.00",
NON_EMPTY_BEHAVIOR = { [Gross Count] },
VISIBLE = 0 , ASSOCIATED_MEASURE_GROUP = 'Some Summary';
Keep in mind we also use our DW & Cube with SharePoint and SSRS reports.
I am facing some problem to calculate values from comparing dimension's value. I have 3 dimensions (Datatype, Customer, Product) and one measure (GrossSales).
What would be the MDX query if I want GrossSales for ProductID = 1,2,3 and Dataype = 4,5,6?
Here Datatype has relationship with GrossSales, Customer has relationship with GrossSales and Product has relationship with Customer.
I am trying this but doesn't work
CREATE MEMBER CURRENTCUBE.[Measures].Forecast_Gross_Sales AS
(
SELECT NON Empty [Measures].[Gross Sale]
FROM [Measures]
WHERE (
[Data Type].[ID].[ID] = 4
AND [Chain].[Customer ID] = [Measures].[Customer ID]
)
), VISIBLE = 1
, DISPLAY_FOLDER = 'Forecast'
, ASSOCIATED_MEASURE_GROUP = 'Data Types';
It looks like you are just getting started with MDX. There are some fundamental concepts that will help you get what you need. This comparison of SQL and MDX might be helpful. MDX uses the where clause as a slicer (to select certain dimension members) rather than a filter. You can't put member = somevalue in the where clause. And you can't really use the where clause to define a relationship to some other table.
Instead, your where clause would be something more like
[Data Type].[ID].[ID].&[4]
Since I can't see your data model, I can't be sure, but I would guess that [Chain].[Customer ID] = [Measures].[Customer ID] is something that you want to define the the dimension usage of your cube rather than in the query.
Edit: Now that the question has been edited, it looks like you are creating a calculated member. in this case there is no select or where clause. It will look more like this:
CREATE MEMBER CURRENTCUBE.[Measures].Forecast_Gross_Sales AS
Aggregate([Data Type].[ID].[ID].&[4], [Measures].[Gross Sale])
, VISIBLE = 1
, DISPLAY_FOLDER = 'Forecast'
, ASSOCIATED_MEASURE_GROUP = 'Data Types';
The relationship from the measure group through the Customer dimension to the Chain dimension is something that should be defined in the dimension usage. This is called a Reference dimension relationship.
I have following Dimensions of same level :
[Area].[Area ID]
[Discipline].[Discipline ID]
and i have following measure:
[Measures].[Risk Score]
Note: These dimensions are related to the fact through a Many-to-many relations through another fact table (Project)
I want to retrieve the Top 5 Risks for every discipline in every Area so for example,
If we have Discipline: Mechanical; and Area: A & B
I want to get:
1) Top 5 Risks of Mechanical Discipline in Area A
2) Top 5 Risks of Mechanical Discipline in Area B
I tried the following code which for some reason is only working for the First Area :
Exists
(
[Risk Dimensions].[RISKSID].[RISKSID],
GENERATE
(
EXCEPT([Area].[Area ID].MEMBERS, [Area].[Area ID].UNKNOWNMEMBER),
[Area].[Area ID].CURRENTMEMBER
*
GENERATE
(
EXCEPT([Discipline].[Discipline ID].MEMBERS, [Discipline].[Discipline ID].UNKNOWNMEMBER),
[Discipline].[Discipline ID].CURRENTMEMBER
*
TopCount
(
[Risk Dimensions].[RISKSID].[RISKSID],
5,
[Measures].[Assessment P-I Score Float]
)
)
)
)
For some reason it is only working for the First Area and the next one will have extra fields
Any help is appreciated