2011-08-11 18 views
0

を検索するための推奨方法私は、次の内容を持つテーブルがある:階層データMSSQL2008

  • 区分
  • のParentID
  • 名前

私はそれだろう検索機能を持っていると思いますたとえば階層全体を検索すると、これはカテゴリのブレッドクラムです。

オートバイ/ J APAN/KAWASAKI/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

私はあなたが、本質的にこの

/1 /のような階層を設定します2008年にあるhierarchyidを使用してお勧めしたい - ルート・ノード /1/1/- オートバイ /1/1/1/- 日本语 /1/1/1/- 川崎 /1/1/1/2 - - ホンダ/1/1/2/- US /1/1/2/1/- ハーレー。

次に、hierarchyidを使用して、600cc 1984川崎からオートバイまでの木全体を取得できます。

は、ここで私の/ 1/1月2日の表現は、文字列表現であるプログラミングの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 

からのコードサンプルです。データベースには、実際には16進表現(たとえば、0x79)が表示されます。

hierarchyidにはいくつかの重要な機能があります。

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

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

このクエリは2つのことを行います。まず、 "Motorcycle"をラベルとして含むノードの階層IDを取得します。 (私は検索フィールドが '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を使っているBTWは、すでにCURSORを使って私の古い解決法より多くのパフォーマンスを示しています:)ありがとう – peter