Forum Discussion
Problem separating data by location after machines switch places in Power BI
Hi everyone,
I am working on a Power BI report that reads data directly from a SQL database. I only have read access and cannot change the database structure or data model.
We have test machines that write their measurement data directly into the database. Our reporting combines data from different locations. So far, each machine has always operated at exactly one location, and therefore the location of a data record has always been identified implicitly based on the machine ID.
Recently, two machines have been physically swapped between two locations.
Let’s call them Machine A and Machine B:
- Machine A has always been at Location X
- Machine B has always been at Location Y
- Now both machines have switched locations
- Both machines keep their existing IDs
- The way the machines write data into the database does not change (only the physical location changes)
This leads to a challenge in my Power BI report:
Because the location has always been inferred directly from the machine ID, the existing logic no longer reflects reality once the machines switch places. Historical data (before the swap) and future data (after the swap) now refer to different physical locations, although the machine IDs stay the same.
My question is:
How can this situation be handled within Power BI so that both historical and future data can still be evaluated correctly based on location, even though the database itself contains no explicit location information and I can't modify it?
Any ideas or best practices would be greatly appreciated.
Thanks!
1) Create a mapping table (in Power BI)
This can be:
Enter Data
Excel / SharePoint
CSV
Dataflow
Example:
MachineID Location StartDate EndDateA X 1900-01-01 2024-06-30 A Y 2024-07-01 9999-12-31 B Y 1900-01-01 2024-06-30 B X 2024-07-01 9999-12-31 This is effectively a Type-2 Slowly Changing Dimension, implemented outside the DB.
2) Add calculated column in Fact
Location = VAR d = Fact[MeasurementDate] VAR m = Fact[MachineID] RETURN CALCULATE ( MAX ( MachineLocation[Location] ), FILTER ( MachineLocation, MachineLocation[MachineID] = m && d >= MachineLocation[StartDate] && d <= COALESCE ( MachineLocation[EndDate], DATE ( 9999, 12, 31 ) ) ) )The solution above is given this way because you told you cannot change anything in DB, if you could the best approach would be adding ID to Fact and Type-2 Slowly Changing Dimension as a new dimention and use those by giving relation between them.
Hi @effi_go,
and are completely correct; this is a classic Slowly Changing Dimension (SCD) Type 2 scenario. Since you cannot modify the database, creating this "Logic Layer" inside Power BI is the only way to solve it.Here is the "Complete" guide to implementing this, including the visual logic and a performance tip for the DAX formula.
1. The Concept: Time-Based Lookup
You are moving from a Static relationship (Machine = Location) to a Temporal relationship (Machine + Time = Location).
You need a standalone "Mapping Table" that defines the "Valid From" and "Valid To" dates for each machine's location.
2. The Implementation (DAX Calculated Column)
The DAX solution provided by @cengizhanarslan is generally the most performant method for this "Range Lookup" scenario in Power BI, as Power Query lookups can be very slow with large datasets.
However, to make the formula robust against missing End Dates (active records), use this refined pattern:
Step 1: Create the Mapping Table (Use "Enter Data" or an Excel sheet as suggested)
-
MachineID | Location | StartDate | EndDate
-
A | X | 1/1/2020 | 6/30/2024
-
A | Y | 7/1/2024 | 12/31/9999 (Use a far future date for "Active")
Step 2: The Calculated Column Add this column to your Fact Table (where your measurements are). Note: Ensure there is NO active relationship between your Fact Table and the Mapping Table.
Calculated Location = VAR CurrentDate = 'FactTable'[MeasurementDate] VAR CurrentMachine = 'FactTable'[MachineID] RETURN CALCULATE ( // We take MAX to return the single text value found MAX ( 'LocationMapping'[Location] ), FILTER ( 'LocationMapping', 'LocationMapping'[MachineID] = CurrentMachine && 'LocationMapping'[StartDate] <= CurrentDate && // Handle NULL EndDates as "Today" or Future COALESCE('LocationMapping'[EndDate], DATE(9999,12,31)) >= CurrentDate ) )3. Why not Power Query?
mentioned that doing this in Power Query "could get slow fast". This is because performing a "Non-Equi Join" (joining on a date range rather than an exact match) forces Power Query to scan the entire mapping table for every single row in your Main Table. DAX (specifically the VertiPaq engine) is much faster at handling these in-memory range filters.
Summary Checklist
-
Create the Mapping Table with Start/End dates.
-
Use 12/31/9999 for the End Date of the current location.
-
Use the Calculated Column approach (not a Measure) so you can use the Location as a slicer/axis in your charts.
If this breakdown helps clarify the "SCD Type 2" implementation, a Kudos is appreciated!
This response was assisted by AI for translation and formatting purposes. -
-
-
-
-
-
6 Replies
- cengizhanarslanSuper User
1) Create a mapping table (in Power BI)
This can be:
Enter Data
Excel / SharePoint
CSV
Dataflow
Example:
MachineID Location StartDate EndDateA X 1900-01-01 2024-06-30 A Y 2024-07-01 9999-12-31 B Y 1900-01-01 2024-06-30 B X 2024-07-01 9999-12-31 This is effectively a Type-2 Slowly Changing Dimension, implemented outside the DB.
2) Add calculated column in Fact
Location = VAR d = Fact[MeasurementDate] VAR m = Fact[MachineID] RETURN CALCULATE ( MAX ( MachineLocation[Location] ), FILTER ( MachineLocation, MachineLocation[MachineID] = m && d >= MachineLocation[StartDate] && d <= COALESCE ( MachineLocation[EndDate], DATE ( 9999, 12, 31 ) ) ) )The solution above is given this way because you told you cannot change anything in DB, if you could the best approach would be adding ID to Fact and Type-2 Slowly Changing Dimension as a new dimention and use those by giving relation between them.
- effi_goFrequent VisitorThank you so much for your help!Your explanation makes a lot of sense. I'm going to try to implement this approach into my report and will let you know, if it works out.
- effi_goFrequent Visitor
Thanks again for your help! I've deceided to mark burakkaragoz's reply as the solution because it provided me with more details, which ended up helping me at completly implementing the SCD-method.
- burakkaragozSuper User
Hi @effi_go,
and are completely correct; this is a classic Slowly Changing Dimension (SCD) Type 2 scenario. Since you cannot modify the database, creating this "Logic Layer" inside Power BI is the only way to solve it.Here is the "Complete" guide to implementing this, including the visual logic and a performance tip for the DAX formula.
1. The Concept: Time-Based Lookup
You are moving from a Static relationship (Machine = Location) to a Temporal relationship (Machine + Time = Location).
You need a standalone "Mapping Table" that defines the "Valid From" and "Valid To" dates for each machine's location.
2. The Implementation (DAX Calculated Column)
The DAX solution provided by @cengizhanarslan is generally the most performant method for this "Range Lookup" scenario in Power BI, as Power Query lookups can be very slow with large datasets.
However, to make the formula robust against missing End Dates (active records), use this refined pattern:
Step 1: Create the Mapping Table (Use "Enter Data" or an Excel sheet as suggested)
-
MachineID | Location | StartDate | EndDate
-
A | X | 1/1/2020 | 6/30/2024
-
A | Y | 7/1/2024 | 12/31/9999 (Use a far future date for "Active")
Step 2: The Calculated Column Add this column to your Fact Table (where your measurements are). Note: Ensure there is NO active relationship between your Fact Table and the Mapping Table.
Calculated Location = VAR CurrentDate = 'FactTable'[MeasurementDate] VAR CurrentMachine = 'FactTable'[MachineID] RETURN CALCULATE ( // We take MAX to return the single text value found MAX ( 'LocationMapping'[Location] ), FILTER ( 'LocationMapping', 'LocationMapping'[MachineID] = CurrentMachine && 'LocationMapping'[StartDate] <= CurrentDate && // Handle NULL EndDates as "Today" or Future COALESCE('LocationMapping'[EndDate], DATE(9999,12,31)) >= CurrentDate ) )3. Why not Power Query?
mentioned that doing this in Power Query "could get slow fast". This is because performing a "Non-Equi Join" (joining on a date range rather than an exact match) forces Power Query to scan the entire mapping table for every single row in your Main Table. DAX (specifically the VertiPaq engine) is much faster at handling these in-memory range filters.
Summary Checklist
-
Create the Mapping Table with Start/End dates.
-
Use 12/31/9999 for the End Date of the current location.
-
Use the Calculated Column approach (not a Measure) so you can use the Location as a slicer/axis in your charts.
If this breakdown helps clarify the "SCD Type 2" implementation, a Kudos is appreciated!
This response was assisted by AI for translation and formatting purposes. -
-
-
-
-
- effi_goFrequent VisitorThank you so much for this detailed explanation. It helped me a lot in implementing the necessary changes to my report!
-
- tayloramySuper User
Hi effi_go,
cengizhanarslan is spot on, you want a type 2 slowly changing dimension to be able to track this over time. Ideally this would be created in a database somewhere, but with your constraints it is possible to build directly in power query, though depending on how many records of data you have that could get slow fast.
Here's some information about type 2 SCD: Slowly changing dimension type 2 - Microsoft Fabric | Microsoft Learn