2011-12-22 69 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