Calculate monthly balance in PowerBI - powerbi

Data-Table:
DB-Fiddle
CREATE TABLE vouchers (
id SERIAL PRIMARY KEY,
event_date DATE,
credits_collected INT,
credits_redeemed INT
);
INSERT INTO vouchers
(event_date, credits_collected, credits_redeemed
)
VALUES
('2020-01-08', '900', '700'),
('2020-02-15', '500', '300'),
('2020-02-20', '100', '250'),
('2020-03-19', '600', '850'),
('2020-04-03', '450', '130');
SQL-Query:
SELECT
t1.event_month AS event_month,
t1.credits_collected AS credits_collected,
t1.credits_redeemed AS credits_redeemed,
SUM(t1.credits_collected - t1.credits_redeemed) OVER (
ORDER BY t1.event_month ASC ROWS UNBOUNDED PRECEDING) AS balance
FROM
(SELECT
DATE_PART('month', v.event_date) AS event_month,
SUM(v.credits_collected) AS credits_collected,
SUM(v.credits_redeemed) AS credits_redeemed
FROM vouchers v
GROUP BY 1
ORDER BY 1) t1
GROUP BY 1,2,3
ORDER BY 1;
Result:
event_month | credits_collected | credits_redeemed | balance
-------------|---------------------|--------------------|------------
1 | 900 | 700 | 200
2 | 600 | 550 | 250
3 | 600 | 850 | 0
4 | 450 | 130 | 320
I am loading the above data-table into PowerBI.
Now, I want to create a report that looks like the results I am getting with the SQL-Query above.
I am able to put credits_collected and credits_redeemed to the report.
However, I have no clue what DAX formula I need to calculated the balance for the end of each month.
Do you have any idea how I can solve this issue?

I could solve the issue with two steps:
Step 1: Implementing an additional column with this DAX formula:
column_balance_calculated_daily = CALCULATE(SUM(Tabelle1[credits_collected])-SUM(Tabelle1[credits_redeemed]),ALL('Tabelle1'),'Tabelle1'[event_date]<=EARLIER('Tabelle1'[event_date] ))
Step 2: Use the column in the following DAX formula:
balance = CALCULATE(MAX(Tabelle1[column_balance_calculated_daily]),ENDOFMONTH(Tabelle1[event_date]))

Related

How to perform two different aggregation on one measure and further aggregate the results to view as one aggegated measure in Tableau

I have a dataset where I wish to place the data in two groups, based on the value name. I then wish to average the result for one group and sum the result for the other. Finally, I wish to sum these two to create a barchart.
updated
Here is the data
Id Total Avail Date group used
A 10 5 9/1/2020 Group1 5
A 40 20 9/1/2020 Group1 20
B 20 10 9/1/2020 Group2 10
B 10 5 9/1/2020 Group2 5
B 10 5 9/1/2020 Group2 5
A 20 10 9/1/2020 Group1 10
A 20 10 9/1/2020 Group1 10
B 10 5 9/1/2020 Group2 5
B 10 5 9/1/2020 Group2 5
This is what I have done
1.
First create calc. field named : group - group the data into groups
IF [Id] = 'A' THEN 'Group1'
ELSEIF [Id] = 'B' THEN 'Group2'
ELSE 'none'
END
Then create calc. field - sum_avg sum of Group1 (A) columns and take average of Group2 (B)
CASE [group]
WHEN 'Group1' THEN { FIXED [Id]: SUM([Avail])}
WHEN 'Group1' THEN {FIXED [Id]: SUM([used])}
WHEN 'Group1' THEN {FIXED [Id]: SUM([Total])}
WHEN 'Group2' THEN { FIXED [Id]: AVG([Avail])}
WHEN 'Group2' THEN {FIXED [Id]: AVG([used])}
WHEN 'Group2' THEN {FIXED [Id]: AVG([Total])}
END
Here is that result:
Desired result:
I wish to add the Group1(sum) and the Group2(avg) for the Avail, Used and Total so that
the final chart combines the two blue values and combines the green values.
The SUM of Group1A Avail = 45 and the AVERAGE of G Avail Group2 = 6
So I wish the Avail section (blue) of the barchart to be: 51
and the Used (green) should be 51 as well
with the total as 102 (Ill add to tooltip)
Any suggestion is appreciated- I am still researching this and any suggestion is appreciated
UPDATE
I have a dataset where I wish to reflect the totals from the custom SQL query. Here is some sample data:
Size Tb Val type Group Sum_AVG SKU Last_refreshed
270 90.5 Free_Space_TB Group2 90.5 Excel 9/1/2020
270 179.5 Used Group2 179.5 Excel 9/1/2020
Here is the custom query output
Here is my view
The avail and used appear when I hover over, but how would I include the total?
This is the calculation I am using (thanks to help from a SO member):
{SUM({Fixed [type]: ZN(sum(if [Group]= 'Group1' then [Val] end))})
+
sum({Fixed [type]: zn(avg(if [Group] = 'Group2' then [Val] end))})}
SUM_AVG is:
zn(sum(if [Group]= 'Group1' then [Val] end))
+
zn(avg(if [Group] = 'Group2' then [Val] end))
I am doing something wrong, because it is totaling up across all the columns, when I just want the total for each column.
(Used was created from using a custom query)
The following solution is proposed-
The total column is of no use. Please drop it. It will unnecessary increase the size of your data. (for tooltip I'll tell you how to do it).
sample data used
+----+-------+--------+------+
| Id | Avail | group | used |
+----+-------+--------+------+
| A | 5 | Group1 | 5 |
+----+-------+--------+------+
| A | 20 | Group1 | 20 |
+----+-------+--------+------+
| B | 10 | Group2 | 10 |
+----+-------+--------+------+
| B | 5 | Group2 | 5 |
+----+-------+--------+------+
| B | 5 | Group2 | 5 |
+----+-------+--------+------+
| A | 10 | Group1 | 10 |
+----+-------+--------+------+
| A | 10 | Group1 | 10 |
+----+-------+--------+------+
| B | 5 | Group2 | 5 |
+----+-------+--------+------+
| B | 5 | Group2 | 5 |
+----+-------+--------+------+
pivot two columns (used and avail). A gif is included below
Create a calculated field CF as
zn(sum(if [Group]= 'Group1' then [Val] end))
+
zn(avg(if [Group] = 'Group2' then [Val] end))
Drag AGG(CF1) to rows shelf and to text also, type to colors in marks card; you'll get your desired view with 51 and 51 values in both types used and avail.
for total tooltip i.e. 102 create a calc field total as
{SUM({Fixed [Type]: ZN(sum(if [Group]= 'Group1' then [Val] end))})
+
sum({Fixed [Type]: zn(avg(if [Group] = 'Group2' then [Val] end))})}
add this field to details in marks card (instead of tooltip as you do normally).
click on tooltip and edit calculation there as per taste. I have edited it like
Out of total <total> TB SKU, <AGG(CF1)> was <Type>
You'll get tooltip like this.
and
P.S./EDIT This is regarding pivoting data in tableau. Instead of connecting to complete data through SQL connection, you can modify the sql query by following the instructions mentioned here. Two options-
Option-1 creating used column in sql and pivoting in tableau. While importing data/creating connection use this query-
select ID, date, avail, total - avail AS used, group from table_name
Option-2 pivot the data in sql itself. Use this query then-
select ID, date, avail as val, "avail" as type, group from table_name
UNION
select ID, date, total - avail AS val, "used" as type, group from table_name
thereafter you can proceed in tableau.
See the tableau uses long data format and your data is in wide format. You should keep your SKU memory allocation in rows instead of columns so whenever you'll need a division, adding an extra field type to marks card will do the job. If instead you will have it in columns, these are always two separate measures whereas in actually it is one measure. I suggest you to read some articles on internet about tidy data format, wherein you'll understand what to keep in rows, columns and column_names clearly. Reshaping data to a correct and tidy format solves a lot of problems.
Remember, if there is any value in variable(column) name you have to pivot a data. Here used and avail are variable values and therefore these cannot be in column_names (i.e. variable names) I think I am pretty clear.
Good Luck.

Sum Where version is highest by another variable that can be split by another columns

As a continuation to this question:
I would like to have a measure that will sum the Value only for the max version of each house.
So following this example table (data table):
|---------------------|------------------|------------------|------------------|
| House_Id | Version_Id | Color_Id | Value |
|---------------------|------------------|------------------|------------------|
| 1 | 1 | 1 (Green) | 1000 |
|---------------------|------------------|------------------|------------------|
| 1 | 2 | 2 (Red) | 2000 |
|---------------------|------------------|------------------|------------------|
| 2 | 1 | 1 (Green) | 3000 |
|---------------------|------------------|------------------|------------------|
| 3 | 1 | 1 (Green) | 5000 |
|---------------------|------------------|------------------|------------------|
The result of this measure should be: 10.000 because the house_id 1 version 1 is ignored as there's another version higher.
If there were more versions, the measure should only take into account the highest of each house.
By House_id the result should be (Again, House_Id 1 / Version 1 is ignored):
|---------------------|------------------|
| House_Id | Value |
|---------------------|------------------|
| 1 | 2000 |
|---------------------|------------------|
| 1 | 3000 |
|---------------------|------------------|
| 2 | 5000 |
|---------------------|------------------|
| Total | 10000 |
|---------------------|------------------|
I also want this measure to be capable of showing the result when using another variable of the Data table (or related tables), but maintaining the logic of Max version per House.
As shown on the example table before I have the Color_Id column.
This Color_Id in the main table is connected to a Color table that contains the color name.
If I add a visual table with ColorName (from the ColorTable) and the measure, the result should be as follows:
|---------------------|------------------|
| ColorName | Value |
|---------------------|------------------|
| Green | 8000 |
|---------------------|------------------|
| Red | 2000 |
|---------------------|------------------|
| Total | 10000 |
|---------------------|------------------|
With the solution provided in the other question, the result is correct on the Total row, but is wrong because it does not show the correct value for each color.
The following table is the result of applying the measure in the question provided (wrong result):
|---------------------|------------------|
| ColorName | Value |
|---------------------|------------------|
| Green | 9000 | <- Error Here
|---------------------|------------------|
| Red | 2000 |
|---------------------|------------------|
| Total | 10000 |
|---------------------|------------------|
This result is wrong per ColorName as 9000 + 2000 is 11000 and not 10000.
The measure should ignore the rows with an old version per house.
In the example before this is the row for House_Id 1 and Color_Id Green because the version is old (there's a newer version for that House_Id).
So:
How can I change the measure so it shows the correct value per Color_Id as well as per House_Id and as a total?
In the data table I have more columns. What If I want to filter by another column from (or related to) the Data table such as Location_Id? It is posible to define the measure in such a way that could work for any given number splits for columns in the main Data table?
Help is greatly appreciated here
EDIT:
The solution provided by Alexis Olson works when the Data table is imported. When the Data table is connected with DirectQuery mode, it won't work.
In RADO's answer, the issue is with the first variable.
Sum of Latest Values =
VAR Latest_Versions =
SUMMARIZE ( Data, Data[House_id], "Latest_Version", MAX ( Data[Version_Id] ) )
VAR Latest_Values =
TREATAS ( Latest_Versions, Data[House_id], Data[Version_Id] )
VAR Result =
CALCULATE ( SUM ( Data[Value] ), Latest_Values )
RETURN Result
The Data table as the first argument in the SUMMARIZE is not the whole table but evaluated within the local filter context. This means that when you are in the Green row in your table, it doesn't see Version_ID = 2 and thus includes the first version in the Green row but not in the total (which sees all of the rows).
The fix is quite simple -- remove the local filter context from that first table argument. One way to do this is to use ALL ( Data ) instead of just Data. This is likely not the most memory-efficient though and you may prefer to write something like this instead:
Sum of Latest Values =
VAR Latest_Versions =
ADDCOLUMNS (
VALUES ( Data[House_Id] ),
"Latest_Version",
CALCULATE ( MAX ( Data[Version_Id] ), ALLEXCEPT ( Data, Data[House_Id] ) )
)
VAR Latest_Values =
TREATAS ( Latest_Versions, Data[House_id], Data[Version_Id] )
VAR Result =
CALCULATE ( SUM ( Data[Value] ), Latest_Values )
RETURN
Result

ALL() isn't working to "remove a filter" in DAX; relationship issue?

Basic premise:
'Orders' are comprised of items from multiple 'Zones'.
Customers can call in for 'Credits' (refunds) on botched 'Orders'.
There is a true many-to-many relationship here, because one order could have multiple credits called in at different times; similarly, a customer can call in once regarding multiple orders (generating only one credit memo).
'Credits' granularity is at the item level, i.e.
CREDIT | SO | ITEM | ZONE | CREDAMT
-------------------------------------------------------
42 | 1 | 56 | A | $6
42 | 1 | 52 | A | $8
42 | 1 | 62 | B | $20
42 | 2 | 56 | A | $12
'Order Details' granularity is at the zone level, i.e.
SO | ZONE | DOL_AMT
-------------------------------
1 | A | $50
1 | B | $20
1 | C | $100
2 | A | $26
I have a 'Zone' filter table that helps me sort things better and roll up into broader categories, i.e.
ZONE | TEMP | SORT
-------------------------------
A | DRY | 2
B | COLD | 3
C | DRY | 1
What I need:
I want a pair of visuals for a side by side comparison of order total by zone next to credit total by zone.
What's working:
The 'Credits' component is easy, CreditTotal = abs(sumx(Credits,Credits[CREDAMT])) with Zone as a legend item.
I have a ORDER component that works when the zone is in the credit memo
Order $ by Zone =
CALCULATE (
SUM ( 'Order Details'[DOL_AMT] ),
USERELATIONSHIP ( 'Order Details'[SO], Credits[SO] ),
ALL ( Credits[CreditCategory] )
)
My issue:
Zones that didn't have a credit against them won't show up. So instead of
CREDIT | ZONE | ORDER $ BY ZONE
----------------------------------
42 | A | $76
42 | B | $20
42 | C | $100
I get
CREDIT | ZONE | ORDER $ BY ZONE
----------------------------------
42 | A | $76
42 | B | $20
I have tried to remove this filter by tacking on ALL(Zones[Zone]) and/or ALL('Order Details'[Zone]), but it doesn't help, presumably because it is reporting "all zones" actually found in the 'Credits' table. I'm hoping there's some way to ask it to report all zones in the 'Order Details' table based upon SOs in the 'Credits' table.
In case it helps, here's how the relationships are structured; as an aside, I've tried mixing and matching various combinations of active/inactive, single vs. bidirectional filtering, etc., but the current configuration is the only one that seems to remotely work as desired.
I'm grateful for any suggestions; please let me know if anything is unclear. Thank you.
I was able to get it to work by using 'Order Details'[Zone] rather than Zones[Zone] in the table visual and this measure:
Order $ by Zone =
CALCULATE (
SUM ( 'Order Details'[DOL_AMT] ),
USERELATIONSHIP ( 'Order Details'[SO], Credits[SO] )
)
Notice that regardless of your measure, there is no row in Credits corresponding to zone C, so it doesn't know what to put in the CREDIT column unless you tell it exactly how.
If you remove the CREDIT dimension column, then you don't need to swap tables as I suggested above. You can just use the measure above and then write a new measure for the CREDIT column instead:
CreditValue =
CALCULATE(
VALUES(Credits[CREDIT]),
ALL(Credits),
Credits[SO] IN VALUES('Order Details'[SO])
)

SUM of column conditional to many values of another column

I am trying to accomplish something, but don't know how to do it.
I have a Dimension (Table called TEntry) that represents time entries for employees like so :
Id | EmployeeId | EntryDT | TimeInMinutes | PriceAgreementId
------ | ---------- | ---------- | ------------- | ----------------
1 | 1 | 2017-03-20 | 100 | 1
2 | 1 | 2017-03-31 | 50 | null
3 | 2 | 2017-03-21 | 100 | 1
4 | 2 | 2017-03-23 | 125 | 2
5 | 3 | 2017-03-15 | 90 | null
6 | 3 | 2017-03-25 | 60 | 1
Sometimes they work on "PriceAgreements", and sometimes they don't.
In my Dashboard, i have a Table that groups the table TEntry by EmployeeId and Sums the TimeInMinutes. I also have a Slicer for EntryDT :
EmployeeId | TimeInMinutes
-------------- | -------------
1 | 150
2 | 225
3 | 150
I need to create 2 new columns that represent :
The total TimeInMinutes an Employee has worked on all PriceAgreements
So for EmployeeId #1, the Total would be 100.
The total TimeInMinutes ALL Employees have worked, but only for the PriceAgreements the current Employee (current row) has worked on.
The Table would look like this (without the PriceAgreementIds in parenthesis) :
EmployeeId | TimeInMinutes | TimeInMinutes on PriceAgreements | TimeInMinutes on PriceAgreements ALL other EmployeeIds
-------------- | ------------- | -------------------------------- | ------------------------------------------------------
1 | 150 | 100 (PriceAgreementId=1) | 260 (PriceAgreementId=1)
2 | 225 | 225 (PriceAgreementId=1 and 2) | 385 (PriceAgreementId=1 and 2)
3 | 150 | 150 (PriceAgreementId=1) | 260 (PriceAgreementId=1)
Column "TimeInMinutes on PriceAgreements" is quite easy, but the other one, i cannot find a solution...
I have this DAX expression I started, but it is not complete:
CALCULATE(SUM(TEntry[TimeInMinutes]), NOT ISBLANK(TEntry[PriceAgreementId]), ALL(TEmployee))
TEmployee is a Dimension linked to the main TEntry Table.
Any help would be appreciated.
Thank you
I'm throwing this on as an answer because (a) it might get you (or someone else) going in the right direction and (b) if it's guaranteed that an Employee would only ever have time entries corresponding to 2 price agreements, this would work - which is unlikely the case for you, but might be the case for others trying to accomplish a similar thing.
Measure =
CALCULATE (
SUM ( TEntry[TimeInMinutes] ),
FILTER (
ALL ( TEntry ),
(
TEntry[PriceAgreementID] = MIN ( TEntry[PriceAgreementID] )
|| TEntry[PriceAgreementID] = MAX ( TEntry[PriceAgreementID] )
)
&& TEntry[PriceAgreementID] <> BLANK ()
)
)
This measure is saying: SUM the TimeInMinutes for all records in the TEntry table where the PriceAgreementID matches either the minimum OR maximum PriceAgreementID (in the context of the current row) AND the PriceAgreementID isn't blank.
The fatal flaw in this answer is in the MIN and MAX. For Employee ID 2, who has 2 PriceAgreementIDs (1 & 2) - the MIN will calculate the minutes for PriceAgreementID 1 and the MAX will calculate the minutes for PriceAgreementID 2. However, to expand to a case where there might be more than 2 PriceAgreements...I don't know how to do that.
It does work on the sample data in your question, though (since there is a max of 2 price agreements per employee):
Typically when I'm faced with a problem like this that isn't easy to solve, I think about my data model and make sure that it conforms to a star schema as closely as possible.
In your case, an employee can have multiple price agreements, and a price agreement can be associated with many employees. That, to me, suggests a many-to-many relationship. I'd strongly recommend reading more about many-to-many relationships and whether restructuring the underlying tables (e.g. to include a bridge table) would help get you closer to the answer you need.
A good starting point might be: https://www.sqlbi.com/articles/many-to-many-relationships-in-power-bi-and-excel-2016/

% of Grand Total of a Measure that uses other Measures and is Crossfiltered

this seems so easy in my head but I haven't been able to get it for the last few hours....
I have a Table visualization that provides Cost by Hour using measures.
Category | Total Cost | Hours | Cost per Hour
A | 1000 | 10 | 100
B | 2000 | 100 | 20
C | 100 | 4 | 25
D | -500 | 100 | -5
Total | 2600 | 214 | 12.1495
For my purposes, I would also like to create a % of Grand Total of Cost per hour to add to a treechart visualization. However, if I simply add [Cost per Hour] to the treechart again and use the "quick clac" functionality on the field it would return 823.7% for the first record in the above table as (100/12.1495) = 8.2307. I would like this % of GT of Cost per Hour to use the total sum of the Cost per Hour column. Desired Result:
Category | Total Cost | Hours | Cost per Hour | % of Cost per Hour
A | 1000 | 10 | 100 | 71.4%
B | 2000 | 100 | 20 | 14.3%
C | 100 | 4 | 25 | 17.9%
D | -500 | 100 | -5 | -3.8%
Total | 2600 | 214 | 12.1495 | 100%
A few things to note that makes the application of any DAX challenging. All of the below Measures are filtered by multiple filter visualizations from Tables 1-5 and page level filters from Tables 1-5
The table visualization exists in Table1. Costs exist in Tables 2-5 and are related to Table1 using a Many-to-One Single Direction Filter Relationship.
[Total Cost] is a Measure that adds together values from 4 different tables. Eg:Total Cost = sum(table2[value])+sum(table3[value])+sum(table4[value])+sum(table5[value])
[Hours] is a Measure that adds together a column from a table and divides by the distinct count of records in that table. Eg:Hours = sum(table1[hours])/Distinctcount(table1[records])
[Cost per Hour] is a Measure consisting of two other measure.Cost per Hour = [Total Cost] / [Hours]
I sort of feel like this is similar to people wanting to add percentages to pie charts... I'm just trying to ascribe a real number to express the proportion displayed in the TreeChart visualization. I really hope that this is easier than it seems.
EDIT #alejandrozuleta:
Table1 is the original table from which tables 2-5 are referenced&created. An index number was assigned in Table1 and tables 2-5 are linked on this reference number. The reason that tables 2-5 exists separately is because they contain separate cost "types" and a join that occurs in these tables adds additional columns that are only applicable to specific costs types.... for example Table2 is Personnel Costs:
index | Category | Cost Type | Value | Age of Personnel
1 | A | Personnel | 1 | 33
and Table3 is Maintenance Costs:
index | Category | Cost Type | Value | Scheduled or UnScheduled Maint
2 | A | Maintenance | 5 | Scheduled
The if [Age of Personnel] existed in Table3 then it would have a "null" for any record of the Maintenance [Cost Type] vice-versa [Scheduled or UnScheduled Maint] would have a "null" if it existed in Table2. Because I don't want to have to deal with filter visualizations needing to select "(blanks)" for certain costs types the data relationship between these tables is a Many-to-One Single Direction Filter using [index] as the key.
EDIT2:
Working .pbix file with notional data and the data model I described is linked:
StackOverflow_GTofMeasure_Crosfilltered.pbix
I think this solution could work for you. Basically I've created two helper measures (which you don't have to show in your table):
CostPerHourHelper = SUMX(TableName,[Cost per Hour])
CostPerHourTotal = SUMX(ALL(TableName),[Cost per Hour])
Now you can create your % of Cost per Hour measure using this expression:
% Cost Per Hour = [CostPerHourHelper]/[CostPerHourTotal]
It should produce:
UPDATE:
Use ALLSELECTED() function to preserve the explicit filters you applied.
% Cost Per Hour = SUMX ( TableName, [Cost per Hour] )
/ SUMX ( ALLSELECTED ( TableName ), [Cost per Hour] )
Let me know if this helps.