SQL Server: иерархия / уровень учетной записи - PullRequest
0 голосов
/ 23 октября 2019

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

Я могу получить уровни, если передам одну запись «самого высокого уровня» в запрос и выведуобратно уровни иерархии, но мне нужно объединить это для всех записей, чтобы вернуть полный набор результатов.

Я покажу свои примеры ниже.

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

Основная таблица с самым высоким уровнем и идентификатором

ID      Code
---------------
2341    ACC0070
5689    ACC0590

Таблица суровни иерархии

Code       Indention    Name
---------------------------------
ACC0010       0         Level 1
ACC0015       1         Level 2
ACC0030       2         Level 3
ACC0065       2         Level 3 
ACC0070       3         Level 4
ACC0155       0         Level 1
ACC0460       1         Level 2
ACC0500       2         Level 3
ACC0585       2         Level 3
ACC0587       3         Level 4
ACC0590       3         Level 4

Конечный результат должен быть следующим:

ID     Code     Indentation     Name
----------------------------------------
2341   ACC0010       0          Level 1
2341   ACC0015       1          Level 2
2341   ACC0065       2          Level 3
2341   ACC0070       3          Level 4
5689   ACC0155       0          Level 1
5689   ACC0460       1          Level 2
5689   ACC0585       2          Level 3
5689   ACC0590       3          Level 4

В отступе будет только 4 уровня.

Уровни иерархии, которые должны быть возвращены, будутвсегда быть MAX([Code]) для предыдущего отступа для любого [Code], меньшего, чем наивысшая запись отступа.

Пример кода, который у меня есть, если я передам [Code] записи с отступом уровня 3. Это вернет правильные результаты для одной записи

SELECT 
    MAX(RowNr) AS [RowNr], MAX([Code]), Indentation 
FROM
    (SELECT 
         ROW_NUMBER() OVER (PARTITION BY Indentation ORDER BY [Code], Indentation DESC) AS [RowNr], 
         [Code], Indentation, [Name]
     FROM 
         <tablename>
     WHERE 
         [Code] <= 'ACC0070') AS res
GROUP BY 
    Indentation

Таким образом, в основном запрос должен выполняться для каждой записи со значением отступа 3 и возвращать уровни для всех записей.

Заранее спасибо.


РЕДАКТИРОВАТЬ

Я могу получить желаемые результаты с помощью курсора. Не уверен, что это будет возможно без использования курсора. Это было бы идеально.

-- Cursor Example
IF OBJECT_ID('tempdb..#HierarchyGL') IS NOT NULL DROP TABLE #HierarchyGL
GO
CREATE TABLE #HierarchyGL
(
    No_ VARCHAR(50),
    [Code] VARCHAR(50),
    [Name] VARCHAR(50)
);

IF OBJECT_ID('tempdb..#HierarchyTEST') IS NOT NULL DROP TABLE #HierarchyTEST
GO
CREATE TABLE #HierarchyTEST
(
    [Dimension Code] VARCHAR(50),
    Code VARCHAR(50),
    [Name] VARCHAR(50),
    Indentation int,
);

-- # INSERT Begin
INSERT INTO #HierarchyGL ([No_],[Code],[Name]) VALUES ('GL1234','ACC0070', 'G L Test Acc 1');
INSERT INTO #HierarchyGL ([No_],[Code],[Name]) VALUES ('GL1235','ACC0587', 'G L Test Acc 2');
INSERT INTO #HierarchyGL ([No_],[Code],[Name]) VALUES ('GL1236','ACC0590', 'G L Test Acc 3');

INSERT INTO #HierarchyTEST ([Dimension Code],Code,[Name],Indentation) VALUES ('GL', 'ACC0010', 'Level 1', 0);
INSERT INTO #HierarchyTEST ([Dimension Code],Code,[Name],Indentation) VALUES ('GL','ACC0015', 'Level 2', 1);
INSERT INTO #HierarchyTEST ([Dimension Code],Code,[Name],Indentation) VALUES ('GL','ACC0030', 'Level 3', 2);
INSERT INTO #HierarchyTEST ([Dimension Code],Code,[Name],Indentation) VALUES ('GL','ACC0035', 'Level 4', 3);
INSERT INTO #HierarchyTEST ([Dimension Code],Code,[Name],Indentation) VALUES ('GL','ACC0065', 'Level 3', 2);
INSERT INTO #HierarchyTEST ([Dimension Code],Code,[Name],Indentation) VALUES ('GL','ACC0070', 'Level 4', 3);
INSERT INTO #HierarchyTEST ([Dimension Code],Code,[Name],Indentation) VALUES ('GL','ACC0155', 'Level 1', 0);
INSERT INTO #HierarchyTEST ([Dimension Code],Code,[Name],Indentation) VALUES ('GL','ACC0460', 'Level 2', 1);
INSERT INTO #HierarchyTEST ([Dimension Code],Code,[Name],Indentation) VALUES ('GL','ACC0500', 'Level 3', 2);
INSERT INTO #HierarchyTEST ([Dimension Code],Code,[Name],Indentation) VALUES ('GL','ACC0585', 'Level 3', 2);
INSERT INTO #HierarchyTEST ([Dimension Code],Code,[Name],Indentation) VALUES ('GL','ACC0587', 'Level 4', 3);
INSERT INTO #HierarchyTEST ([Dimension Code],Code,[Name],Indentation) VALUES ('GL','ACC0590', 'Level 4', 3);
-- # INSERT End

IF OBJECT_ID('tempdb..#results') IS NOT NULL DROP TABLE #results
GO
CREATE TABLE #results
(
    GLAccNo VARCHAR(50),
    GLName VARCHAR(50),
    DimCode VARCHAR(50),
    DimName VARCHAR(50),
    Indentation int
);

DECLARE 
    @gl_no VARCHAR(20), 
    @gl_name VARCHAR(50),
    @dim_code  VARCHAR(50),
    @dim_name VARCHAR(50),
    @indentation int;

DECLARE cursor_hierarchy CURSOR
    FOR SELECT  [No_], gl.Name, hl.Code, hl.Name 
        FROM #HierarchyGL gl
        INNER JOIN #HierarchyTEST hl
        ON gl.Code = hl.Code;

OPEN cursor_hierarchy;

FETCH NEXT FROM cursor_hierarchy INTO   
    @gl_no,
    @gl_name,
    @dim_code,
    @dim_name;

WHILE @@FETCH_STATUS = 0 BEGIN
    PRINT @gl_no;

    -- get hierarchy
    INSERT INTO #results 
    SELECT @gl_no, @gl_name, MAX(Code) AS [Code], MAX([Name]), Indentation
    FROM (
        SELECT 
            ROW_NUMBER() OVER(PARTITION BY Indentation ORDER BY Code , Indentation DESC) AS RowNr,
            Code, [Dimension Code], Indentation, [Name]
        FROM #HierarchyTEST
        WHERE [Dimension Code] = 'GL'
        AND Code <= @dim_code
    ) AS res
    GROUP BY Indentation

    FETCH NEXT FROM cursor_hierarchy INTO   
        @gl_no,
        @gl_name,
        @dim_code,
        @dim_name;

END;

CLOSE cursor_hierarchy;
DEALLOCATE cursor_hierarchy;

SELECT * FROM #results;

И набор результатов

GLAccNo, GLName, DimCode, DimName, Indentation
GL1234, G L Test Acc 1, ACC0010, Level 1, 0 
GL1234, G L Test Acc 1, ACC0015, Level 2, 1 
GL1234, G L Test Acc 1, ACC0065, Level 3, 2 
GL1234, G L Test Acc 1, ACC0070, Level 4, 3 
GL1235, G L Test Acc 2, ACC0155, Level 1, 0 
GL1235, G L Test Acc 2, ACC0460, Level 2, 1 
GL1235, G L Test Acc 2, ACC0585, Level 3, 2 
GL1235, G L Test Acc 2, ACC0587, Level 4, 3 
GL1236, G L Test Acc 3, ACC0155, Level 1, 0 
GL1236, G L Test Acc 3, ACC0460, Level 2, 1 
GL1236, G L Test Acc 3, ACC0585, Level 3, 2 
GL1236, G L Test Acc 3, ACC0590, Level 4, 3 
...