sqlsql-serverangularhighcharts

Organizing data into hierarchical structure with SQL Server


I have a SQL query that I used with SSRS for creating a hierarchical employee report. SSRS had the ability to have recursive parent groupings, which explains the following table structure of the data.

This is how I organized my hierarchical employee list table:

EmpNTID name RMNTID TopManagerEmpNTID Level
1000 Carmen 0
1001 Annette 1000 1000 1
1002 Shireen 1000 1000 1
1003 Jeffrey 1000 1000 1
1004 Eric 1001 1000 2
1005 Alton 1002 1000 2
1006 John 1002 1000 2
1007 Lynn 1003 1000 2
1008 Abhishek 1003 1000 2
1009 Alan 1004 1000 3
1010 Courtney 1004 1000 3
1011 Robert 1004 1000 3
1012 Michael 1004 1000 3

This is how I create my employee table:

BEGIN

DECLARE @ReportingDate date = GETDATE()
SET @ReportingDate = DATEADD(day, -1, @ReportingDate)

DECLARE @EmployeeTopMangerList TABLE 
        (
            RowID int IDENTITY (1, 1),
            [emplid] varchar(255), 
            EmpNTID varchar(255),
            [name] varchar(255), 
            TopManagerEmplID varchar(255),
            TopManagerEmpNTID varchar(255),
            TopManagerName varchar(255)
        )

---*****************add in loop
DECLARE @Employees TABLE
        (
            rowid int IDENTITY (1, 1),
            EmpNTID varchar(255)
        )

INSERT INTO @Employees
    SELECT DISTINCT NTID 
    FROM [dbo].DAILY_ACTIVE_RFCS_RPT
    WHERE ACTL_PCKG_RLS_DATE >= DATEADD(month, -8, DATEADD(DAY, 1, EOMONTH(@ReportingDate, 0)))

-- Get the number of rows in the looping table
DECLARE @RowCount INT
SET @RowCount = (SELECT COUNT(ROWID) FROM @Employees) 

-- Declare an iterator
DECLARE @I INT

-- Initialize the iterator
SET @I = 1

-- Loop through the rows of a table @myTable
WHILE (@I <= @RowCount)
BEGIN
    -- Declare variables to hold the data which we get after looping each record 
    DECLARE @iEmpNTID varchar(max), @iEmplID varchar(max)
    DECLARE @TM_Name varchar(200), @TM_EmplID varchar(25), @TM_EmpNTID varchar(25)

    -- Get the data from table and set to variables
    SELECT @iEmpNTID = EmpNTID   
    FROM @Employees 
    WHERE ROWID = @I;
 
    WITH Hierarchy([emplid],EmpNTID, [name], RMID,  HLevel) AS
    (
        SELECT 
            [emplid], 
            EmpNTID,
            [name], 
            RMID,
            0 AS HLevel
        FROM 
            vw_Employees_Combined 
        WHERE 
            -- EmplID = @iEmplID
            EmpNTID = @iEmpNTID

        UNION ALL

        SELECT 
            SubDepartment.[emplid], 
            SubDepartment.EmpNTID,
            SubDepartment.[name], 
            SubDepartment.RMID,
            HLevel + 1
        FROM 
            vw_Employees_Combined SubDepartment
        INNER JOIN 
            Hierarchy ParentDepartment ON SubDepartment.[emplid] = ParentDepartment.RMID 
    )
    SELECT TOP 1
        @TM_EmplID = [emplid], 
        @TM_EmpNTID = EmpNTID,
        @TM_Name = [name]
    FROM  
        Hierarchy
    ORDER BY
        HLevel DESC

INSERT INTO @EmployeeTopMangerList ([emplid], EmpNTID,[name], TopManagerEmplID, TopManagerEmpNTID, TopManagerName)
    SELECT 
        [emplid], EmpNTID, [name], @TM_EmplID, @TM_EmpNTID, @TM_Name 
    FROM 
        vw_Employees_Combined 
    WHERE 
        EmpNTID = @iEmpNTID     -- EmplID = @iEmplID

    -- -- Increment the iterator
    SET @I = @I + 1
END
----************************    

INSERT INTO @EmployeeTopMangerList ([emplid],EmpNTID,[name],TopManagerEmplID, TopManagerEmpNTID,TopManagerName )
    SELECT DISTCINT 
        a.NTID, a.NTID, a.assgn, '000000', 'Unknown', ' Unknown Manager' 
    FROM
        [dbo].DAILY_ACTIVE_RFCS_RPT a
    WHERE
        a.NTID NOT IN (SELECT EmpNTID FROM @EmployeeTopMangerList)

DECLARE @TopManagersTable TABLE
        (
            rowid int IDENTITY(1,1),
            TopManagerEmplID varchar(255),
            TopManagerEmpNTID varchar(255),
            TopManagerName varchar(255)
        )

INSERT INTO @TopManagersTable (TopManagerEmplID, TopManagerEmpNTID, TopManagerName)
    SELECT DISTINCT 
        TopManagerEmplID, TopManagerEmpNTID, TopManagerName 
    FROM
        @EmployeeTopMangerList

declare @EmployeeList table(
RowID int identity (1,1),
 EmplID varchar(255), 
 EmpNTID varchar(255), 
 [name] varchar(255),
 RMID varchar(255),
 TopManagerEmplID varchar(255)
 );

--------------------------------------------------------------------------
 SET @RowCount = (SELECT COUNT(ROWID) FROM @TopManagersTable) 

-- Declare an iterator
-- Initialize the iterator
 SET @I = 1

-- Loop through the rows of a table @myTable
WHILE (@I <= @RowCount)
 BEGIN
 -- Declare variables to hold the data which we get after looping each record 
-- Get the data from table and set to variables
 SELECT @iEmpNTID = TopManagerEmpNTID   FROM @TopManagersTable WHERE ROWID = @I;


with src as (
  select 
    [emplid]
    ,EmpNTID
    , [name]
    , case 
        when [emplid] = RMID then null --@CurrentUserEmplID then null--[supervisor_id] then null 
        else RMID 
     end as RMID
  from vw_Employees_Combined
), cte as ( 
  select [emplid],EmpNTID, [name], RMID, [emplid] as top_manager
  from src
  where EmpNTID = @iEmpNTID --1 -- this is the manager you're interested in
                     -- it can be a sub-manager in the middle or the tree
union all            -- or the top level manager rooting the tree

  select src.[emplid],src.EmpNTID, src.[name], src.RMID, top_manager
  from src 
  inner join cte on src.RMID = cte.[emplid]
  where src.[emplid] <> top_manager
)
insert into @EmployeeList
select * from cte;

-- -- Increment the iterator
SET @I = @I + 1

END
----************************    
------------------------------------------------------------------------------
--select count(*) from @EmployeeList
--select distinct emplid from @EmployeeList

Declare @EmployeeListLevelList as table(
 EmplID varchar(255), 
 EmpNTID varchar(255),
 [name] varchar(255),
 RMID varchar(255),
 RMNTID varchar(255),
 TopManagerEmplID varchar(255),
 TopManagerEmpNTID varchar(255),
 [Level] int
 );

 Insert into @EmployeeListLevelList(EmplID,EmpNTID,[name],RMID,TopManagerEmplID,[Level])
 select EmplID,EmpNTID,[name],RMID,TopManagerEmplID,'1'
 from @EmployeeList where RMID is null

 insert into @EmployeeListLevelList(EmplID,EmpNTID,[name],RMID,TopManagerEmplID,[Level])
 select EmplID,EmpNTID,[name],RMID,TopManagerEmplID,'2'
 from @EmployeeList where RMID = TopManagerEmplID

  insert into @EmployeeListLevelList(EmplID,EmpNTID,[name],RMID,TopManagerEmplID,[Level])
 select EmplID,EmpNTID,[name],RMID,TopManagerEmplID,'3'
 from @EmployeeList where  RMID in (select EmplID from @EmployeeListLevelList where [Level] = '2')

   insert into @EmployeeListLevelList(EmplID,EmpNTID,[name],RMID,TopManagerEmplID,[Level])
 select EmplID,EmpNTID,[name],RMID,TopManagerEmplID,'4'
 from @EmployeeList where  RMID in (select EmplID from @EmployeeListLevelList where [Level] = '3')

   insert into @EmployeeListLevelList(EmplID,EmpNTID,[name],RMID,TopManagerEmplID,[Level])
 select EmplID,EmpNTID,[name],RMID,TopManagerEmplID,'5'
 from @EmployeeList where  RMID in (select EmplID from @EmployeeListLevelList where [Level] = '4')

    insert into @EmployeeListLevelList(EmplID,EmpNTID,[name],RMID,TopManagerEmplID,[Level])
 select EmplID,EmpNTID,[name],RMID,TopManagerEmplID,'6'
 from @EmployeeList where  RMID in (select EmplID from @EmployeeListLevelList where [Level] = '5')

    insert into @EmployeeListLevelList(EmplID,EmpNTID,[name],RMID,TopManagerEmplID,[Level])
 select EmplID,EmpNTID,[name],RMID,TopManagerEmplID,'7'
 from @EmployeeList where  RMID in (select EmplID from @EmployeeListLevelList where [Level] = '6')

    insert into @EmployeeListLevelList(EmplID,EmpNTID,[name],RMID,TopManagerEmplID,[Level])
 select EmplID,EmpNTID,[name],RMID,TopManagerEmplID,'8'
 from @EmployeeList where  RMID in (select EmplID from @EmployeeListLevelList where [Level] = '7')

    insert into @EmployeeListLevelList(EmplID,EmpNTID,[name],RMID,TopManagerEmplID,[Level])
 select EmplID,EmpNTID,[name],RMID,TopManagerEmplID,'9'
 from @EmployeeList where  RMID in (select EmplID from @EmployeeListLevelList where [Level] = '8')

     insert into @EmployeeListLevelList(EmplID,EmpNTID,[name],RMID,TopManagerEmplID,[Level])
 select EmplID,EmpNTID,[name],RMID,TopManagerEmplID,'10'
 from @EmployeeList where  RMID in (select EmplID from @EmployeeListLevelList where [Level] = '9')

    insert into @EmployeeListLevelList(EmplID,EmpNTID,[name],RMID,TopManagerEmplID,[Level])
 select EmplID,EmpNTID,[name],RMID,TopManagerEmplID,'11'
 from @EmployeeList where  RMID in (select EmplID from @EmployeeListLevelList where [Level] = '10')

     insert into @EmployeeListLevelList(EmplID,EmpNTID,[name],RMID,TopManagerEmplID,[Level])
 select EmplID,EmpNTID,[name],RMID,TopManagerEmplID,'12'
 from @EmployeeList where  RMID in (select EmplID from @EmployeeListLevelList where [Level] = '11')

     insert into @EmployeeListLevelList(EmplID,EmpNTID,[name],RMID,TopManagerEmplID,[Level])
 select EmplID,EmpNTID,[name],RMID,TopManagerEmplID,'13'
 from @EmployeeList where  RMID in (select EmplID from @EmployeeListLevelList where [Level] = '12')

  update a
 set RMNTID = b.EMPNTID
 from @EmployeeListLevelList a, @EmployeeListLevelList b
 where a.RMID = b.EMPLID

  update a
 set TopManagerEmpNTID = b.EMPNTID
 from @EmployeeListLevelList a, @EmployeeListLevelList b
 where a.TopManagerEmplID = b.EMPLID

 truncate table DAILY_EmployeeListLevelList_BADGED
 insert into DAILY_EmployeeListLevelList_BADGED ([EmplID], [EmpNTID], [name], [RMID], [RMNTID], [TopManagerEmplID], [TopManagerEmpNTID], [Level])
 select Distinct [EmplID], [EmpNTID], [name], [RMID], [RMNTID], [TopManagerEmplID], [TopManagerEmpNTID], [Level] from @EmployeeListLevelList
 where EmplID is not null

truncate table DAILY_EmployeeListLevelList_Combined

insert into DAILY_EmployeeListLevelList_Combined (
[EmplID], [EmpNTID], [name], [MMID], [MMNTID], [MMTopManagerEmplID], [MMTopManagerEmpNTID], [MMLevel],
[RMID], [RMNTID], [RMTopManagerEmplID], [RMTopManagerEmpNTID], [RMLevel]
)

SELECT [EmplID], [EmpNTID], [name], [RMID], [RMNTID], [TopManagerEmplID], [TopManagerEmpNTID], [Level],
[RMID], [RMNTID], [TopManagerEmplID], [TopManagerEmpNTID], [Level]
FROM DAILY_EmployeeListLevelList_BADGED 

END

We've moved on from SSRS to using Angular/Highcharts for reporting. I am having a difficult time modifying my original query to return something that allows me to create a report in Angular that allows a user to drill down on an employee and see the people who report to them:

+ Carmen            
    + Annette       
        + Eric  
            Alan
            Courtney
            Robert
            Michael
    + Shireen       
        Alton   
        John    
    + Jeffrey       
        Lynn    
        Abhishek    

How can I structure my employee data to provide a report in Angular/Highcharts that allows me to expand a user to see the employees below them?


Solution

  • Consider recursive query.
    We select the main employees in the anchor part, considering that they have null in the link to the upper level. You can also choose according to another criterion.
    In the recursive part, you choose the next level of accountability. There are no unnecessary steps here. You don't need to filter anything.

    It would be interesting to see the result on a query to a table of 100K rows. And compare with your current method.
    I added "path" column for clarity.
    Recursion level is equal to hierarchy deep.

    See example

    with r as(
      select 0 as lvlN
        ,EmpNTID as TopManagerId,EmpNTID,name,RMNTID 
        ,cast(concat(name,':') as varchar(1000)) path
      from Hierarchy
      where TopManagerEmpNTID is null
      union all
      select lvlN+1 as lvlN
        ,TopManagerId,t.EmpNTID,t.name,t.RMNTID 
        ,cast(concat(path,'->',t.name) as varchar(1000)) path
      from r inner join Hierarchy t  on t.RMNTID=r.EmpNTID and r.EmpNTID<>t.EmpNTID
    )
    select * from r order by lvlN,EmpNTID;
    
    lvlN TopManagerId EmpNTID name RMNTID path
    0 1000 1000 Carmen 1000 Carmen:
    1 1000 1001 Annette 1000 Carmen:->Annette
    1 1000 1002 Shireen 1000 Carmen:->Shireen
    1 1000 1003 Jeffrey 1000 Carmen:->Jeffrey
    2 1000 1004 Eric 1001 Carmen:->Annette->Eric
    2 1000 1005 Alton 1002 Carmen:->Shireen->Alton
    2 1000 1006 John 1002 Carmen:->Shireen->John
    2 1000 1007 Lynn 1003 Carmen:->Jeffrey->Lynn
    2 1000 1008 Abhishek 1003 Carmen:->Jeffrey->Abhishek
    3 1000 1009 Alan 1004 Carmen:->Annette->Eric->Alan
    3 1000 1010 Courtney 1004 Carmen:->Annette->Eric->Courtney
    3 1000 1011 Robert 1004 Carmen:->Annette->Eric->Robert
    3 1000 1012 Michael 1004 Carmen:->Annette->Eric->Michael

    fiddle

    With

    select * from r order by path;
    

    Output is more like your hierarchical shcema.

    lvlN TopManagerId EmpNTID name RMNTID path
    0 1000 1000 Carmen 1000 Carmen:
    1 1000 1001 Annette 1000 Carmen:->Annette
    2 1000 1004 Eric 1001 Carmen:->Annette->Eric
    3 1000 1009 Alan 1004 Carmen:->Annette->Eric->Alan
    3 1000 1010 Courtney 1004 Carmen:->Annette->Eric->Courtney
    3 1000 1012 Michael 1004 Carmen:->Annette->Eric->Michael
    3 1000 1011 Robert 1004 Carmen:->Annette->Eric->Robert
    1 1000 1003 Jeffrey 1000 Carmen:->Jeffrey
    2 1000 1008 Abhishek 1003 Carmen:->Jeffrey->Abhishek
    2 1000 1007 Lynn 1003 Carmen:->Jeffrey->Lynn
    1 1000 1002 Shireen 1000 Carmen:->Shireen
    2 1000 1005 Alton 1002 Carmen:->Shireen->Alton
    2 1000 1006 John 1002 Carmen:->Shireen->John

    fiddle

    You can choose the appropriate output. For example, this.

    with r as(
      select 0 as lvlN
        ,EmpNTID as TopManagerId,EmpNTID,name,RMNTID -- ,TopManagerEmpNTID,Level
        ,cast(concat('0.',EmpNTID) as varchar(1000)) Id
        ,cast(concat('0','.0') as varchar(1000)) parent
      from Hierarchy
      where TopManagerEmpNTID is null
      union all
      select lvlN+1 as lvlN
        ,TopManagerId,t.EmpNTID,t.name,t.RMNTID -- ,t.TopManagerEmpNTID,t.Level
        ,cast(concat(lvlN+1,'.',t.EmpNTID) as varchar(1000)) Id
        ,cast(r.Id as varchar(1000)) parent
      from r inner join Hierarchy t  on t.RMNTID=r.EmpNTID and r.EmpNTID<>t.EmpNTID
      where lvlN<11
    )
    select * from r order by Id;
    
    lvlN TopManagerId EmpNTID name RMNTID Id parent
    0 1000 1000 Carmen 1000 0.1000 0.0
    1 1000 1001 Annette 1000 1.1001 0.1000
    1 1000 1002 Shireen 1000 1.1002 0.1000
    1 1000 1003 Jeffrey 1000 1.1003 0.1000
    2 1000 1004 Eric 1001 2.1004 1.1001
    2 1000 1005 Alton 1002 2.1005 1.1002
    2 1000 1006 John 1002 2.1006 1.1002
    2 1000 1007 Lynn 1003 2.1007 1.1003
    2 1000 1008 Abhishek 1003 2.1008 1.1003
    3 1000 1009 Alan 1004 3.1009 2.1004
    3 1000 1010 Courtney 1004 3.1010 2.1004
    3 1000 1011 Robert 1004 3.1011 2.1004
    3 1000 1012 Michael 1004 3.1012 2.1004

    Or

    [  
      {Id:'0.1000',parent:'0.0',name:'Carmen'}, 
      {Id:'1.1001',parent:'0.1000',name:'Annette'}, 
      {Id:'1.1002',parent:'0.1000',name:'Shireen'},   
      {Id:'1.1003',parent:'0.1000',name:'Jeffrey'}, 
      {Id:'2.1007',parent:'1.1003',name:'Lynn'}, 
      {Id:'2.1008',parent:'1.1003',name:'Abhishek'}, 
      {Id:'2.1005',parent:'1.1002',name:'Alton'}, 
      {Id:'2.1006',parent:'1.1002',name:'John'}, 
      {Id:'2.1004',parent:'1.1001',name:'Eric'}, 
      {Id:'3.1009',parent:'2.1004',name:'Alan'}, 
      {Id:'3.1010',parent:'2.1004',name:'Courtney'}, 
      {Id:'3.1011',parent:'2.1004',name:'Robert'}, 
      {Id:'3.1012',parent:'2.1004',name:'Michael'}
    ]