(反向)递归查询

Pet*_*eed 3 sql t-sql common-table-expression sql-server-2008

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

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...
  ...
Run Code Online (Sandbox Code Playgroud)

我会检索所有“父母”(真的,孩子,但无论如何)和他们任何给定角色的递归父母。例如,我希望有一个查询要求 的父母role.Databases返回role.Applicationsrole.Everyone。同样,我期望的父母要求查询role.NewsService到的回报role.Clientrole.Everyonerole.Client_All,因为role.Client是两个孩子role.Everyonerole.Client_All

我尝试在MSDN 的 CTE 示例之后对查询建模,如下所示,但我无法获得所有递归父项。任何人都可以将我的 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
Run Code Online (Sandbox Code Playgroud)

jkl*_*ack 5

感谢提供全面的 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 
Run Code Online (Sandbox Code Playgroud)