SQL Server:从一个表中选择与层次结构相关的项

时间:2014-01-17 12:09:43

标签: sql sql-server hierarchy

说,我有一个5级深度的组织结构:

CEO -> DeptHead -> Supervisor -> Foreman -> Worker

层次结构存储在表Position中,如下所示:

PositionId | PositionCode | ManagerId
         1 |          CEO |      NULL
         2 |       DEPT01 |         1
         3 |       DEPT02 |         1
         4 |       SPRV01 |         2
         5 |       SPRV02 |         2
         6 |       SPRV03 |         3
         7 |       SPRV04 |         3
       ... |          ... |       ...

PositionIduniqueidentifierManagerId是员工经理的ID,在同一张表中引用PositionId

我需要一个SQL查询来让层次结构树从一个位置下移,作为参数提供,包括位置本身。我设法开发了这个:

-- Select the original position itself
SELECT
'Rank' = 0,
Position.PositionCode
FROM Position
WHERE Position.PositionCode = 'CEO' -- Parameter
-- Select the subordinates
UNION
SELECT DISTINCT
'Rank' =
    CASE WHEN Pos2.PositionCode IS NULL THEN 0 ELSE 1+
        CASE WHEN Pos3.PositionCode IS NULL THEN 0 ELSE 1+
            CASE WHEN Pos4.PositionCode IS NULL THEN 0 ELSE 1+
                CASE WHEN Pos5.PositionCode IS NULL THEN 0 ELSE 1
                END
            END
        END
    END,
'PositionCode' = RTRIM(ISNULL(Pos5.PositionCode, ISNULL(Pos4.PositionCode, ISNULL(Pos3.PositionCode, Pos2.PositionCode)))),
FROM Position Pos1
LEFT JOIN Position Pos2
ON Pos1.PositionId = Pos2.ManagerId
LEFT JOIN Position Pos3
ON Pos2.PositionId = Pos3.ManagerId
LEFT JOIN Position Pos4
ON Pos3.PositionId = Pos4.ManagerId
LEFT JOIN Position Pos5
ON Pos4.PositionId = Pos5.ManagerId
WHERE Pos1.PositionCode = 'CEO' -- Parameter
ORDER BY Rank ASC

它不仅适用于'CEO',也适用于任何职位,展示其下属。这给了我以下输出:

Rank | PositionCode
   0 |          CEO
 ... |          ...
   2 |       SPRV55
   2 |       SPRV68
 ... |          ...
   3 |       FRMN10
   3 |       FRMN12
 ... |          ...
   4 |       WRKR01
   4 |       WRKR02
   4 |       WRKR03
   4 |       WRKR04

我的问题是:

输出不包括中间节点 - 它只输出终端节点,即 no subordinates 的工作人员和中间管理员。我也需要所有中级经理。

我必须在输出顶部手动UNION具有原始位置的行。我有更优雅的方式来做这件事吗?

我希望输出按照hieararchical树顺序排序。不是所有的DeptHeads,然后是所有的主管,然后是所有的工头,然后是所有的工人,但是像这样:

Rank | PositionCode
   0 |          CEO
   1 |       DEPT01
   2 |       SPRV01
   3 |       FRMN01
   4 |       WRKR01
   4 |       WRKR02
 ... |          ...
   3 |       FRMN02
   4 |       WRKR03
   4 |       WRKR04
 ... |          ...

非常感谢任何帮助。

3 个答案:

答案 0 :(得分:1)

尝试递归CTE,TechNet上的示例几乎与我认为的问题相同:

http://technet.microsoft.com/en-us/library/ms186243(v=sql.105).aspx

答案 1 :(得分:1)

Thx,每个人都建议CTE。我得到了以下代码,它工作正常:

WITH HierarchyTree (PositionId, PositionCode, Rank)
AS
(
-- Anchor member definition
    SELECT PositionId, PositionCode, 
        0 AS Rank
    FROM Position AS e
    WHERE PositionCode = 'CEO'
    UNION ALL
-- Recursive member definition
    SELECT e.PositionId, e.PositionCode, 
        Rank + 1
    FROM Position AS e
    INNER JOIN HierarchyTree AS d
        ON e.ManagerId = d.PositionId
)
SELECT Rank, PositionCode
FROM HierarchyTree
GO

答案 2 :(得分:0)

我在最近的一个项目中遇到了类似的问题,但是它的递归长度可变 - 通常在1到10级之间。

我想简化SQL方面的事情,所以除了直接管理器Id之外,我还通过存储“分层路径”将一些额外的工作放入存储递归元素的逻辑中。

这是一个非常人为的例子:

员工

Id |  JobDescription   | Hierarchy | ManagerId
1  |  DIRECTOR         |   1\      |   NULL
2  |  MANAGER 1        |   1\2\    |   1
3  |  MANAGER 2        |   1\3\    |   1
4  |  SUPERVISOR 1     |   1\2\4   |   2
5  |  SUPERVISOR 2     |   1\3\5   |   3
6  |  EMPLOYEE 1       |   1\2\4\6 |   4
7  |  EMPLOYEE 2       |   1\3\5\7 |   5

这意味着您可以通过在层次结构列上使用LIKE查询来快速查询树的任何级别并获取所有后代

例如

SELECT * FROM dbo.Employee WHERE Hierarchy LIKE '\1\2\%' 

将返回

MANAGER 1
SUPERVISOR 1
EMPLOYEE 1

此外,您还可以使用ManagerId列轻松获取树的一个级别。

这种方法的缺点是你必须在插入或更新记录时构建层次结构,但是当我说这个存储结构在以后为我节省了很多痛苦而不需要不必要的查询复杂性时,请相信我。

需要注意的一点是,我的方法为您提供原始数据 - 然后我将结果集解析为服务层中的递归强类型结构。作为一项规则,我不倾向于在S​​QL中格式化输出。