Recursive CTE - Employee with multiple Manager

时间:2015-07-31 19:44:36

标签: sql sql-server tsql common-table-expression

I have a table like follow

+-----------+------------+
| ManagerID | EmployeeID |
+-----------+------------+
| MAN001    | BOY001     |
| MAN001    | BOY002     |
| MAN001    | BOY003     |
| MAN001    | BOY004     |
| MAN001    | BOY005     |
| BOY005    | KID001     |
| BOY005    | KID002     |
| BOY005    | KID003     |
| BOY005    | KID004     |
| MAN002    | BOY005     |
| MAN002    | BOY004     |
| BOY004    | KID001     |
| BOY004    | KID002     |
| BOY004    | KID003     |
| BOY004    | KID004     |
| KID002    | SOM001     |
| KID002    | SOM002     |
| KID002    | SOM003     |
+-----------+------------+

The main point is that MAN001 does not exists in EmployeeID, meaning MAN001 is top level. But BOY005 can work for MAN001 or MAN002 and more ... The same with KID00X CAN work with Any manager MAN00X or BOY00X.

It is also possible that KID001 KID002 KID003 KID004 may have an employee, eg KID002 has three Employee SOM001 SOM002 and SOM003 BUT BOY005 will never be an employee of KID because we know that BOY005 is the manager for KID.

The output that I would like to get is as follow, where the column E can grow as much as the data has.

+--------+--------+--------+--------+-------+
|   M1   |   E1   |   E2   |   E3   |  ...  |
+--------+--------+--------+--------+-------+
| MAN001 | BOY001 |        |        |       |
| MAN001 | BOY002 |        |        |       |
| MAN001 | BOY003 |        |        |       |
| MAN001 | BOY004 |        |        |       |
| MAN001 | BOY005 | KID001 |        |       |
| MAN001 | BOY005 | KID002 |        |       |
| MAN001 | BOY005 | KID003 |        |       |
| MAN001 | BOY005 | KID004 |        |       |
| MAN001 | BOY005 | KID002 | SOM001 |       |
| MAN001 | BOY005 | KID002 | SOM002 |       |
| MAN001 | BOY005 | KID002 | SOM003 |       |
+--------+--------+--------+--------+-------+

I tried to follow this basic simple recursive cte. But the example does not consider if an employee exists in multiple manager. http://blog.sqlauthority.com/2008/07/28/sql-server-simple-example-of-recursive-cte/

Also another different case with the blog post above is in my case Top level ManagerID simply don't exists in EmployeeID

WITH Emp_CTE AS (
SELECT EmployeeID, ManagerID
FROM Employee
WHERE ManagerID IS NULL
UNION ALL
SELECT e.EmployeeID, , e.ManagerID
FROM HumanResources.Employee e
INNER JOIN Emp_CTE ecte ON ecte.EmployeeID = e.ManagerID
)
SELECT *
FROM Emp_CTE

Thank you

EDIT1:

This is pretty close to what I am looking for. I Also think KID00X cannot be directly Under MAN001. Is there a more efficient automated way?

select PC.ManagerID,PC.EmployeeID,PC2.EmployeeID,PC3.EmployeeID
, PC4.EmployeeID, PC5.EmployeeID
from Employee PC
LEFT JOIN (select ManagerID,EmployeeID from Employee
where ManagerID in (select EmployeeID from Employee))PC2 ON PC2.ManagerID=PC.EmployeeID
LEFT JOIN (select ManagerID,EmployeeID from Employee
where ManagerID in (select EmployeeID from Employee
where ManagerID in (select EmployeeID from Employee)))PC3 ON PC3.ManagerID=PC2.EmployeeID
LEFT JOIN (select ManagerID,EmployeeID from Employee
where ManagerID in (select EmployeeID from Employee
where ManagerID in (select EmployeeID from Employee
where ManagerID in (select EmployeeID from Employee))))PC4 ON PC4.ManagerID = PC3.EmployeeID
LEFT JOIN (select ManagerID,EmployeeID from Employee
where ManagerID in (select EmployeeID from Employee
where ManagerID in (select EmployeeID from Employee
where ManagerID in (select EmployeeID from Employee
where ManagerID in (select EmployeeID from Employee)))))PC5 ON PC5.ManagerID = PC4.EmployeeID
Order by PC.ManagerID,PC.EmployeeID,PC2.EmployeeID,PC3.EmployeeID 
, PC4.EmployeeID, PC5.EmployeeID

1 个答案:

答案 0 :(得分:1)

DECLARE @select NVARCHAR(max)
DECLARE @from VARCHAR(max)
DECLARE @i INT
DECLARE @cnt INT
DECLARE @sql NVARCHAR(max)
DECLARE @countsql NVARCHAR(max)

SET @i = 1
SET @select = 'SELECT emp1.managerid as m1, emp1.employeeid as e1';
SET @from = ' FROM employee emp1 LEFT JOIN employee emp0 ON emp1.managerid=emp0.employeeid ';

SET @countsql = 'SELECT @cnt = COUNT(emp1.managerid) '
                + @from + 'WHERE emp0.employeeid IS NULL AND emp' + cast(@i as varchar(10))
                + '.employeeid IS NOT NULL' 

EXEC sp_executesql @countsql, N'@cnt int out', @cnt OUT

WHILE @cnt > 0
BEGIN
    SET @sql = @select + @from + ' WHERE emp0.employeeid IS NULL'
    SET @i = @i + 1

    SET @select = @select + ', emp' + cast(@i as varchar(10)) + '.employeeid as e'
                  + cast(@i as varchar(10))
    SET @from = @from + 'LEFT JOIN employee emp' + cast(@i as varchar(10)) + ' ON emp'
                  + cast(@i as varchar(10)) + '.managerid=emp'
                  + cast(@i - 1 as varchar(10)) + '.employeeid '

    SET @countsql = 'SELECT @cnt = COUNT(emp1.managerid) ' + @from
                    + 'WHERE emp0.employeeid IS NULL AND emp' + cast(@i as varchar(10))
                    + '.employeeid IS NOT NULL'
    EXEC sp_executesql @countsql, N'@cnt int out', @cnt OUT
END

EXEC sp_executesql @sql