我可以用CTE,Pivot,组合或其他东西来解决这个问题吗?

时间:2018-01-23 11:43:17

标签: sql sql-server

我被要求为我们的应用程序生成一个权限矩阵,我想知道最好的方法。

如果我的数据如下:

enter image description here

所需的输出看起来像这样:

enter image description here

能够动态执行此操作的最佳方法是什么?因此,添加新角色或菜单选项或更改标志时,它始终是最新的?我感觉它是CTE-land(可能是+ Pivot),但我现在似乎无法将我的大脑弯曲。

当然,还有很多很多角色和菜单选项! (我还可以获取菜单项ID而不仅仅是名称,显示的数据已经是我写的查询的结果。)

理论上,菜单可以有深度,实际上我们最大的是4级,

CREATE TABLE #question (
    RoleID int NOT NULL,
    RoleName varchar(50) NOT NULL,
    IsReadOnly bit NOT NULL,
    ParentMenuName varchar(100) NULL,
    MenuName varchar(100) NOT NULL,
    Regulierer bit NOT NULL,
    Station bit NOT NULL
) 


insert into #question (RoleID, RoleName, IsReadOnly, ParentMenuName, MenuName, Regulierer, Station) VALUES (1, N'Administrator', 0, NULL, N'Source Data', 0, 0)
insert into #question (RoleID, RoleName, IsReadOnly, ParentMenuName, MenuName, Regulierer, Station) VALUES (1, N'Administrator', 0, N'Source Data', N'Item', 1, 0)
insert into #question (RoleID, RoleName, IsReadOnly, ParentMenuName, MenuName, Regulierer, Station) VALUES (1, N'Administrator', 0, N'Source Data', N'Stations', 1, 0)
insert into #question (RoleID, RoleName, IsReadOnly, ParentMenuName, MenuName, Regulierer, Station) VALUES (1, N'Administrator', 0, N'Source Data', N'Cupboards', 1, 1)
insert into #question (RoleID, RoleName, IsReadOnly, ParentMenuName, MenuName, Regulierer, Station) VALUES (1, N'Administrator', 0, NULL, N'Print', 0, 0)
insert into #question (RoleID, RoleName, IsReadOnly, ParentMenuName, MenuName, Regulierer, Station) VALUES (1, N'Administrator', 0, N'Item List', N'by Item Number', 1, 0)
insert into #question (RoleID, RoleName, IsReadOnly, ParentMenuName, MenuName, Regulierer, Station) VALUES (1, N'Administrator', 0, N'by Item Number', N'ascending', 1, 0)
insert into #question (RoleID, RoleName, IsReadOnly, ParentMenuName, MenuName, Regulierer, Station) VALUES (1, N'Administrator', 0, N'by Item Number', N'descending', 1, 0)
insert into #question (RoleID, RoleName, IsReadOnly, ParentMenuName, MenuName, Regulierer, Station) VALUES (1, N'Administrator', 0, N'Item List', N'by Description', 1, 0)
insert into #question (RoleID, RoleName, IsReadOnly, ParentMenuName, MenuName, Regulierer, Station) VALUES (1, N'Administrator', 0, N'Print', N'Item List', 1, 0)
insert into #question (RoleID, RoleName, IsReadOnly, ParentMenuName, MenuName, Regulierer, Station) VALUES (2, N'Assistant', 0, NULL, N'Source Data', 0, 0)
insert into #question (RoleID, RoleName, IsReadOnly, ParentMenuName, MenuName, Regulierer, Station) VALUES (2, N'Assistant', 0, N'Source Data', N'Item', 1, 0)
insert into #question (RoleID, RoleName, IsReadOnly, ParentMenuName, MenuName, Regulierer, Station) VALUES (2, N'Assistant', 0, N'Source Data', N'Stations', 1, 0)
insert into #question (RoleID, RoleName, IsReadOnly, ParentMenuName, MenuName, Regulierer, Station) VALUES (2, N'Assistant', 1, N'Source Data', N'Cupboards', 1, 1)

2 个答案:

答案 0 :(得分:2)

我在解决方案上做了一些工作。但是,由于时间不够,我无法完成它。但它应该给你一个大致的想法。 毕竟,你对CTE和Pivot是正确的。我使用了两者的组合来实现结果。

首先我用#question结构定义了一个表变量(因此你会在我的代码中找到@question)。

在第一个cte中,我创建了一个菜单的递归树 - 这是为了覆盖你提到的“动态深度”。接下来的两个ctes进行旋转。在下文中,旋转的值连接在一起并转换为'h','s'和' - '。下一个cte准备一个空白树结构并评估菜单条目的深度。最后一个cte评估顶级Flag(如果任何子值为h或s,则为X)。最后但并非最不重要的是,最后一个查询根据菜单条目的深度处理菜单名称的缩进,并将admin和assistant的值加入其中。

希望这对你有所帮助。

WITH cte
AS (
    SELECT 1 lvl
        ,q.RoleID
        ,q.RoleName
        ,q.MenuName TopLvlMenu
        ,q.IsReadOnly
        ,q.ParentMenuName
        ,q.MenuName
        ,CAST(q.MenuName AS VARCHAR(MAX)) AS MenuSrt
        ,q.Regulierer
        ,q.Station
    FROM @question q
    WHERE q.ParentMenuName IS NULL

    UNION ALL

    SELECT lvl + 1 lvl
        ,c.RoleID
        ,c.RoleName
        ,c.TopLvlMenu
        ,q.IsReadOnly
        ,q.ParentMenuName
        ,q.MenuName
        ,CAST(c.MenuSrt + ' - ' + q.MenuName AS VARCHAR(MAX)) AS MenuSrt
        ,q.Regulierer
        ,q.Station
    FROM cte AS c
    JOIN @question AS q ON q.ParentMenuName = c.MenuName
        AND q.RoleID = c.RoleID
        AND q.RoleName = c.RoleName
    WHERE q.ParentMenuName IS NOT NULL
    )
,cteHospital AS (
    SELECT MenuSrt
        ,Administrator
        ,Assistant
    FROM (
        SELECT MenuSrt
            ,RoleName
            ,CAST(Regulierer AS TINYINT) AS Regulierer
        FROM cte
        ) AS j
    PIVOT(MAX(Regulierer) FOR RoleName IN (
                Administrator
                ,Assistant
                )) AS p
    )
,cteStation AS (
    SELECT MenuSrt
        ,Administrator
        ,Assistant
    FROM (
        SELECT MenuSrt
            ,RoleName
            ,CAST(Station AS TINYINT) AS Station
        FROM cte
        ) AS j
    PIVOT(MAX(Station) FOR RoleName IN (
                Administrator
                ,Assistant
                )) AS p
    )
,cteAdminAss AS (
    SELECT ISNULL(h.MenuSrt, s.MenuSrt) MenuSrt
        ,ISNULL(NULLIF(CASE 
                    WHEN h.Administrator = 1
                        THEN 'h,'
                    ELSE ''
                    END + CASE 
                    WHEN s.Administrator = 1
                        THEN 's,'
                    ELSE ''
                    END, ''), '-,') AS Administrator
        ,ISNULL(h.Administrator,0) + ISNULL(s.Administrator, 0) AS Administrator_Num
        ,ISNULL(NULLIF(CASE 
                    WHEN h.Assistant = 1
                        THEN 'h,'
                    ELSE ''
                    END + CASE 
                    WHEN s.Assistant = 1
                        THEN 's,'
                    ELSE ''
                    END, ''), '-,') AS Assistant
        ,ISNULL(h.Assistant,0) + ISNULL(s.Assistant, 0) AS Assistant_Num
    FROM cteHospital h
    FULL JOIN cteStation s ON h.MenuSrt = s.MenuSrt
    )
,cteTree AS (
    SELECT MIN(lvl) lvl
        ,MenuName
        ,MenuSrt
    FROM cte
    GROUP BY MenuName
        ,MenuSrt
    )
,cteTopLevel AS(
    SELECT c.TopLvlMenu, CASE WHEN SUM(caa.Administrator_Num) > 0 THEN 'X' ELSE '-' END Administrator, CASE WHEN SUM(caa.Assistant_Num) > 0 THEN 'X' ELSE '-' END Assistant
      FROM cteAdminAss caa
      JOIN cte c ON c.MenuSrt = caa.MenuSrt
      GROUP BY c.TopLvlMenu
    )
SELECT REPLICATE('          ', lvl - 1) + ct.MenuName AS MenuName
    ,COALESCE(ctl.Administrator, SUBSTRING(ca.Administrator, 1, LEN(ca.Administrator) - 1)) Administrator
    ,COALESCE(ctl.Assistant, SUBSTRING(ca.Assistant, 1, LEN(ca.Assistant) - 1)) Assistant
FROM cteTree ct
JOIN cteAdminAss ca ON ct.MenuSrt = ca.MenuSrt
LEFT JOIN cteTopLevel ctl ON ctl.TopLvlMenu = ct.MenuName
ORDER BY ct.MenuSrt, ct.lvl
OPTION (MAXRECURSION 0)

答案 1 :(得分:0)

我认为我最好在不在SQL中的应用程序中处理此问题。许多角色和许多菜单选项的组合似乎不适合SQL解决方案。

感谢所有花时间的人。