推荐的搜索分层数据SQL 2008的方法

时间:2011-08-11 14:02:16

标签: sql-server-2008 search hierarchical-data

我有一张包含以下内容的表格:

  • 类别ID
  • PARENTID
  • 名称

我希望有一个搜索功能可以搜索整个层次结构,例如,这是一个类别的面包屑:

摩托车/日本/川崎/ 600cc至800cc / 1998-2004

如果有人搜索“600cc Kawasaki”,我希望退回上述类别。所以具有最多匹配的类别路径应该返回。

此刻我想出了这个:

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)

这将首先找到包含任何搜索词的所有catagorynames。问题是,我不希望其他品牌的“600cc”返回,只有与“川崎”相关的那个。 接下来,我为当前类别构建面包屑,看它是否包含所有搜索词。

它有效,但我认为它是无效的,所以我正在寻找一种更好的方法。

也许将完整路径作为文本存储在新列中并搜索它?

1 个答案:

答案 0 :(得分:0)

我建议使用2008年的hierarchyid。你基本上会设置这样的层次结构

/ 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 

My / 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;

这个查询做了几件事。首先,它获取包含“Motorcycle”作为标签的节点的层次结构ID。 (我假设hiearchy字段被命名为'nodeid'但你显然可以称之为。)

接下来,它获取此节点值并查找摩托车的所有直接子项(谁的祖先,1级,是摩托车节点。您实际上可以指定任何值,如GetAncestor(3)将是祖先3级以上)。那么在那种情况下,它会找到日本,美国,德国等。

还有另一种方法,称为IsDescendantOf( node )。您可以像这样使用它:

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

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

这将返回摩托车下面的所有儿童(任何等级)。它实际上还包括摩托车。

您可以通过不同方式组合这些。例如,我们在各种组织结构图中使用它们。我们能够为单个用户或用户及其兄弟姐妹(完全相同级别的每个人)以及用户及其所有后代显示结果。

所以我可以展示您的信息,或者我可以向您所在部门的每个人展示,或者我可以向您公司的每个人展示。