我被要求为我们的应用程序生成一个权限矩阵,我想知道最好的方法。
如果我的数据如下:
所需的输出看起来像这样:
能够动态执行此操作的最佳方法是什么?因此,添加新角色或菜单选项或更改标志时,它始终是最新的?我感觉它是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)
答案 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解决方案。
感谢所有花时间的人。