2011-08-11 38 views
0

我有以下內容的表:推薦的方法來搜索分層數據MSSQL2008

  • 類別ID
  • PARENTID
  • 名稱

我想有一個搜索功能,可能會搜索整個層次結構,例如,這是一個類別的麪包屑:

摩托車/ J apan /川崎/ 600cc到800cc/1998-2004

如果有人搜索「600cc川崎」,我希望返回上述類別。所以應該返回匹配最多的類別路徑。

目前,我想出了這一點:

IF ISNULL(@searchTerm, '') = '' 
    SET @searchTerm = '""' 
DECLARE @Result TABLE (CategoryId int) 

DECLARE CategoryCursor CURSOR LOCAL FAST_FORWARD FOR 
SELECT CategoryId, ParentId, Name 
FROM Category 
WHERE FREETEXT([Name], @searchTerm) 
OPEN CategoryCursor 
DECLARE @CategoryId int 
DECLARE @ParentId int 
DECLARE @Name nvarchar(100) 

FETCH NEXT FROM CategoryCursor INTO @CategoryId, @ParentId, @Name 
WHILE @@FETCH_STATUS = 0 
BEGIN 
    DECLARE @FullPath nvarchar(1000) 
    SET @FullPath = @Name 

    WHILE @ParentId <> 0 
    BEGIN 
     SELECT @ParentId = ParentId, @Name = [Name] 
     FROM Category 
     WHERE CategoryId = @ParentId 

     SET @FullPath = @Name + '\' + @FullPath  
    END 

    -- Check if @FullPath contains all of the searchterms 
    DECLARE @found bit 
    DECLARE @searchWords NVARCHAR(100) 
    DECLARE @searchText NVARCHAR(255) 
    DECLARE @pos int 

    SET @found = 1 
    SET @searchWords = @searchTerm + ' ' 
    SET @pos = CHARINDEX(' ', @searchWords) 
    WHILE @pos <> 0 
     BEGIN 
     SET @searchText = LEFT(@searchWords, @pos - 1) 
     SET @searchWords = STUFF(@searchWords, 1, @pos, '') 
     SET @pos = CHARINDEX(' ', @searchWords) 
     IF @searchText = '' CONTINUE 
     IF @FullPath NOT LIKE '%' + @searchText + '%' 
      BEGIN 
       SET @found = 0 
      BREAK 
      END 
     END 

    IF @found = 1 
     INSERT INTO @Result VALUES(@CategoryId) 

    FETCH NEXT FROM CategoryCursor INTO @CategoryId, @ParentId, @Name 
END 

CLOSE CategoryCursor 
DEALLOCATE CategoryCursor 

SELECT * 
FROM Category 
WHERE categoryID IN (SELECT categoryId FROM @Result) 

這將首先發現其含有任何searchwords的所有catagorynames。問題是,我不想讓其他品牌的「600cc」返回,只有與「川崎」有關的那個。 接下來,我爲當前類別構建麪包屑並查看它是否包含所有搜索詞。

它的工作原理,但我認爲這是無效的,所以我正在尋找一個更好的方法。

也許作爲文本存儲在一個新的列中的完整路徑和搜索?

+0

許多有用的參考文獻[這裏] [1]。 [1]:http://stackoverflow.com/questions/4048151/what-are-the-options-for-storing-hierarchical-data-in-a-relational-database – TMS

回答

0

我建議使用hierarchyid這是在2008年你會基本上設置您的層次結構是這樣

/1/- 根節點 /1/1/- 摩托車 /1/1/1/- 日本 /1/1/1/1/- 川崎 /1/1/1/2/- 本田 /1/1/2/- 美國 /1/1/2/1/- 哈雷。

然後,您可以使用hierarchyid從600cc 1984川崎一直到摩托車的整個樹。

下面是編程Microsoft SQL Server的代碼示例2008

CREATE FUNCTION dbo.fnGetFullDisplayPath(@EntityNodeId hierarchyid) RETURNS varchar(max) AS 
BEGIN  
    DECLARE @EntityLevelDepth smallint  
    DECLARE @LevelCounter smallint  
    DECLARE @DisplayPath varchar(max)  
    DECLARE @ParentEmployeeName varchar(max)  

    -- Start with the specified node  
    SELECT @EntityLevelDepth = NodeId.GetLevel(), 
    @DisplayPath = EmployeeName  
    FROM Employee  
    WHERE NodeId = @EntityNodeId  

    -- Loop through all its ancestors  
    SET @LevelCounter = 0  
    WHILE @LevelCounter < @EntityLevelDepth 
    BEGIN  
     SET @LevelCounter = @LevelCounter + 1  
     SELECT @ParentEmployeeName = EmployeeName   
     FROM Employee  WHERE NodeId = (SELECT NodeId.GetAncestor(@LevelCounter)    
      FROM Employee 
      WHERE NodeId = @EntityNodeId)  

     -- Prepend the ancestor name to the display path  
     SET @DisplayPath = @ParentEmployeeName + ' > ' + @DisplayPath  
    END  

    RETURN(@DisplayPath) 
END 

我/ 1/1/2的表示形式爲字符串表示。在數據庫中你可以看到十六進制表示(例如0x79)。

hierarchyid上有幾個關鍵函數。

declare @motorcycleAncestor hieararchyid 
select @motorcycleAncestor = nodeId.GetAncestor(1) 
from parts 
where Label = 'motorcycle' 

select * from Parts 
where Node.GetAncestor(1) = @motorcyleAncestor; 

這個查詢做了幾件事。首先,它將包含「摩托車」的節點的層次結構id作爲標籤。 (我假設hiearchy字段被命名爲'nodeid',但你可以明顯地稱之爲任何東西)。

接下來,它需要此節點值並查找摩托車的所有直接子代(其祖先是1級,是摩托車你可以實際指定任何值,比如GetAncestor(3)將會是祖先的3級)。所以在這種情況下,它會找到日本,美國,德國等。

還有另一種方法,稱爲IsDescendantOf(節點)。你可以這樣使用它:

declare @motorcycleAncestor hieararchyid 
select @motorcycleAncestor = nodeId.GetAncestor(1) 
from parts 
where Label = 'motorcycle' 

select * from Parts 
where Node.IsDescendantOf(@motorcycleAncestor) = 1 

這將返回所有摩托車下的兒童(任何級別)的物品。它實際上還包括摩托車。

你可以用不同的方式組合這些。例如,我們在一個組織結構圖中使用它們。我們有能力爲單個用戶,或者用戶和他的兄弟姐妹(同一級別的每個人)以及用戶和他的所有後代顯示結果。因此,我可以展示您的信息,或者我可以向您的部門展示每個人,也可以向您的公司中的每個人展示。

+0

這看起來是非常有用的,我一定會查看到hierarchyid類型。但是,如何在這種情況下進行搜索? – peter

+0

我已更新我的答案,包括如何搜索 – taylonr

+0

謝謝!但是,如果我搜索「川崎600cc」,我想只返回川崎的600cc類別,而不是其他品牌。因此,我仍然需要拆分搜索詞並遍歷祖先,以查看整個路徑是否包含全部或大部分搜索到的單詞。它會工作,但我仍然需要像我原來的解決方案一樣循環,我想這是沒有辦法的? (順便說一句,使用hierarchyid已經比使用CURSOR的舊解決方案顯示出了很多性能:)謝謝 – peter