I want to dynamically change the number format of a DAX measure, based on a dimension value (or indeed, based on the order of magnitude of the measure value).
I understand I can use SWITCH and FORMAT, as demonstrated by Kaspar De Jonge here: https://www.kasperonbi.com/dynamic-format-using-dax/
Here's an example of the type of measure I'm creating:
My Measure:=IF (
HASONEVALUE ( dimMeasureType[Measure Type] ),
SWITCH ( VALUES ( dimMeasureType[Measure Type] ),
"Total Cost", FORMAT ( [Total Cost], "#,##0, k" ),
"Cost Per Unit", FORMAT ( [Cost Per Unit], "#,##0.00" ),
"Cost % Sales", FORMAT ( [Cost % Sales], "0.00%" ),
BLANK()
),
BLANK()
)
But this technique returns text measures. I need to be able to chart my measures, so I do not want to convert them to text. Is there another technique for dynamically changing a measure number format, without converting to a string?
If it makes a difference, I'm working in SSAS-Tabular on SQL Server 2016 BI.
I don't believe this is currently possible, but it a popular feature request that will hopefully be implemented in the future.
I recommend voting and commenting on the idea I linked to in order to add your support.
A workaround is to create multiple measures and add them all to your chart. Depending on your dimension value only one measure returns values, all other measures return BLANK() and are not displayed in your chart. You can give them the same display name by adding whitespace to the end of their names:
My Measure:=IF (
SELECTEDVALUE( dimMeasureType[Measure Type] ) = "Total Cost",
[Total Cost],
BLANK()
)
[My Measure ]:=IF (
SELECTEDVALUE( dimMeasureType[Measure Type] ) = "Cost Per Unit",
[Cost Per Unit],
BLANK()
)
[My Measure ]:=IF (
SELECTEDVALUE( dimMeasureType[Measure Type] ) = "Cost % Sales",
[Cost % Sales],
BLANK()
)
This has some drawbacks though:
The chart legend shows all measures, even if all their values are BLANK().
The y-Axis of your chart has the same format as the first measure in its 'Values' section.
Related
I need to write a DAX measure that calculates (e.g., "Count Rows"), but only when another measure value is evaluated (e.g., filtering "[Sales]>100"). So if-- in the context of the selected filters-- Sales is great than 100, then the measure is executed only for those rows.
The measure I have defined works in the context of lower smaller grain. But the totals do not sum correctly.
Any suggestions?
DAX Measure
License Usage =
// Users with active viewership in 3 months
IF (
NOT ( ISBLANK (
CALCULATE (
[Activity Date NEWEST],
KEEPFILTERS ( DATESINPERIOD ( dimCalendar[Date], TODAY (), -90, DAY ) )
)
)), 1
)
Activity Date NEWEST =
MAX('PBI Activity'[Date])
Okay, I figured something out that works.
DAX
License Usage =
// Users with active viewership in 3 months
CALCULATE (
[Count Users],
FILTER ( 'PBI Activity', 'PBI Activity'[Date] >= TODAY () - 90 )
)
Count Users = COUNTROWS('Users')
Also, I later came across this article which looks like it also does what I was hoping to do: Execute calculate expression over filtered rows based upon measure filter.
Reference: Specifying multiple filter conditions in CALCULATE - SQLBI
DAX
DEFINE
MEASURE Sales[Big Sales Amount] =
CALCULATE (
[Sales Amount],
KEEPFILTERS (
FILTER (
ALL ( Sales[Quantity], Sales[Net Price] ),
Sales[Quantity] * Sales[Net Price] > 1000
)
)
)
EVALUATE
SUMMARIZECOLUMNS (
Sales[Quantity],
"Sales Amount", [Sales Amount],
"Big Sales Amount", [Big Sales Amount]
)
I am building a report about costumer complaints.
example
Now I am trying to get the right sum for "Reklamationskosten" (cost).
The correct answer is: 113 EUR.
The formula should do (in words):
"Sum the 'Reklamationskosten' for each 'Rekl. ID' but only once for each 'Reklamationskosten Art' "
Sure there is a way to do this in DAX but I cannot find out how.
Thank you all very much in advance!
For this you can create a measure using SUMX that iterates over a summarized table:
Reklamationskosten Measure =
SUMX (
SUMMARIZE (
'Table',
'Table'[Rekl. ID],
'Table'[Reklamationskosten Art],
'Table'[Reklamationskosten]
),
[Reklamationskosten]
)
This assumes that each "corresponding" row of Reklamationskosten Art has the same value of Reklamationskosten - else the values will be duplicated. You can alter the functionality of this by introducing aggregators handling multiple values using e.g. MAX:
Reklamationskosten Measure (Agg) =
SUMX (
SUMMARIZE (
'Table',
'Table'[Rekl. ID],
'Table'[Reklamationskosten Art],
"Reklamationskosten", MAX ( 'Table'[Reklamationskosten] )
),
[Reklamationskosten]
)
Link: https://www.sqlbi.com/articles/specifying-multiple-filter-conditions-in-calculate/
I am reading this article about a new DAX feature added to CALCULATE, that allows referencing multiple columns in the same predicate. Example:
Red or Contoso Sales :=
CALCULATE (
[Sales Amount],
'Product'[Color] = "Red" || 'Product'[Brand] = "Contoso"
)
This question is not about the above concept, but about the quote below. Specifically - Why is the author saying that using table filter is a common error? What is the problem with using table filter instead of a multi-column filter? I want an example to understand this.
Red or Contoso Sales := CALCULATE (
[Sales Amount],
FILTER (
ALL ( 'Product'[Color], 'Product'[Brand] ),
'Product'[Color] = "Red" || 'Product'[Brand] = "Contoso"
) )
A common error is to use a table filter instead of a multi-column
filter. For example, the Big Sales Amount measure or the initial
example is often written in this sub-optimal manner:
Big Sales Amount := CALCULATE (
[Sales Amount],
FILTER (
Sales,
Sales[Quantity] * Sales[Net Price] > 1000
) )
When you create a filter clause, the DAX engine creates a table. So in this case
Big Sales Amount := CALCULATE ( [Sales Amount], FILTER ( Sales, Sales[Quantity] * Sales[Net Price] > 1000 ) )
It is loading the table 'Sales' completely in the background, then applying the formula on two columns in that table. If it is a wide table in terms of the number of columns, this will use up memory with irrelevant columns needed for the calculation. Marco Russo calls this a 'slow' pattern.
So it would be better to create a measure that only uses the two columns needed, so the filter creates a table in the background that is the minimum for use.
So the above could be done as
Big Sales Amount := CALCULATE ( [Sales Amount], FILTER ( ALL ( Sales[Quantity], Sales[Net Price] ) * Sales[Net Price] > 1000 ) )
So the multi-column version is a lot lower in the memory overhead (called a 'fast' pattern) as it is only using the two columns needed, but as you use ALL you may need a KEEPFILTER in the formula to get the correct results. The DAX Engine has been updated and optimized so it will do this sort of table to column reduction without declaring it explicitly.
So, I have the following tables in my Power BI :
Sales : Date | ID_Client | ID_Product | Amount
Client : ID_Client | Name_Client
I would like to get the number of unique BIG clients in any given month. I therefore use the following formula (which I then put in a column in a table with months in rows):
# BIG Clients =
VAR threshold = 10000
RETURN
(
CALCULATE(
DISTINCTCOUNT( Sales[ID_Client] ),
FILTER(
SUMMARIZE(
Sales,
Sales[ID_Client],
"Sales", SUM( Sales[Amount] )
),
[Sales] >= threshold
)
)
)
QUESTION IS : how can I get the list of those BIG clients for any given month? Let's say I click on the November number of big clients in my table, could another table nearby display the list of those clients ?
Thanks in advance for your kind help, I've been trying for a while :)
I assume that you have a table of clients with the Name column with a one to many relationship with the Sales table and that you do not have duplicate client names. Then you may create a [BIG Sales] measure to be used in a table or matrix visual with client names on the rows.
since [BIG Sales] evaluates to BLANK() for clients with less that threshold sales, they are automatically filtered out from the visual
BIG Sales =
VAR threshold = 10000
VAR BigCustomers =
FILTER(
ADDCOLUMNS(
VALUES( Clients[Name] ),
"Sales", SUM( Sales[Amount] )
),
[Sales] >= threshold
)
RETURN
SUMX(
BigCustomers,
[Sales]
)
You could create a table or matrix visual with the client on the rows and use your measure in the values field. This will show 1 for all big clients and return blank for the rest (which should hide them). If you don't want to show the measure, you can set the value is 1 in the filter pane and remove the measure from the values field.
A more direct option is to use a simple SalesAmount = SUM ( Sales[Amount] ) measure in the values field and filter like this
I have a table that people use in a filter and select multiple items from the for example the values in this table are
US Dollar
Canadian Dollar
Category 1
Category 2
The users will select US Dollars and Calendar Year, which I need to affect a 2 DAX measures. The first measure should be the sum 1 of 2 different columns, something similar to
Currency Amount = CALCULATE(
if(SELECTEDVALUE('Filter'[Description])="USD",
Sum(Test[USD Amount]),
Sum(Test[CD Amount])
))
Then the second measure should be something similar to the below, but what is below doesn't work and I'm fairly certain there is a better way to write this overall:
Currency Category Amount =
if(SELECTEDVALUE('Filter'[Description])="Cat 1",
CALCULATE(
[Currency Amount],
Filter(Test, Test[Category]="Cat 1")),
CALCULATE(
[Currency Amount],
Filter(Test, Test[Category]="Cat 2"))
)
The problem with this is that the slicer is multi select and the Selected Value function doesn't appear to work correctly with this.
I have come up with the following that matches what I'm looking for. If somebody can come up with a better solution I will gladly accept that instead. The main this I don't like about this is the duplication of the entire things. As I mentioned in the comment the actual filter that I would be using is much more complex, something more along the lines what is below.
Currency Category Amount = if(
Contains(Values('Filter'[Description]), 'Filter'[Description], "Cat 1"),
CALCULATE(
[Currency Amount],
Filter(Test, Test[Category]="Cat 1")),
CALCULATE(
[Currency Amount],
Filter(Test, Test[Category]="Cat 2"))
)
Cat 1
FILTER (
ALL ( 'Calendar' ),
'Calendar'[Month In Fiscal Year] <= MAX ( 'Calendar'[Month In Fiscal Year] )
&& 'Calendar'[Fiscal Year] = MAX ( 'Calendar'[Fiscal Year] )
)
Cat 2
FILTER (
ALL ( 'Calendar' ),
'Calendar'[Month In APY] <= MAX ( 'Calendar'[Month In APY] )
&& 'Calendar'[APY] = MAX ( 'Calendar'[APY] )
)
A way to have less code repetition is to encapsulate some of the calculations in different measures, in this way, if you need the same calculation logic you can just use the appropriate measure (the logic will be centralized in one point).
I've used the SWITCH (TRUE(), ...) to choose the appropriate measure since it allows you to have a cleaner code if the options are more than 2, and to set a default result if there are no selections or if the selected value is not valid/mapped.
Note that there might be some syntax errors since I don't have a datamodel to validate the formulas.
Currency Amount = CALCULATE(
--you probably have already replaced this with something different from SELECTEDVALUE
--If more than 2 currency exists, using SWITCH might be better (Pros above)
if(SELECTEDVALUE('Filter'[Description])="USD",
Sum(Test[USD Amount]),
Sum(Test[CD Amount])
))
Amount YTD (Fiscal Cal) =
CALCULATE(
[Currency Amount]
,ALL ( 'Calendar' )
,'Calendar'[Month In Fiscal Year] <= MAX ( 'Calendar'[Month In Fiscal Year] )
&& 'Calendar'[Fiscal Year] = MAX ( 'Calendar'[Fiscal Year] )
)
Amount YTD (APY) =
CALCULATE(
[Currency Amount]
,ALL ( 'Calendar' )
,'Calendar'[Month In APY] <= MAX ( 'Calendar'[Month In APY] )
&& 'Calendar'[APY] = MAX ( 'Calendar'[APY] )
)
Currency Category Amount =
SWITCH(
TRUE()
,Contains(Values('Filter'[Description]), 'Filter'[Description], "Cat 1")
,[Amount YTD (Fiscal Cal)]
,Contains(Values('Filter'[Description]), 'Filter'[Description], "Cat 1")
,[Amount YTD (APY)]
,BLANK() --If nothing match return blank (remove it if not needed)
)
I've noticed that your 'Filter' table contains several entities (currency, year/month), if viable/appropriate, splitting it into several filter tables 'FilterCurrency', 'FilterPeriod', etc... might help you by enabling the "always one selected" in slicers, Which in some cases is very useful.