To extract hierarcies based on parent-child relationships you can try with Common Table Expressions. This is a new feature from MS SQL Server 2005 and it can do some magic. And for retrieving hierarchies it's by far more effective than using a CURSOR.
WITH cte (LVL, EMPID, MGRID )
AS (
SELECT
1 AS LVL , EmployeeID AS EMPID, ManagerID AS MGRID
FROM HumanResources.Employee
WHERE ManagerID IS NULL
UNION ALL
SELECT
cte.LVL + 1, emp.EmployeeID, emp.ManagerID
FROM HumanResources.Employee emp
INNER JOIN cte ON emp.ManagerID = cte.EMPID
)
SELECT * FROM cte
ORDER BY LVL ASC
To explain how this works I'll simplify the CTE as a named and defined resultset. By naming it, it'll become addressable and this makes the magic. Now you can address previously retrieved rows. If we break down the query in the main parts it'll be easier to explain.
First the declarative part, WITH cte(LVL, EMPID, MGRID). This names the resultset to cte with the columns. Secondly we have the AS(...) that is the logic and will do the retrieval of rows. Third and finally the extraction of the data, SELECT * FROM cte ORDER BY LVL. This is the easy part.
If we look at the AS(...)-part. It consists of two SELECT-statements combined with a UNION ALL. The first SELECT-statement retrieves the top level of the hierarchy by retrieving the members that do not have a ManagerID, the 1 AS LVL symbolizes the top level. The second SELECT-statement joins the table against the resultset of the CTE based on cte.EMPID = emp.ManagerID, and in the same time we add 1 to cte.LVL to symbolize the level of the current member. The UNION ALL adds the rows retrieved in the second SELECT-statement with the resultset of the CTE. The second SELECT-statement is then repeated until there are no further rows to retrieve or if the MAXRECURSION-level is reached, by default 100.
This second CTE retrieves the full hierarchy down to the level where a certain member resides.
WITH cte (LVL, EMPID, MGRID )
AS (
SELECT
1 AS LVL , EmployeeID AS EMPID, ManagerID AS MGRID
FROM HumanResources.Employee
WHERE ManagerID IS NULL
UNION ALL
SELECT
cte.LVL + 1, emp.EmployeeID, emp.ManagerID
FROM HumanResources.Employee emp
INNER JOIN cte ON emp.ManagerID = cte.EMPID
)
SELECT * FROM cte
WHERE LVL <= ( SELECT LVL FROM cte WHERE EMPID = 279 )
ORDER BY LVL ASC
The third example retrieves the direct chain of parents for a certain member. A higher LVL means in this example higher up in the hierarchy.
WITH cte (LVL, EMPID, MGRID )
AS (
SELECT
1 AS LVL, EmployeeID AS EMPID, ManagerID AS MGRID
FROM HumanResources.Employee
WHERE EmployeeID = 279
UNION ALL
SELECT
cte.LVL + 1, emp.EmployeeID, emp.ManagerID
FROM HumanResources.Employee emp
INNER JOIN cte ON cte.MGRID = emp.EmployeeID
)
SELECT * FROM cte
ORDER BY LVL DESC