Как получить список всех пользовательских отношений из иерархии? - PullRequest
3 голосов
/ 28 января 2012

У меня есть список пользователей.Для каждого пользователя существует иерархия, определяемая полем ParentId (немногие пользователи находятся на вершине иерархии - в этом поле они имеют значение NULL).Я не хочу изменять структуру этой таблицы (и добавить для exapleierarchyId в таблицу).

На данный момент у меня есть эта таблица:
Пользователи:

UserId INT NOT NULL, ManagerId INT NULL, other fields

Мне нужно создать список всех отношений пользователя-предка и разницы уровней между этими пользователями в форме:

UserId, AncestorId, LevelDifference

Пример:
Из таблицы пользователей:
UserId INT NOT NULL, ManagerId INT NULL
1, NULL, (Jim)
2,1 (Josh)
3, 2 (Дженни)

Я должен получить:
UserId, AncestorId, LevelDifference
2,1,1
3,2,1
3,1,2 - (Джимодин из предков Дженни)

Кто-нибудь знает, как это сделать быстро?

Ответы [ 2 ]

3 голосов
/ 28 января 2012

ОБНОВЛЕНО - Это должно быть то, что вы ищете. Использование рекурсивного CTE, как сказал Джо Стефанелли:

Структура таблицы:

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 голос
/ 28 января 2012

Я бы не стал делать это в SQL. Достаточно просто получить список пользователей с предками, используя только sql, но я не уверен, как бы вы вычислили уровень разницы без структуры tree. Я не говорю, что вы не можете сделать это с помощью sql, я просто не знаю, как это работает.

Я бы поместил ваших пользователей в древовидную структуру данных. Оттуда было бы легче получить разницу уровней (высота поддерева).

...