9

我有一个这样的父 ID 列表,100, 110, 120, 130它是动态的并且可以更改。我想在一组中获取指定父母的所有后代。为了获得一个单亲的孩子,我使用了这样的查询:

WITH parent AS (
    SELECT PersonHierarchyID FROM PersonHierarchy
    WHERE PersonID = 100    
)
SELECT * FROM PersonHierarchy
WHERE PersonHierarchyID.IsDescendantOf((SELECT * FROM parent)) = 1

不知道如何为多个父母做到这一点。我的第一次尝试是写几个联合之类的东西,但是我确信应该有更聪明的方法来做这件事。

SELECT * FROM PersonHierarchy 
WHERE PersonHierarchyID.IsDescendantOf(
    (SELECT PersonHierarchyID FROM PersonHierarchy WHERE PersonID = 100)
) = 1
UNION ALL
SELECT * FROM PersonHierarchy 
WHERE PersonHierarchyID.IsDescendantOf(
    (SELECT PersonHierarchyID FROM PersonHierarchy WHERE PersonID = 110)
) = 1
UNION ALL ...

PS我还发现这样的查询来选择可能有帮助的ID列表:

SELECT * FROM (VALUES (100), (110), (120), (130)) AS Parent(ParentID)

总而言之,我的目标是编写接受父 ID 数组作为参数的查询,并在一个集合中返回它们的所有后代。

4

4 回答 4

16

你想太多了。

WITH parent AS (
    SELECT PersonHierarchyID FROM PersonHierarchy
    WHERE PersonID in (<list of parents>)    
)
SELECT * FROM PersonHierarchy
WHERE PersonHierarchyID.IsDescendantOf((SELECT * FROM parent)) = 1

不过,我会这样写:

select child.*
from PersonHierarchy as parent
inner join PersonHierarchy as child
   on child.PersonHierarchyID.IsDescendantOf(
       parent.PersonHierarchyId
   ) = 1
where Parent.PersonId in (<list of parents>)

注意:在这两种情况下,这可能会很慢,因为它必须为 n*m 个条目评估 IsDescendantOf(其中 n 是父母列表的基数,m 是表的基数)。

我最近遇到了一个类似的问题,我通过编写一个表值函数来解决它,给定一个 hierarchyId 将返回所有的父母。让我们看一下使用该方法的问题的解决方案。一、功能:

CREATE FUNCTION [dbo].[GetAllAncestors] (@h HierarchyId, @IncludeSelf bit)
RETURNS TABLE
AS RETURN

    WITH cte AS (
        SELECT @h AS h, 1 AS IncludeSelf
    )
    SELECT @h.GetAncestor(n.NumberId) AS Hierarchy
    FROM ref.Number AS n
    WHERE n.NumberId <= @h.GetLevel()
    AND n.NumberId >= 1

    UNION ALL

    SELECT h
    FROM cte
    WHERE IncludeSelf = @IncludeSelf

它假定您有一个 Numbers 表。它们非常有用。如果您没有,请在此处查看接受的答案。让我们先谈谈这个功能。本质上,它说“对于传入的hierarchyId,获取当前级别。然后调用GetAncestor,直到您处于层次结构的顶部。”。请注意,它可以选择返回传入的 hierarchyId。就我而言,我想将唱片视为其自身的祖先。你可能想也可能不想。

转向使用它的解决方案,我们得到如下信息:

select child.*
from PersonHierarchy as child
cross apply [dbo].[GetAllAncestors](child.PersonHierarchyId, 0) as ancestors
inner join PersonHierarchy as parent
  on parent.PersonHierarchyId = ancestors.Hierarchy
where parent.PersonId in (<list of parents>)

它可能适合您,也可能不适合您。试试看!

于 2014-08-01T15:02:00.480 回答
4

它可能对某人有用。我通过自连接查询找到了这样做的方法:

SELECT p2.* FROM PersonHierarchy p1
LEFT JOIN PersonHierarchy p2 
    ON p2.PersonHierarchyID.IsDescendantOf(p1.PersonHierarchyID) = 1
WHERE 
    p1.PersonID IN (100, 110, 120, 130)
于 2014-08-01T15:00:54.910 回答
1

您可以使用此查询

Select 
   child.*, 
   child.[PersonHierarchyID].GetLevel(),
   child.[PersonHierarchyID].GetAncestor(1)
From 
   PersonHierarchy as parents
   Inner Join PersonHierarchy as child
               On child.[PersonHierarchyID].IsDescendantOf(parents.[PersonHierarchyID] ) = 1
Where
   parents.[PersonHierarchyID] = 0x68
于 2019-03-20T06:45:17.200 回答
0

请检查,这应该适合你。我没有尝试修改您的脚本,只是将查询置于循环中。希望能帮助到你。

DECLARE @String VARCHAR(MAX) = '100, 110, 120, 130'
DECLARE @SQL VARCHAR(MAX)

SET @String = REPLACE(@String, CHAR(32), '') + ','

WHILE CHARINDEX(',', @String) > 0
    BEGIN
       DECLARE @ToString INT
       DECLARE @StringLength INT
       DECLARE @WorkingString VARCHAR(MAX)
       DECLARE @WorkingLength INT

       SET @ToString = CHARINDEX(',', @String)
       SET @StringLength = LEN(@String)
       SET @WorkingString = SUBSTRING(@String, 1, @ToString - 1)

       SET @String = SUBSTRING(@String, @ToString + 1, @StringLength)

       SET @WorkingString =  'SELECT * FROM PersonHierarchy ' + CHAR(13) + CHAR(10) 
                       + 'WHERE PersonHierarchyID.IsDescendantOf((SELECT PersonHierarchyID FROM PersonHierarchy WHERE PersonID = ' 
                       + @WorkingString + ')) = 1' + CHAR(13) + CHAR(10) 
                       + CASE WHEN CHARINDEX(',', @String) > 0 THEN 'UNION ALL'+ CHAR(13) + CHAR(10) ELSE '' END
       SET @SQL = ISNULL(@SQL,'') + @WorkingString
    END
PRINT @SQL
EXEC (@SQL)
于 2014-08-01T13:13:20.230 回答