I have users who would like to be able to modify what columns a table aggregates by. My issue is that I seem unable to do this in Power BI. I basically want to be able to do the following in SQL:
SELECT
<OrgLevel1>,
<OrgLevel2>,
SUM([Revenue])
FROM [Data]
GROUP BY
<OrgLevel1>,
<OrgLevel2>
;
where the user can change <OrgLevel1> and/or <OrgLevel2> to be any of { "(All)", [Department], [Product] }.
The issue may be related to this post: https://community.powerbi.com/t5/Desktop/Calculated-Column-Table-Change-Dynamically-According-to-Slicer/m-p/655991#M314800
Here's a link to a workbook that illustrates this issue, TestParameterizeGroupby.pbix (hosted by Google Drive). I've also included field definitions below with screenshots. Thanks for any help.
TestParameterizeGroupby.pbix
Link: TestParameterizeGroupby.pbix (hosted by Google Drive)
Problem
[Org Level 1] and [Org Level 2] fields are not recalculating from the users' selection. Only the default values are shown.
Expected result in table
"Org Level 1", "Org Level 2", "Revenue"
"(All)", "(All)", 28
Note
The purpose is to have parameterizable organization level fields so that the report user can aggregate by all, department, product, or both in either order.
Table and column definitions
'Data' = DATATABLE(
"Department",
STRING,
"Product",
STRING,
"Revenue",
DOUBLE,
{
{"DeptA", "ProdX", 5.0},
{"DeptA", "ProdY", 6.0},
{"DeptB", "ProdX", 10.0},
{"DeptB", "ProdY", 7.0}
}
)
'Data'[Org Level 1] = SWITCH(
'Org Level 1 Parameter'[Org Level 1 Parameter Value],
0,
"(All)",
1,
[Department],
2,
[Product]
)
// Problem: [Org Level 1] and [Org Level 2] fields are not recalculating from the users' selection. Only the default values are shown.
'Org Level 1' = DATATABLE(
"Org Level 1",
STRING,
"Org Level 1 Parameter",
INTEGER,
{
{"(0) (All)", 0},
{"(1) Department", 1},
{"(2) Product", 2}
}
)
'Org Level 1 Parameter'[Org Level 1 Parameter] = GENERATESERIES(0, 2, 1)
'Org Level 1 Parameter'[Org Level 1 Parameter Value] = SELECTEDVALUE('Org Level 1 Parameter'[Org Level 1 Parameter], 1)
Table 'Org Level 1' has a 1-1 relationship with 'Org Level 1 Parameter' on column [Org Level 1 Parameter].
The user selects the value for 'Data'[Org Level 1] by selecting the value for 'Org Level 1'[Org Level 1].
Tables and columns for [Org Level 2] are defined in the same way as [Org Level 1].
Screenshots
Report view:
Data view:
Model view:
Cross-reference to post in Power BI forum:
Power BI Forum: How to parameterize a column for aggregation
One solution to this is to add two list values parameters and use their values in Power Query M code to modify the database query. Lets assume that you have a table Data with columns Department, Product and Revenue. For simplicity I will add one more column, named Dummy Column, with all rows having the same value (e.g. null). I will explain why later in this post. So the table looks like this:
Then in your report specify a query when adding this table to your model (lets assume we will import it, but in general you can do this in DirectQuery too):
Now if you look the M code you will see the above query there:
Source = Sql.Database(".", "StackOverflow", [Query=" select ....
Now define couple of parameters, that the end-user can use to select how the data should be aggregated. Lets name them Level 1 and Level 2:
The value of a parameter can be used in M by parameter name, and & is used to concatenate strings. So if there is a parameter Name with value Samuel, the expression "Hello, " & Name & "!" will be evaluated as Hello, Samuel!. The idea is to check the value of our parameters and modify the database query accordingly.
In the select part, we will replace the name of the field selected, or we will put '' (empty string) in case of <All> (I surrounded parameter values with brackets to be more easily to distinguish parameter values from database field names). So the expression should look like:
"select " & (if #"Level 1" = "<Department>" then "Department" else ..." (and so on)
Because there is a space in our parameter's name, we need to surround it with #" and ", so Level1 can be referenced simply as Level1 in the code, but Level 1 becomes #"Level 1".
The group by part is a bit trickier. We should add a comma between field names, add or not field name, or even omit the group by at all (in case both parameters are set to <All>). To simplify this, I added one dummy column, with all rows having the same value (e.g. null) and always group by this column. This way building the group by clause is way more simpler - in case the parameter value is not <All>, we should add , fieldname. So the code could look like this:
"group by DummyColumn" & (if #"Level 1" = "<Department>" then ", Department" else ..." (and so on)
So the final M code is this:
let
Source = Sql.Database(".", "StackOverflow", [Query="select#(lf) " & (if #"Level 1" = "<Department>" then "Department" else if #"Level 1" = "<Product>" then "Product" else "''") & " as [Org Level 1]#(lf) , " & (if #"Level 2" = "<Department>" then "Department" else if #"Level 2" = "<Product>" then "Product" else "''") & " as [Org Level 2]#(lf) , SUM(Revenue) as Revenue#(lf)from Data#(lf)group by DummyColumn" & (if #"Level 1" = "<Department>" then ", Department" else if #"Level 1" = "<Product>" then ", Product" else "") & (if #"Level 2" = "<Department>" then ", Department" else if #"Level 2" = "<Product>" then ", Product" else "")])
in
Source
Now the end-user can change parameter values, by clicking Edit Queries -> Edit Parameters:
And select how to group the data:
By default, Power BI Desktop will warn you first time, when particular query is executed:
If you want to turn this off, go to File -> Options and settings -> Options -> (GLOBAL) Security and make sure Require user approval for new native database queries is not selected:
When the end-user changes parameter values, the data will change too, e.g.:
Or:
And so on...
This trick works well in Power BI Desktop when every user has its own copy of the .pbix file. However, if you publish it, first changing parameter values is not very convenient (you must go to datasat's settings) and more important, changing parameter values affects all users, which are looking at this report. You can also use it to modify Table.Group statements generated by Power Query Editor, in case you want to aggregate the data in Power BI, but changing the database query is easier and more flexible.
If you want to enable this scenario for concurrent multi-users scenarios for published reports, you can use slicers and What-if parameters. Unfortunately, What-if parameters can be numeric (you can't define the list of values there), so you can use measures to "decode" the int value of the parameter and write some DAX code to perform different aggregations accordingly. It is more work, but if it is needed, it can be made too.
I have a total column that needs to follow the following criteria:
If the type = 1, then it needs to total all the rows that have a '1' and then add "1 - New" at the end, if the type = 2, then it needs to total all the rows that have a '2' and then add "2 - Reprint" at the end, and if the type = 50, then it needs to total all the rows that have a '50' and then add "50 - draft" at the end.
I want to total all my New, all my Reprint, and then drafts. I cannot find an expression that will allow me to do this. any assistance would be great! :-)
Insert a row below your details row that is inside the Type group, but outside the details row. Add an expression like this one into a cell in this new row:
=cStr(countrows()) + iif(Fields!Type.Value = "1", " 1 - New", iif(Fields!Type.Value = "2", " 2 - Reprint", iif(Fields!Type.Value = "50", " 50 - draft", "")))
This expression works because when used within the Type group, the function countrows() will return the number of rows within the group, not the number of rows in the full dataset. There are other aggregate expressions you could use if you were not grouping on Type in your table.
I have a customer name with first name, last name, middle name. I want to concatenate these fields. When I concatenate these fields I am getting some of the fields as null cause as it contains at least one null value field. I have tried with this formula.
{FIRST_NAME}&","&{MIDDLE_NAME}&","&{LAST_NAME}
Ex: I have first name, last name but middle name is null then I am getting entire field as null.
Please help me how to resolve this.
You'll probably want to wrap each field with a formula to adjust for nulls:
// {#FIRST_NAME}
If Isnull({table.FIRST_NAME}) Then "" Else {table.FIRST_NAME}
Then create a formula to concatenate them
// {#FULL_NAME}
{#FIRST_NAME} + "," + {#MIDDLE_NAME} + "," + {#LAST_NAME}
I have a report that brings total sales and total probability sale.
The request was that this be shown in one table as "R"{totalamount}" (R"{totprobamount")".
So i added this together in a variable with the variable expression being
"R" + $F{Totalt} +" (R" + $F{Totalp} +")"
but by doing this the Thousands separator does not show anymore?
If you can add a field for each value you wouldn't do this with String concatenation but by using patterns on text field. add for each field in the properties panel a patter such as R #,##0.00.
if it has to be in a single field you'd need to add an expression to actually format the numbers in the desired way such as for example: "R" + new DecimalFormat("#,##.00").format($F{Totalt}) + " (R" + new DecimalFormat("#,##.00").format($F{Totalp}) + ")"
You can use the FORMAT function to have thousand separator.
FORMAT({totalamount} +{totprobamount},2)
This column become String column so you have to add this column separately , you cant use same column for integer value. Where 2 is for up to 2 decimal value.
I have a Crystal Report with a formula on the page header like this:
dim t as string
t = "DD Waiver Waitlist "
if {rpt_xtab_waitlist.CountyName} = "statewide" then
t = t + {rpt_xtab_waitlist.CountyName}
else
t = t + "for " + {rpt_xtab_waitlist.CountyName} + " County"
end if
formula = t
The problem is not all counties have data for the report, but I still want the page header to contain the county name.
Ultimately, other staff puts a front-end on the report with a combo box for the parameter listing all of the counties, so the parameter UI will not come from Crystal. I can't just change the parameter to dynamic.
I thought I could use the county name from the parameter instead of the data, but that's in an array and I can't figure out how to index it.
I tried:
if {rpt_xtab_waitlist.CountyName} = "statewide" then
t = t + {?County}( RecordNumber)
else
t = t + "for " + {?County}( RecordNumber) + " County"
end if
but it doesn't print the county name corresponding to the selected county in the group tree.
How should I index a parameter array in a formula?
Thanks for any help,
-Beth
Sample county names:
Anoka
Hennepin
Yellow Medicine
Output I get now:
report with page header function suppressed when they select Yellow Medicine county as their parameter from the list of counties
Output I want to get:
report with page header function returning "DD Waiver Waitlist for Yellow Medicine County"
when Yellow Medicine county selection criteria applied returns 0 rows.
I don't think there is a useful index, and for our purposes, they can only select one county as a parameter, so I ended up using the (1) index of the parameter array.
If anyone knows of a way to derive a meaningful index, please post.