You can use CTE to create a full path column on the fly.
--DROP TABLE Gruppe, Kunde, Gruppe_Gruppe, Kunde_Gruppe CREATE TABLE Gruppe ( Id INT PRIMARY KEY , Name VARCHAR(100) ) CREATE TABLE Kunde ( Id INT PRIMARY KEY , Name VARCHAR(100) ) CREATE TABLE Gruppe_Gruppe ( ParentGruppeId INT , ChildGruppeId INT ) CREATE TABLE Kunde_Gruppe ( KundeId INT , GruppeId INT ) INSERT Gruppe VALUES (1, 'Group 1'), (2, 'Group 2'), (3, 'Group 3') , (4, 'Sub-group A'), (5, 'Sub-group B'), (6, 'Sub-group C'), (7, 'Sub-group D') INSERT Kunde VALUES (1, 'Kunde 1'), (2, 'Kunde 2'), (3, 'Kunde 3') INSERT Gruppe_Gruppe VALUES (1, 4), (1, 5), (1, 7) , (2, 6), (2, 7) , (6, 1) INSERT Kunde_Gruppe VALUES (1, 1), (1, 2) , (2, 3), (2, 4) ;WITH CTE AS ( SELECT CONVERT(VARCHAR(1000), REPLACE(CONVERT(CHAR(5), k.Id), ' ', 'K')) AS TheKey , k.Name AS Name FROM Kunde k UNION ALL SELECT CONVERT(VARCHAR(1000), REPLACE(CONVERT(CHAR(5), x.KundeId), ' ', 'K') + REPLACE(CONVERT(CHAR(5), g.Id), ' ', 'G')) AS TheKey , g.Name FROM Gruppe g JOIN Kunde_Gruppe x ON g.Id = x.GruppeId UNION ALL SELECT CONVERT(VARCHAR(1000), p.TheKey + REPLACE(CONVERT(CHAR(5), g.Id), ' ', 'G')) AS TheKey , g.Name FROM Gruppe g JOIN Gruppe_Gruppe x ON g.Id = x.ChildGruppeId JOIN CTE p ON REPLACE(CONVERT(CHAR(5), x.ParentGruppeId), ' ', 'G') = RIGHT(p.TheKey, 5) WHERE LEN(p.TheKey) < 32 * 5 ) SELECT * , LEN(TheKey) / 5 AS Level FROM CTE c ORDER BY c.TheKey
Performance may not be optimal if you have a lot of readings or rare changes.