我有一个像这个100, 110, 120, 130
的父ID列表,它是动态的,可以改变。我希望在一组中获得指定父项的所有后代。为了获得单亲的孩子,我使用了这样的查询:
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 ...
附:我也找到了这样的查询来选择可能有用的id列表:
SELECT * FROM (VALUES (100), (110), (120), (130)) AS Parent(ParentID)
总而言之,我的目标是编写一个接受父ID数组作为参数的查询,并将所有后代返回到一个集合中。
你的想法太难了。
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表。它们非常有用。如果你没有,请查看接受的答案here。让我们来谈谈这个功能。从本质上讲,它表示“对于传入的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>)
它可能适用于您,也可能不适合您。试试看吧!
它可能对某人有用。我通过自联接查询找到了这样做的方法:
SELECT p2.* FROM PersonHierarchy p1
LEFT JOIN PersonHierarchy p2
ON p2.PersonHierarchyID.IsDescendantOf(p1.PersonHierarchyID) = 1
WHERE
p1.PersonID IN (100, 110, 120, 130)
您可以使用此查询
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
请检查,这应该适合你。我还没有尝试修改你的脚本,只是把查询放在循环中。希望能帮助到你。
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)