我在 SQL 服务器中有一个表,它具有 Item_ID、Item_ParentID 的正常树结构。假设我想迭代并获取特定 Item_ID 的所有 CHILDREN(在任何级别)。
递归似乎是解决这个问题的一个直观的候选者,我可以编写一个 SQL Server 函数来做到这一点。
如果我的表有很多记录,这会影响性能吗?如何避免递归并简单地查询表?请问有什么建议吗?
我在 SQL 服务器中有一个表,它具有 Item_ID、Item_ParentID 的正常树结构。假设我想迭代并获取特定 Item_ID 的所有 CHILDREN(在任何级别)。
递归似乎是解决这个问题的一个直观的候选者,我可以编写一个 SQL Server 函数来做到这一点。
如果我的表有很多记录,这会影响性能吗?如何避免递归并简单地查询表?请问有什么建议吗?
作为一般答案,只需使用迭代算法,就可以在 SQL Server 中做一些通常需要递归的非常复杂的东西。我设法在 Transact SQL 中做了一个 XHTML 解析器,效果出奇的好。我编写的代码美化器是在存储过程中完成的。它不是优雅的,它就像看水牛做芭蕾一样。但它有效。
您使用的是 SQL 2005 吗?
如果是这样,您可以为此使用公用表表达式。这些方面的东西:
;
with CTE (Some, Columns, ItemId, ParentId) as
(
select Some, Columns, ItemId, ParentId
from myTable
where ItemId = @itemID
union all
select a.Some, a.Columns, a.ItemId, a.ParentId
from myTable as a
inner join CTE as b on a.ParentId = b.ItemId
where a.ItemId <> b.ItemId
)
select * from CTE
递归和性能将面临的问题是它必须递归多少次才能返回结果。每个递归调用都是另一个单独的调用,必须加入到总结果中。
在 SQL 2k5 中,您可以使用公共表表达式来处理此递归:
WITH Managers AS
(
--initialization
SELECT EmployeeID, LastName, ReportsTo
FROM Employees
WHERE ReportsTo IS NULL
UNION ALL
--recursive execution
SELECT e.employeeID,e.LastName, e.ReportsTo
FROM Employees e INNER JOIN Managers m
ON e.ReportsTo = m.employeeID
)
SELECT * FROM Managers
或另一种解决方案是将层次结构展平到另一个表中
Employee_Managers
ManagerId(PK,FK 到 Employee 表)
EmployeeId(PK,FK 到 Employee 表)
所有父子关系都将存储在此表中,因此如果经理 1 管理经理 2 管理员工 3,则该表如下所示:
ManagerId EmployeeId
1 2
1 3
2 1
这允许轻松查询层次结构:
select * from employee_managers em
inner join employee e on e.employeeid = em.employeeid and em.managerid = 42
这将返回所有拥有经理 42 的员工。好处将是更好的表现,但坏处将是维持层次结构
Joe Celko 有一本书(<- 链接到亚马逊)专门介绍 SQL 数据库中的树结构。虽然您的模型需要递归,并且肯定存在性能问题的可能性,但根据您的具体问题涉及的具体问题,有其他方法可以对树结构进行建模,这可以避免递归并提供更好的性能。
也许一些更多的细节是有序的。
如果您有描述的主从关系,那么简单的 JOIN 不会得到您所需要的吗?
如:
SELECT
SOME_FIELDS
FROM
MASTER_TABLE MT
,CHILD_TABLE CT
WHERE CT.PARENT_ID = MT.ITEM_ID
您不应该需要对孩子进行递归-您只需要查看正下方的级别(即select * from T where ParentId = @parent
)-您只需要对所有后代进行递归。
在 SQL2005 中,您可以通过以下方式获取后代:
with AllDescendants (ItemId, ItemText) as (
select t.ItemId, t.ItemText
from [TableName] t
where t.ItemId = @ancestorId
union
select sub.ItemId, sub.ItemText
from [TableName] sub
inner join [TableName] tree
on tree.ItemId = sub.ParentItemId
)
您根本不需要递归......请注意,我将列更改为 ItemID 和 ItemParentID 以便于输入......
声明 @intLevel INT 设置@intLevel = 1插入到 TempTable(ItemID,ItemParentID,级别) 选择 ItemID、ItemParentID、@intLevel ItemParentID 为空
而@intLevel < @TargetLevel 开始 设置@intLevel = @intLevel + 1 插入到 TempTable(ItemID,ItemParentID,级别) 选择 ItemID、ItemParentID、@intLevel WHERE ItemParentID IN(从 TempTable WHERE Level = @intLevel-1 中选择 ItemID) -- 如果没有插入行,则没有子代 如果@@ROWCOUNT = 0 休息 结尾
从 TempTable WHERE Level = @TargetLevel 中选择 ItemID