Hierarchy(NodeID, ParentID, NodeName) LOAD ...
Hierarchy and HierarchyBelongsTo in Power BI
Qlik's Hierarchy prefix flattens a parent-child table into level columns, and HierarchyBelongsTo builds an ancestor-to-descendant table. In Power BI, the standard pattern is DAX PATH functions in calculated columns: PATH() builds the chain, PATHITEM() extracts Level 1, Level 2 and so on, and PATHCONTAINS() answers "belongs to" questions, for example in RLS.
Short answer: Add Path = PATH(Emp[EmpID], Emp[ManagerID]), then Level1 = LOOKUPVALUE(Emp[Name], Emp[EmpID], PATHITEM(Emp[Path], 1, INTEGER)) and so on for each level. Build a matrix hierarchy from Level1–LevelN. Hide the blank levels of ragged hierarchies with an ISINSCOPE/depth check.
What it does in Qlik
Hierarchy(NodeID, ParentID, NodeName [, ParentName [, PathSource [, PathName [, PathDelimiter [, Depth]]]]])produces one row per node with expanded level fields, and optionally a path and a depth.HierarchyBelongsTo(NodeID, ParentID, NodeName, AncestorID, AncestorName [, DepthDiff])produces one row per node–ancestor pair.
Qlik example
Org:
Hierarchy(EmpID, ManagerID, Name, , Name, Path, '/', Depth)
LOAD EmpID, ManagerID, Name FROM [lib://Data/employees.qvd] (qvd);Power BI equivalent
DAX calculated columns
Path = PATH ( Emp[EmpID], Emp[ManagerID] )
Depth = PATHLENGTH ( Emp[Path] )
Level1 = LOOKUPVALUE ( Emp[Name], Emp[EmpID], PATHITEM ( Emp[Path], 1, INTEGER ) )
Level2 = LOOKUPVALUE ( Emp[Name], Emp[EmpID], PATHITEM ( Emp[Path], 2, INTEGER ) )
Level3 = LOOKUPVALUE ( Emp[Name], Emp[EmpID], PATHITEM ( Emp[Path], 3, INTEGER ) )HierarchyBelongsTo-style RLS
// role filter on Emp: the user sees themselves and everyone below them
PATHCONTAINS (
Emp[Path],
LOOKUPVALUE ( Emp[EmpID], Emp[Email], USERPRINCIPALNAME () )
)Gotchas and behaviour differences
- You create one Level column per possible depth, so the depth is fixed at design time. Qlik's Hierarchy expands as deep as the data goes. Add spare levels, and test whether the data has grown deeper.
- PATH errors on loops, where a node is its own ancestor, and on orphaned parent IDs. Clean those first in Power Query. Qlik is more forgiving.
- Ragged hierarchies show blank members in a matrix. The usual fix is a measure that returns BLANK when the row's level is deeper than the node's Depth. See the Microsoft parent-child article.
Official documentation
- Hierarchy - script prefix (Qlik Help)
- HierarchyBelongsTo - script prefix (Qlik Help)
- Understanding functions for parent-child hierarchies in DAX (Microsoft Learn)
- PATH function (DAX) (Microsoft Learn)
- PATHCONTAINS function (DAX) (Microsoft Learn)
Related pages
Section Access; LOAD ACCESS, USER.EMAIL, REGION ...; Section Application;
Section Access (data reduction)
Section Access reduces data per user with a security table in the script. Power BI uses row-level security roles: DAX filters evaluated with the signed-in user's UPN, usually through a security table. OMIT maps to object-level security.
Partial / different approach
IntervalMatch(value) LOAD start, end ...
IntervalMatch
IntervalMatch links values (dates or numbers) to the intervals that contain them. Power BI relationships only match equal values. Do it in Power Query, in a DAX calculated column, or by expanding intervals into one row per day.
Partial / different approach
If(Dimensionality() = 0, 'Total', ...)
Dimensionality and SecondaryDimensionality
Dimensionality() tells a measure which level of a pivot table it is on. DAX doesn't return a level number. Test each level with ISINSCOPE, deepest first, or use ISATLEVEL in a visual calculation. A pivot's column levels (SecondaryDimensionality) are handled the same way.
Partial / different approach
Spotted an error or a better pattern? QlikToDax is maintained by a working BI consultant; corrections are welcome via the About page. Last reviewed 2026-10-25.