2011-12-22 74 views
2

我们有一个角色继承结构,其中,而不是最高级别的筛选下来,它假定每个人都在默认情况下得到的最低水平的作用,图形描述如下:(反向)递归查询

role.Everyone //lowest level; everyone gets this role 
    role.Applications // everyone assigned this role gets applications && everyone roles 
    role.Databases // everyone assigned this role gets databases && applications && everyone roles 
    role.SoftwareSubscriber 
    role.Client_All // etc. 
    role.Client 
    role.ITClient 
    role.Client 
    role.NewsService // everyone assigned this role gets NewsService && Client && Everyone 
        // && Client_All roles, since Client is also a child of Client_All 
    role.ClientDeliverable // etc. 
    role.Employee 
    role.Corporate 
    role.Marketing 
    role... 
    ... 

我向检索所有“父母”(真的,孩子,但不管),以及任何给定角色的递归父母。例如,我希望查询请求role.Databases的父母返回role.Applicationsrole.Everyone。同样,我希望查询请求role.NewsService的父母返回role.Client,role.Everyonerole.Client_All,因为role.Clientrole.Everyonerole.Client_All的子项。

我试图在MSDN's CTE example之后建立一个查询,如下所示,但我无法获得所有递归父项。任何人都可以在正确的方向上引导我的CTE查询吗?

CREATE TABLE #ATTRIBASSIGN 
(
    ATTRIBID int not null 
    , ITEMID int not null 
    , ITEMCLASS VARCHAR(10) NOT NULL DEFAULT ('ATTRIB') 
    , CONSTRAINT PK_ATTRIBASSIGN_ATTRIBID_ITEMID_ITEMCLASS PRIMARY KEY (ATTRIBID, ITEMID, ITEMCLASS) 
) 

CREATE TABLE #ATTRIBPROP 
(
    ATTRIBID int not null identity(1,1) primary key 
    , ATTRIBNAME VARCHAR(50) not null 
) 
GO 

INSERT INTO #ATTRIBPROP (ATTRIBNAME) 
VALUES ('role.Databases'), ('role.Applications'), ('role.Everyone'), ('role.Client_All'), ('role.Employee'), ('role.SoftwareSubscriber'), 
    ('role.Client'), ('role.ITClient'), ('role.NewsService'), ('role.ClientDeliverable'), ('role.Corporate'), ('role.Marketing') 

GO 
INSERT INTO #ATTRIBASSIGN (ATTRIBID, ITEMID) 
SELECT A.ATTRIBID, B.ATTRIBID 
FROM #ATTRIBPROP A 
    CROSS JOIN #ATTRIBPROP B 
WHERE A.ATTRIBNAME = 'role.Everyone' 
    AND B.ATTRIBNAME = 'role.Applications' 
UNION 
SELECT A.ATTRIBID, B.ATTRIBID 
FROM #ATTRIBPROP A 
    CROSS JOIN #ATTRIBPROP B 
WHERE A.ATTRIBNAME = 'role.Everyone' 
    AND B.ATTRIBNAME = 'role.Client_All' 
UNION 
SELECT A.ATTRIBID, B.ATTRIBID 
FROM #ATTRIBPROP A 
    CROSS JOIN #ATTRIBPROP B 
WHERE A.ATTRIBNAME = 'role.Everyone' 
    AND B.ATTRIBNAME = 'role.Client' 
UNION 
SELECT A.ATTRIBID, B.ATTRIBID 
FROM #ATTRIBPROP A 
    CROSS JOIN #ATTRIBPROP B 
WHERE A.ATTRIBNAME = 'role.Everyone' 
    AND B.ATTRIBNAME = 'role.Employee' 
UNION 
SELECT A.ATTRIBID, B.ATTRIBID 
FROM #ATTRIBPROP A 
    CROSS JOIN #ATTRIBPROP B 
WHERE A.ATTRIBNAME = 'role.Applications' 
    AND B.ATTRIBNAME = 'role.Databases' 
UNION 
SELECT A.ATTRIBID, B.ATTRIBID 
FROM #ATTRIBPROP A 
    CROSS JOIN #ATTRIBPROP B 
WHERE A.ATTRIBNAME = 'role.Applications' 
    AND B.ATTRIBNAME = 'role.SoftwareSubscriber' 
UNION 
SELECT A.ATTRIBID, B.ATTRIBID 
FROM #ATTRIBPROP A 
    CROSS JOIN #ATTRIBPROP B 
WHERE A.ATTRIBNAME = 'role.Client_All' 
    AND B.ATTRIBNAME = 'role.Client' 
UNION 
SELECT A.ATTRIBID, B.ATTRIBID 
FROM #ATTRIBPROP A 
    CROSS JOIN #ATTRIBPROP B 
WHERE A.ATTRIBNAME = 'role.Client_All' 
    AND B.ATTRIBNAME = 'role.ITClient' 
UNION 
SELECT A.ATTRIBID, B.ATTRIBID 
FROM #ATTRIBPROP A 
    CROSS JOIN #ATTRIBPROP B 
WHERE A.ATTRIBNAME = 'role.Client' 
    AND B.ATTRIBNAME = 'role.NewsService' 
UNION 
SELECT A.ATTRIBID, B.ATTRIBID 
FROM #ATTRIBPROP A 
    CROSS JOIN #ATTRIBPROP B 
WHERE A.ATTRIBNAME = 'role.Client' 
    AND B.ATTRIBNAME = 'role.ClientDeliverable' 
UNION 
SELECT A.ATTRIBID, B.ATTRIBID 
FROM #ATTRIBPROP A 
    CROSS JOIN #ATTRIBPROP B 
WHERE A.ATTRIBNAME = 'role.Employee' 
    AND B.ATTRIBNAME = 'role.Corporate' 
UNION 
SELECT A.ATTRIBID, B.ATTRIBID 
FROM #ATTRIBPROP A 
    CROSS JOIN #ATTRIBPROP B 
WHERE A.ATTRIBNAME = 'role.Employee' 
    AND B.ATTRIBNAME = 'role.Marketing' 

GO 

WITH RoleStructure (parentRole, currentRole, Level) 
AS 
(
    SELECT B.ITEMID, B.ATTRIBID, 0 level 
    FROM #ATTRIBASSIGN B 
    WHERE B.ATTRIBID NOT IN 
     (
      SELECT ITEMID 
      FROM #ATTRIBASSIGN C 
      WHERE B.ATTRIBID = C.ITEMID 
     ) 
     AND B.ITEMCLASS = 'attrib' 
    UNION ALL 
    SELECT B.ITEMID, B.ATTRIBID, D.level - 1 
    FROM #ATTRIBASSIGN B 
     INNER JOIN RoleStructure D ON B.ATTRIBID = D.parentRole 
    WHERE B.ITEMCLASS = 'attrib' 
) 
SELECT B.ATTRIBNAME, C.ATTRIBNAME, level 
FROM RoleStructure A 
    INNER JOIN #ATTRIBPROP B ON A.parentRole = B.ATTRIBID 
    INNER JOIN #ATTRIBPROP C ON A.currentRole = C.ATTRIBID 

回答

3

感谢全面SQL和样本数据 - 即取得了很大的建设的回答更容易!看起来你的主要错误是让你自己在父母&孩子之间感到困惑。我认为你太过分关注结构是如何“倒退”,并且颠倒了你的想法。我对你的SQL做了两个主要的编辑来使它工作。

1)翻转父母&当前项目。在“AttribAssign”中,我将ATTRIBID作为“父母”和“ITEMID”作为“孩子”对待,所以你有一个很好的常规树。我也结束了翻转联盟的下半部分(递归部分)排队

2)我没有过滤'锚'数据集。额外的10行是必要的,以保持你想要的递归。我这样做是因为你想为任何父/子组合输出一行,而不管递归的级别如何。您的原始表格具有所有“直接”组合。你想要扩展每一个“直接”来包含N + 1层的间接性。你的查询只给出了“直接”关系。通过保留所有原始链接,我们可以构建该集合,以更好地查找所有链接,而不管间接程度如何。令人困惑,是的,但它工作。

;WITH RoleStructure (parentRole, currentRole, Level) 
AS 
( 
     SELECT B.ATTRIBID, B.ITEMID, 0 level 
     FROM #ATTRIBASSIGN B 
     WHERE B.ITEMCLASS = 'attrib' 

     UNION ALL 

     SELECT D.parentRole, B.ITEMID, D.level - 1 
     FROM #ATTRIBASSIGN B 
       INNER JOIN RoleStructure D ON B.ATTRIBID = D.currentRole 
     WHERE B.ITEMCLASS = 'attrib' 
) 
SELECT a.parentRole, a.currentRole, B.ATTRIBNAME, C.ATTRIBNAME, level 
FROM RoleStructure A 
     INNER JOIN #ATTRIBPROP B ON A.parentRole = B.ATTRIBID 
     INNER JOIN #ATTRIBPROP C ON A.currentRole = C.ATTRIBID 
+0

华丽 - 我已经彻底地测试您的查询,它的伟大工程。我会咀嚼你的解释,直到我完全吸收它。 – 2011-12-23 03:39:30

+0

不要太多 - 我所做的一半就是用SQL来玩,直到它工作。从逻辑上讲,我也有点困惑,但回想起来,我认为“破坏”MSDN示例的事情是,你没有一棵真正的树。 “客户”节点可以存在于多个父母下,所以你确实有一个有向图。虽然我的大脑仍然没有在所有的圆柱体上射击,但它的核心就在于它 – jklemmack 2011-12-23 06:49:00