2015-06-04 88 views
0

我有兩個表跟蹤用戶組的權限。第一個表只有兩列,一個標識符和一個名稱,僅用於權限的名稱。第二個表是應用權限的位置,父權限被分配以創建層次結構。我的問題是,我使用連接創建基於父權限的權限層次結構「字符串」,並且不知道父遞歸可能會有多深,我無法知道要創建多少聯接。我的問題是,是否有更正確的方法來解決這個問題?如何處理扁桌中的遞歸?

我已經包含了完整的工作腳本,但我剝離不必要的列:

CREATE TABLE #TempPermissions 
(
    Permission_ID INT IDENTITY, 
    Permission VARCHAR(50) 
) 

CREATE TABLE #TempAppPermissions 
(
    AppPermission_ID INT IDENTITY, 
    Permission_ID INT, 
    Parent_ID INT 
) 

INSERT INTO #TempPermissions VALUES ('Users') 
INSERT INTO #TempPermissions VALUES ('Add') 
INSERT INTO #TempPermissions VALUES ('Edit') 
INSERT INTO #TempPermissions VALUES ('Remove') 
INSERT INTO #TempPermissions VALUES ('Permissions') 
INSERT INTO #TempPermissions VALUES ('Configure') 

INSERT INTO #TempAppPermissions VALUES (1, -1) 
INSERT INTO #TempAppPermissions VALUES (2, 1) 
INSERT INTO #TempAppPermissions VALUES (3, 1) 
INSERT INTO #TempAppPermissions VALUES (4, 1) 
INSERT INTO #TempAppPermissions VALUES (5, 1) 
INSERT INTO #TempAppPermissions VALUES (6, 5) 

SELECT app.AppPermission_ID, 
     (CASE WHEN NOT child3.Permission IS NULL THEN '/' + child3.Permission ELSE '' END)+ 
     (CASE WHEN NOT child2.Permission IS NULL THEN '/' + child2.Permission ELSE '' END)+ 
     '/' + child1.Permission AS PermissionString 
FROM #TempAppPermissions app 
INNER JOIN #TempPermissions child1 
    ON child1.Permission_ID = app.Permission_ID 
LEFT JOIN #TempAppPermissions parent1 
    ON parent1.AppPermission_ID = app.Parent_ID 
LEFT JOIN #TempPermissions child2 
    ON child2.Permission_ID = parent1.Permission_ID 
LEFT JOIN #TempAppPermissions parent2 
    ON parent2.AppPermission_ID = parent1.Parent_ID 
LEFT JOIN #TempPermissions child3 
    ON child3.Permission_ID = parent2.Permission_ID 

DROP TABLE #TempPermissions, #TempAppPermissions 

這爲我提供了結果:

AppPermission_ID PermissionString 
1 /Users 
2 /Users/Add 
3 /Users/Edit 
4 /Users/Remove 
5 /Users/Permissions 
6 /Users/Permissions/Configure 

這工作得很好原樣,但如果我是去深深的另一位父母:

INSERT INTO #TempPermissions VALUES ('Reports') 
INSERT INTO #TempAppPermissions VALUES (7, 6) 

我將不得不補償它與另一組o ˚F連接和在select語句另一種情況表達:

(CASE WHEN NOT child4.Permission IS NULL THEN '/' + child4.Permission ELSE '' END)+ 

... 

LEFT JOIN #TempAppPermissions parent3 
    ON parent3.AppPermission_ID = parent2.Parent_ID 
LEFT JOIN #TempPermissions child4 
    ON child4.Permission_ID = parent3.Permission_ID 

如果我不這樣做,我將最終失去對最後結果的最頂層父:

1 /Users 
2 /Users/Add 
3 /Users/Edit 
4 /Users/Remove 
5 /Users/Permissions 
6 /Users/Permissions/Configure 
7 /Permissions/Configure/Reports 

從技術上講,我可以重複任意以彌補這種結構可能會有多深的次數,但我覺得這個問題可能有更好的方法。提前致謝。

回答

2

我會使用CTE(公用表表達式)。

;WITH t AS (
SELECT 1 AS iteration, p.Permission_ID AS PermissionID, p.Permission_ID, CAST(N'/' + p.Permission AS NVARCHAR(MAX)) AS Permission 
FROM #TempPermissions AS p 
UNION ALL 
SELECT iteration + 1, t.PermissionID, p.Parent_ID, COALESCE(N'/' + (SELECT s.Permission FROM #TempPermissions AS s WHERE s.Permission_ID = p.Parent_ID), N'') + t.Permission 
FROM t INNER JOIN #TempAppPermissions AS p ON t.Permission_ID = p.Permission_ID 
) 
SELECT PermissionID, Permission FROM t 
WHERE Permission_ID = -1 
ORDER BY PermissionID, Iteration 

讓我知道這是否有幫助!

+0

這精美的作品。我必須分開來理解它才能實際應用它。謝謝! – Tim

+0

嗨,我不得不回覆我的答案作爲答案,謝謝! – Tim

0

在此補充最佳答案,因爲我無法在評論中發佈代碼。在玩了JoeFletch的代碼之後,我意識到實際的遞歸應該只發生在表#TempAppPermissions中。用JoeFletch的代碼,我會在更大的表上達到100的最大遞歸。迭代也不重要。

要注意的一件事是遞歸中第13行的「SELECT r.AppPermission_ID」,因爲我需要#TempAppPermissions中的那個孩子的ID(不是父母的)來引用回用戶以查看他們是否具有該權限。 #TempPermissions中的「Permission_ID」從select中省略,因爲它只需要獲取實際的權限名稱,「Parent_ID」僅用於過濾出依賴權限的單個實例。

再次感謝JoeFletch。

;WITH r AS 
(
    SELECT p.AppPermission_ID, 
      p.Parent_ID, 
      CAST('/' + (SELECT s.Permission 
         FROM #TempPermissions AS s 
         WHERE s.Permission_ID = p.Permission_ID) 
      AS NVARCHAR(MAX)) AS Permission 
    FROM #TempAppPermissions p 

    UNION ALL 

    SELECT r.AppPermission_ID, 
      p.Parent_ID, 
      COALESCE(N'/' + (SELECT s.Permission 
          FROM #TempPermissions AS s 
          WHERE s.Permission_ID = p.Permission_ID), N'') 
      + r.Permission 
    FROM r 
    INNER JOIN #TempAppPermissions p 
     ON p.AppPermission_ID = r.Parent_ID 
) 
SELECT r.AppPermission_ID, r.Permission 
FROM r 
WHERE r.Parent_ID = -1 
ORDER BY r.AppPermission_ID ASC