This may require more thorough testing, I've only tested vs. your sample data, and it works for me.
I have created your initial table - in my example, this is called dbo.EmpHierarchy - so in my code below, just replace every instance of dbo.EmpHierarchy, with the table that contains your unflattened data.
From there, I run a few loops.
Loop #1
This first loop gives each employee a level number (depending on how deep down their manager levels go. e.g. employee 7 would be classed as level 3). I also do this, so I can determine the MAX level that we need to go to. In order to dynamically create our manager level table in the next step.
EDIT: The foundation of this answer is built on this first loop - credit to wBob for cooking this one up, see here: Synapse Top Level Parent Hierarchy
What follows from here, could possibly be replaced with something a little more concise / efficient - this is the only way I could figure out how to get your output.
Loop #2
This loop generates some dynamic SQL to create a temporary table, with enough columns to cover all the manager levels, by looping through until we hit the Max level from Loop #1
I've then inserted all the Employee Id's into that temp table (with no manager Id's yet)
After that, I run an update on the highest level of the hierarchy, to simply set the Supervisor Id to be their supervisor Id. We don't need to do anything special here. e.g. at this point we know Employee #7 has a Max level supervisor, so we set their supervisor here directly.
Loop #3
Final loop - I run DOWN through every level, starting at Max, and Update the appropriate Manager level to be equal to EITHER the supervisor Id of the employee (if we are looking at their current level) OR the supervisor Id, of the manager from the level Above.
Honestly, I'm not exactly a genius at this stuff - but I saw you didn't have any answers (until someone else gave you one earlier). So, even if this only points you in a direction, I hope it's useful.
-- Loop counter
DECLARE @counter INT = 0;
-- drop if temp table exists
IF OBJECT_ID('tempdb..#emp') IS NOT NULL DROP TABLE #emp;
-- Create temp table with initial records where employee directly reports
CREATE TABLE #emp
WITH (DISTRIBUTION = ROUND_ROBIN)
AS
WITH cte AS
( --supervisorId NULL means this is a top level Emp (no supervisor) - this hopefully covers the situation where
--Emp 1 isn't the only top level employee
SELECT 0 AS xlevel, *
FROM dbo.emphierarchy e
WHERE supervisorId IS NULL
)
SELECT *
FROM cte;
-- Loop through, to get the level for each employee
WHILE EXISTS
(
SELECT c.*
FROM #emp p
INNER JOIN dbo.emphierarchy c ON p.empId = c.SupervisorId
WHERE p.xlevel = @counter
)
BEGIN
-- Insert next level
INSERT INTO #emp ( xlevel, EmpId, SupervisorId )
SELECT @counter + 1 AS xlevel, c.EmpId, c.SupervisorId
FROM #emp p
INNER JOIN dbo.emphierarchy c ON p.EmpId = c.SupervisorId
WHERE p.xlevel = @counter;
SET @counter += 1;
-- break in case of infinite loop
IF @counter > 30
BEGIN
RAISERROR( 'Exceeded Loop Counter', 16, 1 )
BREAK
END;
END
DECLARE @MaxLevel INT = 1
SELECT @MaxLevel = MAX(Xlevel) From #emp
-- drop if temp table exists
IF OBJECT_ID('tempdb..#empFlat') IS NOT NULL DROP TABLE #empFlat;
DECLARE @SQLCreate VARCHAR(MAX) = 'CREATE TABLE #empFlat (Emp_Win INT NOT NULL, Manager_1 INT NULL '
DECLARE @SQLWith VARCHAR(MAX) = ') WITH (DISTRIBUTION = ROUND_ROBIN) '
DECLARE @SqlCounter INT = 2
-- Create our Dynamic Temp Table, with the right number of manager levels
WHILE @SqlCounter <= @MaxLevel
BEGIN
SET @SQLCreate = @SQLCreate + ', Manager_' + CAST(@SqlCounter AS VARCHAR(2)) + ' INT NULL'
SET @SqlCounter = @SqlCounter + 1
END
SET @SQLCreate = @SQLCreate + @SQLWith;
EXEC (@SQLCreate)
-- Insert all employee Id's into our Flat Table
INSERT INTO #empFlat (Emp_win)
SELECT EmpId FROM dbo.emphierarchy;
-- Next, we will run a series of updates on each employee, at each level
DECLARE @SQLUpdate VARCHAR(MAX)
--Run first update, for the max level - no need to do additional joins here, max level is the 'end' of the line
SET @SQLUpdate = 'UPDATE ef SET ef.Manager_' + CAST(@MaxLevel AS VARCHAR(2)) + ' = e.SupervisorId FROM #empFlat ef INNER JOIN #emp e ON ef.Emp_win = e.EmpId WHERE e.xlevel = ' + CAST(@MaxLevel AS VARCHAR(2))
EXEC (@SQLUpdate)
/*Loop through the remaining levels. Set the Manager ID to be equal to the manager of the previous level
OR just the manager of the current employee, if we are on their level */
SET @SQLCounter = @MaxLevel - 1
WHILE @SqlCounter > 0
BEGIN
SET @SQLUpdate = 'UPDATE ef SET ef.Manager_' + CAST(@SQLCounter AS VARCHAR(2)) + ' = COALESCE(e.SupervisorId, e1.SupervisorId) FROM #empFlat ef LEFT JOIN #emp e ON ef.Emp_win = e.EmpId AND e.xlevel = ' + CAST(@SQLCounter AS VARCHAR(2)) + ' LEFT JOIN #emp e1 ON e1.EmpId = ef.Manager_' + CAST(@SQLCounter + 1 AS VARCHAR(2)) + ' AND e1.xlevel = ' + CAST(@SQLCounter AS VARCHAR(2))
EXEC (@SQLUpdate)
SET @SqlCounter = @SqlCounter - 1
END
select * from #empFlat order by emp_win
Here is my output
