(反向)递归查询

时间:2011-12-22 18:04:43

标签: sql sql-server-2008 tsql common-table-expression

我们有一个角色继承结构,而不是最高级别的过滤,它假定默认情况下每个人都获得最低级别的角色,图形描述如下:

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.Clientrole.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

1 个答案:

答案 0 :(得分:4)

感谢全面的SQL和示例数据 - 这使得构建答案变得更加容易!看起来你的主要错误是让自己在父母与父母之间感到困惑。儿童。我认为你过分关注结构是如何“反向”并且颠覆你的想法。我对您的SQL进行了两次主要编辑,以使其正常工作。

1)翻转父母&目前的项目。在“AttribAssign”中,我将ATTRIBID视为“父”,将“ITEMID”视为“子”,因此您有一个很好的常规树。我也最终在UNION的下半部分(递归部分)翻转来排队

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