2011-08-11 35 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