Power BI Visual broken due to missing relationship - powerbi

I'm trying to wrap my head around this conundrum and I'm drawing blanks.
I have Table Locals which is a list of locations each person is located in and during what dates:
Name Location Date Start Date End
Paul Las Vegas 2019/01/01 2020/12/31
Tom Los Angeles 2019/03/21 2020/02/14
Susan New York 2019/08/30 2020/06/28
Paul Chicago 2021/01/01 2021/12/31
and Table Contracts which is a table of contracts (each person can have more than one contract):
ID (unique) Person ID Type Date
1 345456345 Video 2019/02/05
2 867345315 Book 2019/05/14
3 345456345 Photo 2021/01/23
and Table Contacts which is a list of people and their contracts dates (each person can have more than one contract period):
Person ID (unique) Name
345456345 Paul
867345315 Tom
879316455 Susan
I need to determine the location of each contract.
So I established a relationship as follows:
Contracts/Person ID -> Contacts/Person ID
Contacts/Name -> Locals/Name
But from here, I am lost. My data visual (table containing Contract/Contract ID, Contacts/Name, Locals/Location) is broken because of the confusion in determining the location of Paul's contracts. The "Fix This" button is telling me that there is a missing relationship between the fields but autodetect isn't finding any relationships (obviously) and I don't know what other relationship I can create manually.
I know the secret is in determining whether the date on the contract falls within the Date Start and Date End of each location belonging to Paul but I don't know how to go about doing that.
Any ideas?

Ok, I took a closer look and I think I know what your problem is. You're trying to show in a visual, which contracts each person had in each location.
When you put Person's name and Location in a table, everything is fine. When you put Person's name and Contract ID everything is fine. The problem starts when you put it all together, as Power BI cannot determine, based on relationships, which contract was signed in which location.
To solve the issue, you would have to create a measure that would (for instance) count these contracts taking dates into account. Something like this:
#Contracts in Location =
var dateFrom = SELECTEDVALUE(Locals[Date Start])
var dateTo = SELECTEDVALUE(Locals[Date End])
return
COUNTROWS(FILTER(Contracts, Contracts[Date] >= dateFrom && Contracts[Date] <= dateTo))
If you place this measure in your visual, DAX will be able to figure out appropriate relationships based on row context and measure filters.
EDIT
Here's a measure that will also count contracts, when there is no person in the calculation context (by introducing it). I will assume that there is neither gap nor overlap for location dates for a single person.
#Contracts in Location =
SUMX (
Contacts,
CALCULATE (
COUNTROWS (
FILTER (
Contracts,
Contracts[Date] >= MIN ( Locals[Date Start] )
&& Contracts[Date] <= MAX ( Locals[Date End] )
)
)
)
)

It's possible to do it with your tables structure, but it would be better to change a bit your model.
That said, to obtain the following table visual, it's necessary to add also the contract ID set as "Don't summarize". This is needed to stop the table visual to try aggregate the rows, to allow the ContractLocation measure to work with the correct filter context, that must contain a single contract and a single contact.
and use a measure to set the relationship over the dates
ContractLocation =
VAR CurrentContractDate = SELECTEDVALUE( Contracts[Date] )
RETURN
IF(
NOT ISBLANK( CurrentContractDate ),
CALCULATE(
SELECTEDVALUE( Locals[Location] ),
FILTER( Locals, Locals[Date Start] <= CurrentContractDate && Locals[Date End] >= CurrentContractDate )
)
)
Edit: adding measure to also match location when there are no matching intervals
This measure uses the last known location. If the contract is before the first know location, then it uses the first known location. To be tested.
FuzzyContractLocation =
IF (
ISEMPTY ( Locals ) && NOT ISEMPTY(Contracts),
"Locale unknown",
VAR CurrentContractDate =
SELECTEDVALUE ( Contracts[Date] )
RETURN
IF (
NOT ISBLANK ( CurrentContractDate ),
VAR MinDateStart =
MIN ( Locals[Date Start] )
VAR MaxDateStart =
CALCULATE (
MAX ( Locals[Date Start] ),
Locals[Date Start] <= CurrentContractDate,
REMOVEFILTERS ( Locals[Date End] ),
REMOVEFILTERS ( Locals[Location] )
)
VAR DateStart =
IF ( ISBLANK ( MaxDateStart ), MinDateStart, MaxDateStart )
RETURN
CALCULATE ( SELECTEDVALUE ( Locals[Location] ), Locals[Date Start] = DateStart )
)
)
This would be better to be done in the data cleansing stage of the ETL, by building a consistent location table.

Related

Search on value (number) to find text string PowerBI

I work with warranty claims and got two sheets with different suppliers, priority and prices for different part numbers for our product.
One sheet with prices/suppliers is used for our repairs in Asia and other sheet is used for the rest of the world (WW)
Sheet 1: PN-List Asia
Sheet 2: PN-List WW
I've managed to make an IF-statement that find the right price, from the right list (PPU).
The formula I've used:
Price Per unit (PPU) =
IF (
SELECTEDVALUE ( 'Regress List'[Continent] ) = "Asia",
CALCULATE (
MAX ( 'PN-List Asia'[Price Per Unit] ),
FILTER ( 'PN-List Asia', 'PN-List Asia'[Prio] = MIN ( 'PN-List Asia'[Prio] ) )
),
CALCULATE (
MAX ( 'PN-List WW'[Price Per Unit] ),
FILTER ( 'PN-List WW', 'PN-List WW'[Prio] = MIN ( 'PN-List WW'[Prio] ) )
)
)
Now I need to get the suppliers name of the product/item, into my matrix in PowerBI.
Suppliers name can be found in the two different sheets (PN-List Asia & PN-List WW) under supplier name ('PN-list WW'[supplier name]).
My guess is that I need to use an IF statement to decide if I should search in PN-List Asia or PN-List WW, but I'm not sure how to find the right supplier name with Priority (prio) and/or Price Per Unit (PPU).
Answer to #Mik
Funny enough, I should be able to make an export (to excel) from PowerBI, that looks like this:
So I can know who to invoice for all the costs. If the supplier wants more information, we will have it in our PowerBI-matrix.
What if you change your visual, for example, like this? I mean getting of supplier name through measure.
used measures
Price Per unit (PPU) =
VAR partNo = SELECTEDVALUE('Claims List (Monthly refilled'[Used Part ])
VAR priority =
CALCULATE(
MIN('PN-List (static)'[Priority])
,TREATAS({partNo},'PN-List (static)'[Part no])
)
RETURN
CALCULATE(
SELECTEDVALUE('PN-List (static)'[Price Per Unit])
,TREATAS({partNo},'PN-List (static)'[Part no])
,'PN-List (static)'[Priority]=priority
)
Supplier =
VAR Usedpart = VALUES('Claims List (Monthly refilled'[Used Part ])
RETURN
CALCULATE(
SELECTEDVALUE(
'PN-List (static)'[Supplier name]
,IF(
ISBLANK(DISTINCTCOUNT('PN-List (static)'[Supplier name]))
,BLANK()
,"- " & DISTINCTCOUNT('PN-List (static)'[Supplier name]) & " Suppliers")
)
,TREATAS(Usedpart,'PN-List (static)'[Part no])
)

DAX Service recall rate measure

I'm struggling to write/calculate this measure in DAX. The definition of recall rate is count of repeat service bookings (count of distinct booking number, should be distinct anyway but just in case) for a customer, asset combination within a week (Closed on date, 7 day period). So I go out to fix a machine, if I get called out again to fix the same machine for the customer within a week that is then a recall of 1 (or more if i get called out multiple times within a week). I've highlighted the groups in different colours. Null Assets, Closed On and null booking number needs to be filtered out (this is done by inner join in SQL in below code, needs to be in DAX query) Thanks! EDIT : Sorry realised it would be more helpful if I posted sql code to generate data please see below :
SELECT
FB.BookingNumber,
FB.EngineerEmployeeID,
FWO.ServiceAccountRecID AS Customer,
FWO.AssetRecID AS Asset,
FWO.ClosedOn
FROM dbo.FactWorkOrder AS FWO JOIN dbo.FactBooking AS FB ON FB.WorkOrderID = FWO.WorkOrderID
WHERE FWO.WorkOrderType = 'Breakdown'
AND AssetRecID IS NOT NULL
AND ClosedOn IS NOT NULL
ORDER BY BookingNumber
It's most efficient if you first define a calculated column that gives the first CloseOn date for each Customer/Asset combination.
FirstClosed =
CALCULATE (
MIN ( WorkOrder[ClosedOn] ),
ALLEXCEPT ( WorkOrder, WorkOrder[Customer], WorkOrder[Asset] )
)
and then write a measure
TotalRecalls =
COUNTROWS (
FILTER (
WorkOrder,
WorkOrder[ClosedOn] > WorkOrder[FirstClosed] &&
WorkOrder[ClosedOn] < WorkOrder[FirstClosed] + 7
)
)
However, you can do this all within a single measure if you prefer.
TotalRecalls =
VAR AddCol =
ADDCOLUMNS (
WorkOrder,
"#FirstClosed",
CALCULATE (
MIN ( WorkOrder[ClosedOn] ),
ALLEXCEPT ( WorkOrder, WorkOrder[Customer], WorkOrder[Asset] )
)
)
RETURN
COUNTROWS (
FILTER (
AddCol,
WorkOrder[ClosedOn] > [#FirstClosed] &&
WorkOrder[ClosedOn] < [#FirstClosed] + 7
)
)
Either way, here's what this looks like used in a visual:
I would first create a "Booking Key" column:
Booking Key = [Customer] & "|" & [Asset] & "|" & WEEKNUM ( [ClosedOn] )
Then I would create a Measure to return a modified distinct count on the Booking Key:
# Repeat Service Bookings =
VAR v_Count = DISTINCTCOUNT ( 'Table'[Booking Key] )
RETURN IF ( v_Count > 1, v_Count - 1 )
I would add # Repeat Service Bookings to the Visual Level Filters of your table visual, with the filter set to: is greater than 1.

How can I get DAX to return just the record with a date closest to the slicer?

I'm hoping someone can help as I've completely run out of ideas.
I'm working on performance reporting data, producing a number of visuals to summarise the most recent data. To allow users to retrospectively produce reports from previous quarters, I have added a date slicer as a way to "View data as at xxxx date".
Here's a rough representation of my data table - the due dates are in English format (dd/mm/yyyy):
The ratings are calculated in another system (based on a set of targets), so there are no calculated columns here. In reality, there are a lot more measures that report on different time periods (some weekly, some annually, etc) and there are different lags before the data is "due".
I eventually managed to get a measure that returned the latest actual:
MostRecentActual =
VAR SlicerDate = MAX ( Dates[Day] )
RETURN
CALCULATE (
SUM ( Data[Actual] ),
Data[Data due] <= SlicerDate,
LASTDATE ( Data[Data due] )
)
I'm not completely sure I've done it right but it seems to work. I'd be happier if I understood it properly, so explanations or alternatives would be welcomed.
What I'm trying to do now is a basic summary pie chart at the beginning which shows the proportion of the measures that were red, amber, green or unrated as at the date selected. So I would need it to count the number of each rating, but only one for each measure and only for the date that is closest to (but before) the slicer date, which would vary depending on the measure. So using the above three measures, if the slicer was set to 10/10/2019 (English format - dd/mm/yyyy), it would count the RAGs for Q3 2019/20 for measures A an C and for Q2 2019/20 for measure B as there is a time lag which means the data isn't ready until the end of the month. Results:- A: Amber, B: Green, C:Red.
If I were able to create the measure that counted these RAGs, I would then want to add it to a pie chart, with a legend that is "Rating", so it would split the chart up appropriately. I currently can't seem to be able to do that without it counting all dates before the slicer (not just the most recent) or somehow missing ratings from the total for reasons I don't understand.
Any help would be very gratefully received.
Many thanks
Ben
Further update. I've been working on this for a while!
I have created a COUNTAX measure to try to do what I was wanting to do. In some circumstances, it works, but not all and not in the crucial ones. My measure is:
TestCountaxpt2 =
VAR SlicerDate = MAX ( Dates[Date] )
VAR MinDiff =
MINX (
FILTER (
ALL ( Data ),
Data[Ref] IN VALUES ( Data[Ref] ) &&
Data[Data due] <= SlicerDate
),
ABS ( SlicerDate - Data[Data due] )
)
VAR thisdate =
MINX (
FILTER (
ALL ( Data ),
Data[Ref] IN VALUES ( Data[Ref] ) &&
ABS ( SlicerDate - Data[Data due] ) = MinDiff
),
Data[Data due]
)
RETURN
COUNTAX (
FILTER ( Data, Data[Data due] = thisdate && Data[Ref] IN VALUES ( Data[Ref] ) ),
Data[RAG]
)
It produces the following table for a subset of the performance measures, which looks almost ok:
Table showing the result of the TestCountaxpt2 measure:
The third column is the measure above and it seems to be counting one RAG per measure and the dates look correct as the slicer is set to 3rd January 2020. The total for column 3 confuses me. I don't know what that is counting and I don't understand why it doesn't add up to 7.
If I add in the RAG column from the data table, it goes a bit more wrong:
Same table but with RAG Rating added:
The pie chart that is produced is also wrong. It should show 2 Green, 2 Red, 2 Grey (no rating) and 1 Amber. This is what happens.......
Pie chart for the DAX measure, with RAG Rating in the legend:
I can see what it is doing, which is to work out the most recent due date to the slicer in the whole table and using that (which is 1st Jan 2020) whereas I want it to calculate this separately for each measure.
Link to PBIX:
https://drive.google.com/file/d/1RTokOjAUADGHNXvZcnCCSS3Dskgc_4Cc/view?usp=sharing
Reworking the formula to count the ratings:
RAGCount =
VAR SlicerDate =
MAX ( Dates[Day] )
RETURN
COUNTAX (
ADDCOLUMNS (
SUMMARIZE (
FILTER ( Data, Data[Data due] <= SlicerDate ),
Data[Ref],
"LastDateDue", LASTDATE ( Data[Data due] )
),
"CountRAG", CALCULATE (
COUNTA ( Data[RAG] ),
Data[Data due] = EARLIER ( [LastDateDue] )
)
),
[CountRAG]
)
Here's the table it produces:
The reason for Total = 4 for the third column is straightforward. The SelectDate is maximal over all of the Refs in the table and there are only four Refs that match that date.
To fix this and get the totals you're after, you'll need to iterate over each Ref and calculate the SlicerDate for each independently and only then do your lookups or sums.
I haven't tested this code but it should give you an idea of a direction to try:
MostRecentActual =
VAR SlicerDate = MAX ( Dates[Day] )
RETURN
SUMX (
ADDCOLUMNS (
SUMMARIZE (
FILTER ( Data, Data[Data due] <= SlicerDate ),
Data[Ref],
"LastDateDue", LASTDATE ( Data[Data due] )
),
"SumActual", CALCULATE (
SUM ( Data[Actual] ),
Data[Data due] = EARLIER ( [LastDateDue] )
)
),
[SumActual]
)
Going inside to outside,
FILTER the table to ignore any dates beyond the SlicerDate.
Calculate the LastDateDue for each Ref using SUMMARIZE.
Sum the Actual column for each Ref value using its specific LastDateDue.
Iterate over this summary table to add up SumActual across all Refs in the current scope.
Note that for 4, only the Total row in your visual will contain multiple Refs since the innermost Data table inside FILTER is not the entire Data table but only the piece visible in the local filter context.

Power BI: Use unrelated date table to create a measure

Hi I have been struggling with this for a bit now and I hope I didn't miss a previous question. I am trying to get a count of the vehicles we have based on an EOMONTH date. We are buying and selling cars on a regular basis and for reporting we need to know how many we had at the end of each month and the report is a rolling 12 months.
I've tried creating the relationship with the purchasedate of the vehicle to the date of my date table but when I create the measure (Used to calculate the number of vehicles purchased but haven't been sold):
SalesBlank = CALCULATE(
COUNT(Vehicles[MVANumber]),
FILTER(Vehicles, Vehicles[purchasedate] <= RELATED('Date'[EOMONTH]) && ISBLANK(Vehicles[saledate])))
I only get a count of vehicles purchased that month and don't have a sale date - I'm not surprised because my relationship with the date table is the purchase date.
How can I set up a measure to look at the date table and filter the vehicles table with this logic:
purchasedate <= date[EOMONTH] && ISBLANK(salesdate)
Any help would be greatly appreciated!!
Thanks,
Matt
Sample Data and Desired Results
Relationships
If I understand you correctly, you want to get a count of the vehicles on hand at the end of each month. That could be calculated by counting the vehicles with a purchase date less than or equal to the selected end of month and subtracting the count of vehicles with a sale date less than or equal to the selected end of month.
You can create an active relationship between Vehicle[PurchaseDate] and Date[Date]. Then create an inactive relationship based upon Vehicles[SaleDate] and Date[Date].
You could use a measure that is something like this:
Inventory Count =
VAR MaxDate =
MAX ( 'Date'[Date] )
VAR MinDate =
CALCULATE ( MIN ( 'Date'[Date] ), ALL ( 'Date' ) )
VAR Purch =
CALCULATE (
COUNT ( 'Vehicles'[VehicleID] ),
DATESBETWEEN ( 'Date'[Date], MinDate, MaxDate )
)
VAR Sales =
CALCULATE (
COUNT ( 'Vehicles'[VehicleID] ),
USERELATIONSHIP ( 'Date'[Date], Vehicles[Sale Date] ),
DATESBETWEEN ( 'Date'[Date], MinDate, MaxDate )
)
VAR MaxPurDt =
CALCULATE ( MAX ( 'Vehicles'[Purchase Date] ), ALL ( 'Vehicles' ) )
VAR MaxSlDt =
CALCULATE ( MAX ( 'Vehicles'[Sale Date] ), ALL ( 'Vehicles' ) )
RETURN
IF (
MIN ( 'Date'[Date] ) <= MaxPurDt
|| MIN ( 'Date'[Date] ) <= MaxSlDt,
Purch - Sales
)
This measure gets a cumulative count of purchases and a cumulative count of sales and then subtracts them. The IF check is to avoid propagation of cumulative totals beyond the maximum date in the Vehicle table.
I'm not sure how to interpret your showing of just 3 months in the desired results. This will produce the same answers as what you have, but without a filter applied to the table, it starts at 3/31/2016 (the date of the first sale).
Edit: There's probably a more efficient way along the lines you were thinking, but it is escaping me at the moment.

How to dynamic change the filter in Power BI DAX based on a selected filter

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.