简体   繁体   English

如何使用 SQL Server 2008hierarchyid 获取节点的所有祖先?

[英]How do you get all ancestors of a node using SQL Server 2008 hierarchyid?

Given a table with a hierarchyid type column, how do you write a query to return all rows that are ancestors of a specific node?给定一个具有hierarchyid类型列的表,如何编写查询以返回作为特定节点的祖先的所有行?

There is an IsDescendantOf() function, which is perfect for getting the children, but there's no corresponding IsAncestorOf() function to return ancestors (and the absence of a GetAncestors() function seems like quite an oversight.)有一个IsDescendantOf()函数,它非常适合获取孩子,但没有相应的IsAncestorOf()函数来返回祖先(并且缺少GetAncestors()函数似乎是一个疏忽。)

The most commonly used approach would be a recursive Common Table Expression (CTE)最常用的方法是递归公用表表达式 (CTE)

WITH Ancestors(Id, [Name], AncestorId) AS
(
      SELECT
            Id, [Name], Id.GetAncestor(1)
      FROM
            dbo.HierarchyTable
      WHERE
            Name = 'Joe Blow'  -- or whatever you need to select that node

      UNION ALL

      SELECT
            ht.Id, ht.[Name], ht.Id.GetAncestor(1)
      FROM
            dbo.HierarchyTable ht
      INNER JOIN 
            Ancestors a ON ht.Id = a.AncestorId
)
SELECT *, Id.ToString() FROM Ancestors

(adapted from a Simon Ince blog post ) (改编自Simon Ince 博客文章

Simon Ince also proposes a second approach where he just basically reverses the condition - instead of detecting those person entries that are an ancestor of the target person, he turns the check around:西蒙·因斯 (Simon Ince) 还提出了第二种方法,他只是基本上颠倒了条件——而不是检测那些是目标人的祖先的人条目,他把检查反过来:

DECLARE @person hierarchyid

SELECT @person = Id
FROM dbo.HierachyTable
WHERE [Name] = 'Joe Blow';

SELECT
    Id, Id.ToString() AS [Path], 
    Id.GetLevel() AS [Level],
    Id.GetAncestor(1),
    Name
FROM 
    dbo.HierarchyTable
WHERE 
    @person.IsDescendantOf(Id) = 1

This will select all the rows from your table, where the target person you're interested in is a descendant of - any level down the hierarchy.这将从您的表中选择所有行,其中您感兴趣的目标人员是其后代 - 层次结构中的任何级别。 So this will find that target person's immediate and non-immediate ancestors all the way up to the root.所以这会找到目标人的直系祖先和非直系祖先,一直到根。

Here's an answer rolled into a single select:这是一个包含在单个选择中的答案:

SELECT t1.Id.ToString() as Path, t1.Name
    FROM (SELECT * FROM HierarchyTable
        WHERE Name = 'Joe Blow') t2,
    HierarchyTable t1
    WHERE t2.Id.IsDescendantOf(t1.Id) = 1
Declare @hid hierarchyid=0x5D10 -- Child hierarchy id

SELECT
*
FROM 
  dbo.TableName
WHERE 
  @hid.IsDescendantOf(ParentHierarchyId) = 1

I wrote a user-defined table-valued function that expands a hierarchyid value into its constituent ancestors.我编写了一个用户定义的表值函数,它将一个hierarchyid 值扩展为其组成的祖先。 The output can then be joined back on the hierarchyid column to get those ancestors specifically.然后可以将输出连接回hierarchyid 列以明确获取这些祖先。

alter function dbo.GetAllAncestors(@h hierarchyid, @ReturnSelf bit)
returns table
as return
 select @h.GetAncestor(n.Number) as h
 from dbo.Numbers as n
 where n.Number <= @h.GetLevel()
  or (@ReturnSelf = 1 and n.Number = 0)

 union all

 select @h
 where @ReturnSelf = 1
go

To go about using it:要开始使用它:

select child.ID, parent.ID
from dbo.yourTable as child
cross apply dbo.GetAllAncestors(child.hid, 1) as a
join dbo.yourTable as parent
   on parent.hid = a.h

Perfecting Ben Thui's answer which I find to be the best one so far...完善 Ben Thui 的答案,我认为这是迄今为止最好的答案……

The approach below allows to retrieve not only one but potentially several leaf rows and their ascendants in one single query.下面的方法允许在一个查询中不仅检索一个而且可能检索多个叶行及其后代。

Create Or Alter Function dbo.GetAllAncestors
(
    @Path       HierarchyId,
    @WithSelf   Bit = 1,
    @MinLevel   Int = 0,
    @MaxLevel   Int = Null
)
Returns Table
As
Return

With Ancestor As
(
    Select  @Path As Path
    Union All

    Select  Path.GetAncestor(1)
    From    Ancestor
    Where   Path.GetLevel() > 0
)

Select  Path, Path.GetLevel() As Level
From    Ancestor
Where   (@WithSelf = 1 Or Path <> @Path)
And     Path.GetLevel() >= Case When @MinLevel < 0 Or @MinLevel Is Null Then 0 Else @MinLevel End
And     (@MaxLevel Is Null Or Path.GetLevel() <= @MaxLevel)

To use:使用:

-- This assumes the table has a Path HierarchyId colum, and the values are unique and indexed.

-- If you know the path
Select *
From MyTable
Where Path In
(
    Select Path From dbo.GetAllAncestors(@ThePath, Default, Default, Default)
)

-- If you don't know the path
Select *
From MyTable t1
Where Path In 
(
    Select Path
    From   MyTable t2
           Cross Apply dbo.GetAllAncestors(t2.Path, Default, Default, Default)
    Where  /* Find the leaf record(s) here.
              Note that if multiple rows match, they will all be returned as well as their parents in a single roundtrip. */
)
DECLARE @hid_Specific HIERARCHYID 
SET @hid_Specific = '/1/1/3/1/';

SELECT hrchy_id,* FROM tblHierarchyData 
WHERE PATINDEX(hrchy_id.ToString() + '%', @hid_Specific.ToString()) = 1

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM