9

我在 SQL 服务器中有一个表,它具有 Item_ID、Item_ParentID 的正常树结构。假设我想迭代并获取特定 Item_ID 的所有 CHILDREN(在任何级别)。

递归似乎是解决这个问题的一个直观的候选者,我可以编写一个 SQL Server 函数来做到这一点。

如果我的表有很多记录,这会影响性能吗?如何避免递归并简单地查询表?请问有什么建议吗?

4

8 回答 8

5

使用新的 MS SQL 2005,您可以使用WITH关键字

看看这个问题,特别是这个答案

使用 Oracle,您可以使用CONNECT BY关键字来生成分层查询(语法)。

AFAIK 与 MySQL 你将不得不使用递归。

或者,您始终可以为您的记录父->子关系构建一个缓存表

于 2008-10-10T13:24:01.030 回答
2

作为一般答案,只需使用迭代算法,就可以在 SQL Server 中做一些通常需要递归的非常复杂的东西。我设法在 Transact SQL 中做了一个 XHTML 解析器,效果出奇的好。我编写的代码美化器是在存储过程中完成的。它不是优雅的,它就像看水牛做芭蕾一样。但它有效。

于 2008-10-10T14:04:00.590 回答
1

您使用的是 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
于 2008-10-10T13:30:13.277 回答
1

递归和性能将面临的问题是它必须递归多少次才能返回结果。每个递归调用都是另一个单独的调用,必须加入到总结果中。

在 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 的员工。好处将是更好的表现,但坏处将是维持层次结构

于 2008-10-10T13:51:56.620 回答
1

Joe Celko 有一本书(<- 链接到亚马逊)专门介绍 SQL 数据库中的树结构。虽然您的模型需要递归,并且肯定存在性能问题的可能性,但根据您的具体问题涉及的具体问题,有其他方法可以对树结构进行建模,这可以避免递归并提供更好的性能。

于 2008-10-10T14:37:16.107 回答
0

也许一些更多的细节是有序的。

如果您有描述的主从关系,那么简单的 JOIN 不会得到您所需要的吗?

如:

SELECT
  SOME_FIELDS
FROM
  MASTER_TABLE MT
 ,CHILD_TABLE CT
WHERE CT.PARENT_ID = MT.ITEM_ID
于 2008-10-10T13:18:19.590 回答
0

您不应该需要对孩子进行递归-您只需要查看正下方的级别(即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
)
于 2008-10-10T13:34:57.230 回答
0

您根本不需要递归......请注意,我将列更改为 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

于 2008-10-10T15:37:57.327 回答