2009-04-30 57 views
4

我使用嵌套集(aka修改的預定義樹遍歷)來存儲組的列表,我試圖找到一種快速方法來生成麪包屑(作爲字符串,而不是表)爲所有的羣體一次。我的數據也使用鄰接列表模型進行存儲(有觸發器使兩者保持同步)。查找嵌套集的麪包屑

因此,例如:

ID Name ParentId Left Right 
0 Node A 0   1  12 
1 Node B 0   2  5 
2 Node C 1   3  4 
3 Node D 0   6  11 
4 Node E 3   7  8 
5 Node F 4   9  9 

其表示樹:

  • 節點A
    • 節點B
      • 節點C
    • 節點d
      • 節點E
      • 節點F

我想能夠有一個返回表中的用戶定義的函數:

ID Breadcrumb 
0 Node A 
1 Node A > Node B 
2 Node A > Node B > Node C 
3 Node A > Node D 
4 Node A > Node D > Node E 
5 Node A > Node D > Node F 

爲了使這個稍微複雜一些(儘管它有點超出了問題的範圍),但我也有需要遵守的用戶限制。因此,舉例來說,如果我只訪問ID = 3,當我運行查詢,我應該得到:

ID Breadcrumb 
3 Node D 
4 Node D > Node E 
5 Node D > Node F 

我有一個用戶定義的函數,它接受一個用戶ID作爲參數,並返回一個表與所有有效的組的ID,所以只要在查詢的某個地方

WHERE group.id IN (SELECT id FROM dbo.getUserGroups(@userid)) 

它會工作。


我有一個現有的標量函數可以做到這一點,但它只是沒有在組(需要>經10秒鐘2000組)的任何合理數量的工作。它將一個groupid和userid作爲參數,並返回一個nvarchar。它查找給定的組父母(1個查詢獲取左/右值,另一個查找父母),將列表限制爲用戶有權訪問的組(使用與上述相同的WHERE子句,以及另一個查詢),然後使用遊標遍歷每個組並將其附加到一個字符串,然後才能最終返回該值。

我需要一種快速運行的方法(例如,< = 1s)。

這是在SQL Server 2005上。

回答

1

我最終什麼事做的是做一個大的聯合,簡單地捆綁此表本身,一遍又一遍,每級。

首先,我只填寫第一級組(如果您只有一個根,您可以跳過此步驟),然後@userGroups與用戶可以看到的組一起填充@topLevelGroups表。

SELECT groupid, 
    (level1 
    + CASE WHEN level2 IS NOT NULL THEN ' > ' + level2 ELSE '' END 
    + CASE WHEN level3 IS NOT NULL THEN ' > ' + level3 ELSE '' END 
    )as [breadcrumb] 
FROM (
    SELECT g3.* 
    ,g1.name as level1 
    ,g2.name as level2 
    ,g3.name as level3 
    FROM @topLevelGroups g1 
    INNER JOIN @userGroups g2 ON g2.parentid = g1.groupid and g2.groupid <> g1.groupid 
    INNER JOIN @userGroups g3 ON g3.parentid = g2.groupid 

    UNION 

    SELECT g2.* 
    ,g1.name as level1 
    ,g2.name as level2 
    ,NULL as level3 
    FROM @topLevelGroups g1 
    INNER JOIN @userGroups g2 ON g2.parentid = g1.groupid and g2.groupid <> g1.groupid 

    UNION 

    SELECT g1.* 
    ,g1.name as level1 
    ,NULL as level2 
    ,NULL as level3 
    FROM @topLevelGroups g1 

) a 
ORDER BY [breadcrumb] 

這是一個相當大的黑客,並明顯限制在一定的數量級的(我的應用程序,有一個合理的限度我可以選擇),這個問題,更多的層面得到支持,它以指數形式增加聯接的數量,因此速度要慢得多。

在代碼中做它最當然更容易,但對我來說簡直是並不總是一個選擇 - 有些時候我需要這個,請直接從一個SQL查詢。


我接受這個作爲回答,因爲這是我落得這樣做,它可能適用於其他人 - 但是,如果有人能拿出一個更有效的方法我就改成他們。

2

如果可以,使用路徑(或我想我已經聽說過它稱爲譜系)領域,如:

ID Name ParentId Left Right Path 
0 Node A 0   1  12  0, 
1 Node B 0   2  5  0,1, 
2 Node C 1   3  4  0,1,2, 
3 Node D 0   6  11  0,3, 
4 Node E 3   7  8  0,3,4, 
5 Node F 4   9  9  0,3,4, 

得到公正節點d及以後的(僞代碼):

path = SELECT Path FROM Nodes WHERE ID = 3 
SELECT * FROM Nodes WHERE Path LIKE = path + '%' 
0

沒有SQL服務器的特定代碼,但你只是在尋找:

SELECT * FROM表WHERE左<(currentid.left)和右>(currentid.right)

+0

這隻適用於特定節點(currentid),並返回一個表,而不是一個字符串。此外,它應該是<= and > =包含的節點,ORDER BY留下來把他們在等級秩序。例如:對於 「節點F」,ID 5,它會返回: 0節點A 3節點d 4節點F 那部分,我知道該怎麼做。我想將「節點A>節點D>節點F」作爲字段返回,並在一個大查詢中爲每個組執行此操作。 – gregmac 2009-04-30 22:40:06

+0

嗯SQL服務器的技能不走那麼遠,我可能會處理這件事我的數據庫之外。 – Evert 2009-04-30 23:07:13

2

這裏是爲我工作,從樹中的任何一點得到了「麪包屑」路徑的SQL。希望能幫助到你。

SELECT ancestor.id, ancestor.title, ancestor.alias 
FROM `categories` child, `categories` ancestor 
WHERE child.lft >= ancestor.lft AND child.lft <= ancestor.rgt 
AND child.id = MY_CURRENT_ID 
ORDER BY ancestor.lft 

凱絲

+0

這是一種簡單的方法來獲取單個id的路徑 - 實際上,這是使用MPTT結構的巨大優勢之一。但是它會爲每個祖先返回一行 - 最初的問題是關於如何爲所有節點批量查找麪包屑。 – gregmac 2011-08-05 15:59:59

3

確定。這是針對MySQL的,而不是SQL Server 2005.它使用了GROUP_CONCAT和子查詢。

這應該返回完整的麪包屑單列。

SELECT 
(SELECT GROUP_CONCAT(parent.name SEPARATOR ' > ') 
FROM category parent 
WHERE node.Left >= parent.Left 
AND node.Right <= parent.Right 
ORDER BY Left 
) as breadcrumb 
FROM category node 
ORDER BY Left 
1

我修改凱西的陳述得到麪包屑的每一個元素

SELECT 
    GROUP_CONCAT(
     ancestor.name 
     ORDER BY ancestor.lft ASC 
     SEPARATOR ' > ' 
    ), 
    child.* 
FROM `categories` child 
JOIN `categories` ancestor 
ON child.lft >= ancestor.lft 
AND child.lft <= ancestor.rgt 
GROUP BY child.lft 
ORDER BY child.lft 

隨意例如增加一個WHERE條件

WHERE ancestor.lft BETWEEN 6 AND 11