如何从层次结构中获取所有用户关系的列表?

时间:2012-01-27 21:52:45

标签: sql sql-server sql-server-2008 hierarchical-data

我有用户列表。对于每个用户,由字段ParentId定义了层次结构(少数用户位于层次结构之上 - 它们在此字段中为空)。我不想更改此表的结构(并添加exaple hierarchyId到表)。

此刻我有这张桌子:
用户:

UserId INT NOT NULL, ManagerId INT NULL, other fields

我需要创建所有关系用户 - 祖先的列表以及这些用户之间的级别差异:

UserId, AncestorId, LevelDifference

例:
来自用户表:
UserId INT NOT NULL,ManagerId INT NULL
1,NULL,(吉姆)
2,1(约什)
3,2(珍妮)

我应该得到:
UserId,AncestorId,LevelDifference
2,1,1
3,2,1
3,1,2 - (吉姆是珍妮的祖先之一)

有没有人知道如何快速地做到这一点?

2 个答案:

答案 0 :(得分:3)

更新 - 这应该是您所寻找的。 使用递归CTE,如Joe Stefanelli所说:

表格结构:

CREATE TABLE [HR].[Employees](
    [empid] [int] IDENTITY(1,1) NOT NULL,
    [lastname] [nvarchar](20) NOT NULL,
    [firstname] [nvarchar](10) NOT NULL,
    [mgrid] [int] NULL
);

我使用的示例数据:

empid       lastname             firstname   mgrid
----------- -------------------- ----------  -----------
1           Davis                Sara        NULL
2           Funk                 Don         1
3           Lew                  Judy        2
4           Peled                Yael        3
5           Buck                 Sven        2
6           Suurs                Paul        5
7           King                 Russell     5
8           Cameron              Maria       3
9           Dolgopyatova         Zoya        5

查询:

WITH RCTE AS (

    SELECT NULL        AS PrevEmpId,
           NULL        AS PrevMgrId,
           E.empid     AS CurEmpId,
           E.mgrid     AS CurMgrid,
           0           AS [Level],
           E.lastname  AS LastName,
           E.firstname AS FirstName       
    FROM HR.Employees AS E
    WHERE E.mgrid IS NULL

    UNION ALL

    SELECT PREV.CurEmpId      AS PrevEmpId,
           PREV.CurMgrid      AS PrevMgrId,
           CUR.empid          AS CurEmpId,
           CUR.mgrid          AS CurMgrId, 
           Prev.Level + 1     AS [Level],
           CUR.lastname       AS LastName,
           CUR.firstname      AS FirstName
    FROM RCTE AS PREV
    JOIN HR.Employees AS CUR ON CUR.mgrid = PREV.CurEmpId
),RAnecestors AS (

    SELECT E.empid     AS StartEmpId,
           NULL        AS PrevEmpId,
           NULL        AS PrevMgrId,
           E.empid     AS CurEmpId,
           E.mgrid     AS CurMgrid,
           1           AS [LevelDiff],
           E.lastname  AS LastName,
           E.firstname AS FirstName       
    FROM HR.Employees AS E

    UNION ALL

    SELECT PREV.StartEmpId      AS StartEmpId,
           PREV.CurEmpId        AS PrevEmpId,
           PREV.CurMgrid        AS PrevMgrId,
           CUR.empid            AS CurEmpId,
           CUR.mgrid            AS CurMgrId, 
           Prev.[LevelDiff] + 1 AS [LevelDiff],
           CUR.lastname         AS LastName,
           CUR.firstname        AS FirstName
    FROM RAnecestors AS PREV
    JOIN HR.Employees AS CUR ON CUR.empid = PREV.CurMgrid
)
SELECT RCTE.CurEmpId           AS CurrentID,
       RCTE.LastName           AS CurrentLastName,
       RAnecestors.CurEmpId    AS AncestorID,
       RAnecestors.LastName    AS AncestorLastName,
       [Level]                 AS [Level],
       [LevelDiff] - 1         AS [LevelDiff]
LEFT JOIN RAnecestors ON RAnecestors.StartEmpId = RCTE.CurEmpId
      AND RCTE.CurEmpId <> RAnecestors.CurEmpId
ORDER BY RCTE.CurEmpId, RAnecestors.LevelDiff

输出:

CurrentID   CurrentLastName      AncestorID  AncestorLastName     Level       LevelDiff
----------- -------------------- ----------- -------------------- ----------- -----------
1           Davis                NULL        NULL                 0           NULL
2           Funk                 1           Davis                1           1
3           Lew                  2           Funk                 2           1
3           Lew                  1           Davis                2           2
4           Peled                3           Lew                  3           1
4           Peled                2           Funk                 3           2
4           Peled                1           Davis                3           3
5           Buck                 2           Funk                 2           1
5           Buck                 1           Davis                2           2
6           Suurs                5           Buck                 3           1
6           Suurs                2           Funk                 3           2
6           Suurs                1           Davis                3           3
7           King                 5           Buck                 3           1
7           King                 2           Funk                 3           2
7           King                 1           Davis                3           3
8           Cameron              3           Lew                  3           1
8           Cameron              2           Funk                 3           2
8           Cameron              1           Davis                3           3
9           Dolgopyatova         5           Buck                 3           1
9           Dolgopyatova         2           Funk                 3           2
9           Dolgopyatova         1           Davis                3           3

答案 1 :(得分:1)

我不会在SQL中这样做。只使用sql获取祖先用户列表很容易,但我不确定如何在没有tree结构的情况下计算leveldifference。我不是说你不能用sql做到这一点,我只是不知道解决方案。

我会将您的用户置于树数据结构中。从那里可以更容易地获得水平差异(子树的高度)。