The challenge — recursive employee–manager relationships
One of the recurring problems Power BI developers run into is how to handle employee–manager relationships. Most HR systems store all employees in a single table, and some of those employees are managers to other employees in the same table. That's a recursive (or self-referencing) relationship.
The rule is simple: one employee has one manager, but one manager can have many direct reports. Easy to draw on a whiteboard, less obvious how to make Power BI render it as a hierarchy you can slice and visualise.
The data model
You typically have two tables. The first is the Employee table — a flat list of every person and their ID:
Employee
─────────────────
Employee ID │ Name
─────────────────
1 │ Jane
2 │ John
3 │ Sara
6 │ Tom
7 │ Anna
8 │ Mark
9 │ Lisa
15 │ Paul
16 │ Emma
17 │ David
18 │ Maya
The second table — call it EmpManager — assigns each employee an immediate parent (their manager's Employee ID). For convenience here we'll call that column Lvl1:
EmpManager
──────────────────────────
Employee ID │ Name │ Lvl1
──────────────────────────
2 │ John │ 1
6 │ Tom │ 2
7 │ Anna │ 2
8 │ Mark │ 2
9 │ Lisa │ 2
15 │ Paul │ 6
16 │ Emma │ 6
17 │ David │ 6
18 │ Maya │ 6
Reading those rows: employee 2 (John) supervises 6, 7, 8, 9. Employee 6 (Tom) in turn supervises 15, 16, 17, 18. Multiple levels of management, all encoded with a single column referencing IDs in the same table.
Building the path with PATH()
DAX has a function built specifically for this: PATH. It walks up the recursive relationship from any row to the top of the chain, returning a delimited string of every ID along the way.
Add a calculated column to EmpManager:
EntityPath = PATH ( EmpManager[Employee ID], EmpManager[Lvl1] )
The first argument is the column with the row's own ID. The second is the column with the parent ID. The result for our sample data:
Employee ID │ Name │ Lvl1 │ EntityPath
───────────────────────────────────────
2 │ John │ 1 │ 1|2
6 │ Tom │ 2 │ 1|2|6
7 │ Anna │ 2 │ 1|2|7
8 │ Mark │ 2 │ 1|2|8
9 │ Lisa │ 2 │ 1|2|9
15 │ Paul │ 6 │ 1|2|6|15
16 │ Emma │ 6 │ 1|2|6|16
17 │ David │ 6 │ 1|2|6|17
18 │ Maya │ 6 │ 1|2|6|18
Each row now contains the full reporting chain from that person all the way up to the top.
Pulling level names with PATHITEM
The path is in IDs, but reports usually need names. PATHITEM picks out the ID at a given position in the chain, and LOOKUPVALUE resolves it to a name. Together they let you build one calculated column per level of the hierarchy.
For the top of the chain (level 0):
Level0 =
VAR LevelNumber = 1
VAR LevelKey = PATHITEM ( EmpManager[EntityPath], LevelNumber, INTEGER )
VAR LevelName = LOOKUPVALUE (
EmpManager[Name],
EmpManager[Employee ID], LevelKey
)
RETURN LevelName
To create level 1, level 2 and so on, copy the column and change LevelNumber to 2, 3, 4… You end up with one column per organisational tier:
Employee ID │ Name │ Level0 │ Level1 │ Level2 │ Level3
────────────────────────────────────────────────────────
15 │ Paul │ Jane │ John │ Tom │ Paul
16 │ Emma │ Jane │ John │ Tom │ Emma
17 │ David │ Jane │ John │ Tom │ David
18 │ Maya │ Jane │ John │ Tom │ Maya
HR conventions usually call the top of the org "level 0" — the CEO, founder, or whatever sits at the apex. Adjust the LevelNumber variable accordingly so your indexing matches your organisation.
Visualising the org chart
Once each row carries its complete reporting chain as separate columns, you have several options for putting it on a page:
- Drop
Level0throughLevelNinto a hierarchy slicer, decomposition tree, or matrix visual - Use a third-party AppSource visual designed for org charts (several free ones render directly from this column shape)
- Feed the levels into a custom visual built with HTML/SVG if you need full control over layout
The data work is the hard part. Every visual on AppSource expects roughly the same input — a flat table where each row contains its position in the hierarchy as an ordered set of columns. PATH and PATHITEM get you there.
When this pattern earns its keep
Beyond visualising the chart itself, the same calculated columns unlock a few common reporting needs:
- Roll-up KPIs by reporting line — headcount, salary cost, average tenure aggregated up any tier
- Row-level security tied to manager chains — a manager sees only their own reports, recursively, by filtering on the path string
- Org changes that don't break the model — when reporting lines move, only the
Lvl1column updates;PATHandPATHITEMrecompute automatically
Need help with a Power BI model — recursive relationships, row-level security, or a full BI migration? Get in touch.
This pattern is one of those small DAX tricks that pays itself back many times over once you've used it once. If your HR data lives in a single self-referencing table, you can move from "we don't have an org chart in Power BI" to a working hierarchy in an afternoon.