Imagine a cube with dimensions - DimA, DimB, DimC and DimD.
I want a calculated Member that should only be calculated if sliced against DimA or DimB. If only sliced against DimC or DimD, then it should return Null.
Currently I have achieved this, by using the 3 calculated members below:
Create Member CurrentCube.[Measures].[CalMeasure1] As Null, VISIBLE = 0;
SCOPE([Measures].[CalcMeasure1]);
SCOPE ([DimA].[DimA].[DimA]);
This = [Measures].[Camp Index];
END SCOPE;
END SCOPE;
Create Member CurrentCube.[Measures].[CalcMeasure2] As Null, VISIBLE = 0;
SCOPE([Measures].[CalcMeasure2]);
SCOPE ([DimB].[DimB].[DimB]);
This = [Measures].[Camp Index];
CREATE MEMBER CURRENTCUBE.[Measures].[CalcMeasure3]
AS IIF(ISEMPTY([Measures].[CalcMeasure1]),[Measures].[CalcMeasure2],[Measures].[CalcMeasure1]),
VISIBLE = 1 ;
END SCOPE;
END SCOPE;
I have to create quite a few of these, so ideally, I wanted to do this with a single measure, rather than 3 separate measures. I have tried creating a nested scope. I have also tried putting both dimension hierarchies within a single scope. Neither of these methods work, however. Both methods require that the measure is sliced against both DimA AND DimB. I need it to work if it is sliced against either DimA OR DimB.
Note My code above DOES work, I just want to condense it to a single calculated member
Edit:- Expected Results, for different pivot table usages.
DimA Attribute CalcMeasure3
1 1.1
2 1.1
3 8.6
DimB Attribute CalcMeasure3
4 2.1
5 2.1
6 9.6
DimA Attribute DimC Attribute CalcMeasure3
1 A 1.1
2 B 1.1
3 C 8.6
DimB Attribute DimD Attribute CalcMeasure3
4 D 1.1
5 E 1.1
6 F 8.6
DimC Attribute CalcMeasure3
A (Null)
B (Null)
C (Null)
DimD Attribute CalcMeasure3
D (Null)
E (Null)
F (Null)
How about this calculated measure, without scope:
WITH MEMBER [Camp Index] AS
IIF(
not ([DimA].[DimA].CurrentMember is [DimA].[DimA].[All]) or not([DimB].[DimB].CurrentMember is [DimB].[DimB].[All]),
[You Calcuate],
IIF(
not ([DimC].[DimC].CurrentMember is [DimC].[DimC].[All]) or not([DimD].[DimD].CurrentMember is [DimD].[DimD].[All]),
NULL,
[You Calcuate]
),
NULL
)
SELECT
{[Camp Index]} on 0
from [Adventure Works]
My first guess:
Scope([Measures].[Camp Index]);
This = IIF(
[DimC].[DimC].[DimC].CurrentMember is [DimC].[DimC].[All]
and
[DimD].[DimD].[DimD].CurrentMember is [DimD].[DimD].[All]
[Measures].[Camp Index],
NULL
);
End Scope;
Related
Data : Desired result:
class type number class rate score
------------------------- ----------------------
2021 1 5 2021 0.5 4.8
2021 1 4.6 2022 0.5 4.6
2021 0 4.8
2021 null null
2022 1 4.2
2022 1 5
2022 0 4.2
2022 null null
rate = (type = 1 / all list) group by class.
score = AVG(number) where type = 1 group by class.
I want to do like below:
SELECT
a.class, SUM(type) / COUNT(*) AS rate, b.score
FROM
data as a
LEFT JOIN
(SELECT
class, AVG(number) AS score
FROM
data
WHERE
type = 1
GROUP BY
class) AS b ON a.class = b.class
GROUP BY
class
Is there any method to do this without JOIN?
First some issues should be named:
Do not use SQL key words like type or number as column names or table names.
Do not do a division without ruling out possible dividing by zero exceptions.
Anyway, in case your description is correct, you can do following:
SELECT class,
ROUND(AVG(CAST(COALESCE(type,0) AS FLOAT)),2) AS rate,
ROUND(AVG(CASE WHEN type = 1 THEN COALESCE(number,0) END),2) AS score
FROM data
GROUP BY class;
You can see here it's working correctly: db<>fiddle
Some explanations:
AVG will build the average without doing risky divisions.
COALESCE replaces NULL values by zero to make sure the average will be correct.
ROUND makes sure the average will be shown as example as 0.33, not as 0.33333...
If this is not sufficient for you, please be more precise about what exactly you want to do.
I have this issue with an MDX query where the NON EMPTY clause isn’t working as I expected after adding KPI goals, trends and status to the query.
The basic query looks like this
SELECT
NON EMPTY({[Measure1], [Measure2], KPIValue('MyKpi')})
ON COLUMNS,
NON EMPTY [Dim Country].[Name].[Name].ALLMEMBERS ON ROWS
FROM [BIA CO]
returning something like this, which is fine:
Measure1 Measure2 MyKpi
Canada 7977 4487 3231
USA 6 14 6
UK 442 1179 180
Problems comes when I add KPI goal, trend and status:
SELECT
NON EMPTY({[Measure1], [Measure2], KPIValue('MyKpi'), KPIGoal('MyKpi'), KPIStatus('MyKpi'), KPITrend('MyKpi')})
ON COLUMNS,
NON EMPTY [Dim Country].[Name].[Name]ALLMEMBERS ON ROWS
FROM [BIA CO]
Which returns something like:
Measure1 Measure2 MyKpi MyKpi Goal MyKpi Status MyKpi Trend
Mexico (null) (null) (null) 40300 -1 -1
Cuba (null) (null) (null) 40300 -1 -1
Canada 7977 4487 3231 40300 -1 1
Portugal (null) (null) (null) 40300 -1 -1
China (null) (null) (null) 40300 -1 -1
USA 6 14 6 40300 -1 1
UK 442 1179 180 40300 -1 1
How can I get rid of all those rows with nulls except for the goal, status and trend?
Simply add the Non-empty behavior to your KPIs. If you set Measure1 you'll filter out all empty tuples. It'll inherit the non-empty behavior from the Measure1. Also you may return the the following set on rows with similar logic (the output will be the same):
NonEmpty([Dim Country].[Name].[Name].ALLMEMBERS,[Measures].[Measure1]) on rows
I had a similar problem and this is what I did. In my case I had no control over the cube design. I'm not an expert so wait for some experienced feedback before using it.
WITH
MEMBER [KPIValue(ReservaKPI)] as KPIValue('ReservaKPI')
MEMBER [KPIGoal(ReservaKPI)] as CASE WHEN ISEMPTY([KPIValue(ReservaKPI)]) THEN NULL ELSE KPIGoal('ReservaKPI') END
MEMBER [KPIStatus(ReservaKPI)] as CASE WHEN ISEMPTY([KPIValue(ReservaKPI)]) THEN NULL ELSE KPIStatus('ReservaKPI') END
MEMBER [KPITrend(ReservaKPI)] as CASE WHEN ISEMPTY([KPIValue(ReservaKPI)]) THEN NULL ELSE KPITrend('ReservaKPI') END
SELECT
NON EMPTY({[Cant Adultos], [Cant Noches], [KPIValue(ReservaKPI)], [KPIGoal(ReservaKPI)], [KPIStatus(ReservaKPI)], [KPITrend(ReservaKPI)]})
ON COLUMNS,
NON EMPTY [Dim Mun].[NOMBRE].[NOMBRE].ALLMEMBERS ON ROWS
FROM [BIA CO]
I sometimes like to use the HAVING clause like this maybe:
SELECT
NON EMPTY
{
[Measure1]
,[Measure2]
,KPIValue('MyKpi')
,KPIGoal('MyKpi')
,KPIStatus('MyKpi')
,KPITrend('MyKpi')
} ON COLUMNS
,NON EMPTY
[Dim Country].[Name].[Name].ALLMEMBERS HAVING
(NOT IsEmpty([Measure1])) AND (NOT IsEmpty([Measure2])) ON ROWS
FROM [BIA CO];
But you could move that logic into a WITH clause as suggested by #user1998299 - I'd probably do a custom set:
WITH
SET [SmallSet] AS
NonEmpty
(
NonEmpty
(
[Dim Country].[Name].[Name].ALLMEMBERS
,[Measure1]
)
,[Measure2]
)
SELECT
NON EMPTY
{
[Measure1]
,[Measure2]
,KPIValue('MyKpi')
,KPIGoal('MyKpi')
,KPIStatus('MyKpi')
,KPITrend('MyKpi')
} ON COLUMNS
,NON EMPTY
[SmallSet] ON ROWS
FROM [BIA CO];
I have two dimensions DimFlag and DimPNL and a fact table FactAmount. I am looking to:
When pnl is stat(Is Stat=1) : sum (Actual x FlagId)
For pnl I multiply the amounts by field FlagId basically if it will be so 0 0 X = 0 ...
DimFlag
FlagId FlagLabel
-----------------
1 NotClosed
0 IsClosed
DimPNL
PNLId PNLName Is Stat
1 a 1
2 test 1
3 test2 0
FactAmount
id PNLId FlagId Actual
1 1 1 100
2 2 1 10
3 3 0 120
I tried the following MDX but it didn't work, any idea please ?
Scope (
[Dim PNL].[PNL].members,[Measures].members
);
this = iif([Dim PNL].[PNL].CurrentMember.Properties("Is Stat") =1
,
aggregate([Dim PNL].[PNL].currentmember,[Measures].currentmember)* iif([Dim Flag].[Flag Label].[Flag Label].currentmember = 0, 0, 1),
aggregate([Dim PNL].[PNL].currentmember,[Measures].currentmember)
);
While this type of calculation can be done in MDX, the MDX can get complex and performs bad. I would suggest to explicitly do the calculation e. g. in the DSV or a view on the fact table that you then use instead of the fact table directly in the DSV. The result of the calculation would then be another column on which you can base a standard measure.
To do it in the DSV, assuming you use a relational table as the base for the fact table, add a named calculation to it, define the column name however you like, and use the expression Actual * FlagID. For the other calculation, you may need a subselect, i. e. the expression would be Actual * case when pnlId in(1,2) then 1 else 0 end. You can use any SQL that works as a column expression in the select list as the expression in for a named calculation.
Implementing the same in a view on FactAmount, you could implement the second expression better, as then you could join table DimPNL in the view definition and thus use column IsStat in the calculation. Then you would replace table FactAmout by the view, which has the two additional measure columns.
In either case, just define two measures on the two new columns in the cube, and you are done.
As a rule, calculations that are done on record level in the fact table before any aggregation should be done at data loading time, i. e. as described above.
I have a many-to-many relationship between dimension D and measure M.
I need to create a generic calculated measure that would have the following formula: M/Σ(M), where Σ stands for the sum of all measure's facts that are associated with at least one member of a given dimension.
It's easy for other (one-to-many) dimensions, but getting Σ in many-to-many... well, if it was just a regular MDX query, that would be easy as well. I could just slice on all children of a dimension:
SELECT [Measures].[M] ON 0
FROM [MyCube]
WHERE [D].[All].CHILDREN
But how do I slice in a calculated measure?
A simplified example of what I would expect to work:
CREATE MEMBER [Measures].[Calc] AS
[Measures].[M] / ( DRILLUPLEVEL( AXIS( 1 ).ITEM( 0 ).HIERARCHY.MEMBERS ).ITEM( 0 ), [Measures].[M] )
WHERE DRILLUPLEVEL( AXIS( 1 ).ITEM( 0 ).HIERARCHY.MEMBERS ).ITEM( 0 ).CHILDREN
But of course, MDX doesn't support a WHERE clause in MEMBER definitions.
Please advise me how to approach this.
How about using the aggregate method ?
CREATE MEMBER [Measures].[Calc] AS
[Measures].[M] / Aggregate( DRILLUPLEVEL( AXIS( 1 ).ITEM( 0 ).HIERARCHY.MEMBERS ).ITEM( 0 ).CHILDREN, [Measures].[M] )
In the end, this is what I got:
CREATE HIDDEN [Total M] =
AGGREGATE(
DESCENDANTS(
AXIS( 1 ).ITEM( 0 ).DIMENSION.LEVELS( 0 ).ITEM( 0 ),
AXIS( 1 ).ITEM( 0 ).DIMENSION.LEVELS.COUNT
) - AXIS( 1 ).ITEM( 0 ).DIMENSION.LEVELS( 0 ).ITEM( 0 ),
[Measures].[M]
);
In short, I subtracted the root member from the set of the dimension's members. Aggregating over the remaining set gives proper value in many-to-many relationships. I can now use this measure in other measures to calculate M/Σ(M), etc.
I have the following structure of measures and dimensions, and some example data
Fact Table:
id_dim1 id_dim2 id_dim ...measure1
1 2 ...120
2 1 ...101
1 1 ...95
3 3 ...12
Dim1:
id_dim1 member1value
1 Value1
2 Value2
3 Value3
Dim2:
id_dim2 member2value
1 Value1
2 Value2
3 Value3
Dim1 and Dim2 are actually roleplaying dimension and are based on the same dimension table
I want to calculate a measure which sums measure1 only when:
[Dim1].[Hierarchy1].[Level based on member2value]=[Dim2].[Hierarchy1].[Level based on member2value]
In the above example, the measure would be calculated as: 95+12=107
The catch is that I want the measure calculated correctly even though Dim1 and Dim2 are not used in the later mdx query.
So far I have the following, which only works when Dim1 and Dim2 are used in the later query:
Member [Measures].[CondMeasure] AS 'IIF(
[Dim1].[Hierarcy1].[All].[Level].CurrentMember.Name =
[Dim2].[Hierarchy1].[All].[Level].CurrentMember.Name
,
([Measures].[Measure1],
[DimXX].[Hxx].[LevelXX].[MemberXX],
[DimYY].[Hyy].[LevelYY].[MemberYY])
),
0
)
'
The measure is also only calculated in context of some other members, as demonstrated above.
Btw. I am trying this in SSAS
Regards
Soeren
What do you think about creation additional field in fact table or expression in DSV?
case
when id_dim1 = id_dim2 then measure1
else 0
end
after that you can create new metric...
In some situations it's easier than writing difficult mdx-expressions.