[英]Can I solve this with a CTE, Pivot, a combination or something else?
我被要求为我们的应用程序生成一个权限矩阵,我想知道最好的方法。
如果我有如下数据:
所需的输出如下所示:
能够动态执行此操作的最佳方法是什么-当添加新角色或菜单选项或更改标志时,它始终是最新的吗? 我感觉这是CTE领域(可能是+ 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)
我在解决方案上做了一些工作。 但是,由于时间不足,我无法完成它。 但这应该给您一个大致的想法。 毕竟,您对CTE和Pivot的看法是正确的。 为了达到结果,我将两者结合使用。
最初,我使用#question的结构定义了一个表变量(因此您将在我的代码中找到@question)。
在第一个CTE中,我创建菜单的递归树-以覆盖您提到的“动态深度”。 接下来的两个cte执行枢轴操作。 接下来,将枢轴值连接在一起并转换为“ h”,“ s”和“-”。 下一个cte准备一个空白树结构并评估菜单项的深度。 最后一个cte评估顶级标志(如果任何子值是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)
我认为最好不要在应用程序中使用SQL处理此问题。 许多角色和许多菜单选项的结合似乎并不适合SQL解决方案。
感谢所有为此付出时间的人。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.