Forum Discussion
Best Data Model for Headcount (Monthly) and Course Data in Power BI
I’m working with two datasets in Power BI that both contain multiple rows per employee, but represent different types of information.
Dataset 1: Combined Headcount Data
Every month, I load a headcount report.
It includes:
Employee ID
Gender
Age
Ethnicity
Position / Job level
Salary
Agency (this is large marketing company with multiple agencies)
Because I append monthly files together:
Each employee appears once per month
Therefore, the table contains multiple rows per Employee ID
Example:
Employee ID 12345 appears in January, February, December, etc.
Dataset 2: Campus (Training) Data
Contains training/course records
Each row represents one course taken
Employees can appear multiple times
Example:
If Employee 12345 took:
Excel101
PowerBI101
PowerPoint101
They will appear three times in this dataset.
What I’m Trying to Achieve
My dashboard has three sections:
1. Headcount Section (Independent from Campus)
Has a Month slicer
Has an Agency slicer
If I select January, it should show:
Headcount for January only
Headcount by Agency for January
This section works correctly and should remain independent from Campus data.
2. Campus Section
Shows courses taken
Needs:
Month slicer
Agency slicer
If I select:
January + Agency A
→ It should show how many courses were taken by employees in Agency A in January.
3. Demographics / Employee Info (Based on Campus Students)
This section should show demographics of employees who took training, such as:
Age group
Ethnicity
Job level
Agency
But filtered by:
Selected Month
Selected Agency
The Issue
When I try to create a relationship between Campus and Headcount, Power BI only allows many-to-many because both tables have multiple rows per Employee ID.
If I use that many-to-many relationship:
When I select January in a slicer
My demographics section shows the total January headcount
Instead of only the employees who actually took training in January
What I’m Trying to Achieve
When I select:
Month = January
Agency = X
I want to see:
Courses taken in January
And demographic info (age group, ethnicity, job level)
But only for employees who took training in that selected month
My Question
What is the correct way to model this?
Should I avoid directly relating the two fact tables?
How do I structure the relationships so that Month filtering correctly limits demographics to only Campus participants?
Right now, the many-to-many relationship is giving me incorrect filtering behavior.
What is the recommended modeling approach for this scenario?
Hi b-kopik ,
If you want to count how many courses were taken, try something like:
CoursesTaken :=
CALCULATE (
COUNTROWS ( 'Campus Data' ),
TREATAS (
VALUES ( 'Combined Headcount'[Employee ID] ),
'Campus Data'[Employee ID]
)
)
or if you want number of employees trained, try something like:EmployeesTrained :=
CALCULATE (
DISTINCTCOUNT ( 'Campus Data'[Employee ID] ),
TREATAS (
VALUES ( 'Combined Headcount'[Employee ID] ),
'Campus Data'[Employee ID]
)
)
26 Replies
- Ritaf1983
Super User
Hi b-kopik
order to get this dashboard working correctly, what needs to be done is to transition from a direct many-to-many relationship between your two data tables to a star schema structure with shared dimensions. The current issue exists because Power BI cannot effectively filter the Headcount table using the Campus table when an Employee ID appears in multiple months across both; this is why selecting January shows you the entire headcount instead of just the trainees. To fix this, the approach involves creating a unique Employee table and a dedicated Calendar table that both connect to your existing data. For the Employee table, you can use a DAX calculated table like Employees = DISTINCT(UNION(SELECTCOLUMNS('Headcount', "ID", 'Headcount'[Employee ID]), SELECTCOLUMNS('Campus', "ID", 'Campus'[Employee ID]))) to ensure every ID is represented only once. Similarly, a Calendar table should be generated using Calendar = CALENDARAUTO() so that your Month slicer comes from a single, neutral source.
Once these tables are created, you should connect the Employee ID from the new Employee table to both the Headcount and Campus tables with a one-to-many relationship, and do the same for the Date or Month columns from the Calendar table. When the slicers for Month and Agency are pulled from these new shared dimension tables, they will filter both fact tables simultaneously and accurately. To ensure the Demographics section specifically isolates only those who took training in the selected month, what needs to be done is to create a simple DAX measure such as IsParticipant = IF(ISBLANK(COUNTROWS('Campus')), 0, 1) and add it to the "Filters on this visual" pane for your demographic charts, setting the value to 1. This logic forces the demographic visuals—which pull their descriptive data like Age and Ethnicity from the Headcount table—to only display rows where a corresponding training record exists in the Campus table for that specific month and agency. This structure keeps your Headcount section independent while allowing the Campus and Demographics sections to sync perfectly without double-counting or over-reporting.
If this post helps, then please consider Accepting it as the solution to help the other members find it more quickly
- johnt75
Super User
Never create a relationship between fact tables, that goes completely against the star schema which is the best way to model data. Instead create dimension tables which have one-to-many relationships to your fact tables, this is much more efficient and it is much easier to manipulate the filters in the way that you want to.
As a minimum you will want a date dimension and an employee dimension, both linked to both your fact tables. You can create an employee dimension by retrieving a list of unique employee IDs. You can do this in Power Query or DAX, the only thing you need to be mindful of is whether or not it is possible for an employee ID to appear in the training data and not in the headcount data. If it is not possible then you can create an employee dimension with a simple DISTINCT(Headcount[EmployeeID] ). If there might be employees in training who are not in headcount then you could do something like
Employee = DISTINCT ( UNION ( DISTINCT ( Headcount[Employee ID] ), DISTINCT ( Training[Employee ID] ) ) )Once you have the employee dimension, link it to both headcount and training.
You could also create dimensions for e.g. Agency, Gender, Ethnicity, Course etc, again by using DISTINCT. If you do create these extra dimensions, make sure that you use the dimension in all your visuals and filters.
You can now create a measure to show the number of employees who did training in the chosen month like
Num employees with training = CALCULATE ( COUNTROWS ( Headcount ), Training )Because this is counting from Headcount, you can break it down by any of the columns in that table, or by dimensions you have chosen to extract from that table. It works by using the expanded table of Training, so only employees who have rows in the Training table for the given month will be considered.
One thing to bear in mind is that this method needs to have only 1 month chosen in the slicer. If more than one month is chosen then employees will be counted multiple times. You can enable single select on the slicer to ensure that only 1 month can be chosen.
- b-kopik
Helper III
Thanks, I’ve now remodelled this into a proper star schema as suggested.
I created an Employee dimension (distinct Employee IDs) linked one-to-many to both fact tables
There is no longer any direct relationship between the two fact tables.
Section 1 (Headcount) works correctly.
Month + Agency slicers correctly show headcount for the selected month.Section 2 (Training/Campus) also works correctly.
Month + Agency slicers correctly show number of courses taken.However, Section 3 (Demographics of employees who took training) is still not behaving correctly.
When I select:
Month = January
Agency = X
The demographics visuals (Age group, Ethnicity, Job level, etc.) are still showing the total January headcount, not just the employees who actually took training in January.
So although the model now follows a star schema and filtering works independently for each fact table, I’m not getting the intersection behavior I need for the demographics section.
Am I correct in thinking I now need a specific measure (rather than relying on implicit filtering) to restrict Headcount to only employees that exist in Training for the selected month?
If so, what is the recommended pattern to achieve this without reintroducing many-to-many behavior?
- johnt75
Super User
Yes, you need to write an explicit measure. I included an example, and an explanation of how it works, in my original post but it was quite a long post. Have another look at it from the second code snippet down.
- v-hashadapu
Community Support
Hi b-kopik , hope you are doing great. May we know if your issue is solved or if you are still experiencing difficulties. Please share the details as it will help the community, especially others with similar issues.
- v-hashadapu
Community Support
Hi b-kopik , Hope you're doing okay! May we know if it worked for you, or are you still experiencing difficulties? Let us know — your feedback can really help others in the same situation.
- b-kopik
Helper III
Hello,
Can I create one table with one column for months and one for the years? I need a slicer that connects to the different datasets for each month - but then not sure how it will work if I have multiple years worth of data? So say I filter for April 2026 but I also have April 2025?
- v-hashadapu
Community Support
Hi b-kopik , Thank you for reaching out to the Microsoft Community Forum.
you should create a proper Date (Calendar) table rather than a simple Month/Year table. Have a single date column that uniquely identifies each period, so April 2025 and April 2026 are treated as different selections. Build a Calendar table, relate it to both Headcount and Campus on their date columns and use fields like “MMM YYYY” (sorted by a numeric YearMonth key) in your slicer.
- b-kopik
Helper III
Thanks! So should I make sure I hvae a column in all the datasets that has March 2026, April 2026, etc?
- v-hashadapu
Community Support
Hi b-kopik , Hope you're doing fine. Can you confirm if the problem is solved or still persists? Sharing your details will help others in the community.
- b-kopik
Helper III
Hello,
I'm getting an ambiguous path error in Power BI and I'm trying to understand the best way to structure my model.
My data
I have four datasets:
1. Master Headcount table
- Employee ID
- Employee information
- Agency
- Business Unit
- Business Area
This contains all active employees and is my primary headcount table.
2. Organizational hierarchy table
- Agency
- Business Unit
- Business Area
This table contains only the organizational structure and no employee-level data.
3. Terminations table
- Employee ID
- Employee information
- Agency
- Business Unit
- Business Area
- Termination details
4. Promotions table
- Employee ID
- Employee information
- Agency
- Business Unit
- Business Area
- Promotion details
What I'm trying to achieve
I want users to be able to select an Agency, Business Unit, or Business Area from slicers and have all visuals update accordingly, including:
- Current Headcount
- New Joiners
- Terminations
- Promotions
For example, if a user selects a specific Business Area, all charts should automatically filter to that Business Area across all datasets.
The issue
When I create relationships between these tables, Power BI throws an error saying there are ambiguous paths between Business Area and Dates (or other dimensions). It appears there are multiple relationship paths connecting the same tables through different fact tables, and Power BI doesn't know which path to use for filtering.
My question
What is the recommended model design for this scenario?
- v-hashadapu
Community Support
Hi b-kopik , hope you are doing well.
Can you please create a new post here in the community detailing this new issue? that way it will get noticed better and you will be able to get better suggestions. Also, others with similar issues may follow it better that way.
Thank you.