递归是否适合SQL Server?

时间:2008-10-10 13:16:20

标签: sql-server recursion

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

递归似乎是这个问题的直观候选者,我可以编写一个SQL Server函数来执行此操作。

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

8 个答案:

答案 0 :(得分:5)

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

结帐this question,特别是this answer

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

使用MySQL的AFAIK,你必须使用递归。

或者,您始终可以为记录父级>子关系

构建缓存表

答案 1 :(得分:2)

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

答案 2 :(得分:1)

您使用的是SQL 2005吗?

如果是这样,您可以使用Common Table Expressions。这些方面的东西:

;
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

答案 3 :(得分: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到员工表)
EmployeeId(PK,FK到员工表)

所有父子关系船都将存储在此表中,因此如果Manager 1管理Manager 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的员工回归。上行将是更好的表现,但缺点是维持层次结构

答案 4 :(得分:1)

Joe Celko有一个book(&lt; - 亚马逊链接),专门针对SQL数据库中的树结构。虽然您需要递归模型,并且肯定存在潜在的性能问题,但还有其他方法可以根据您的具体问题进行建模树形结构,从而避免递归并提供更好的性能。

答案 5 :(得分:0)

也许还有一些细节。

如果你描述了一个主 - 细节关系,那么简单的JOIN不会得到你需要的吗?

如:

SELECT
  SOME_FIELDS
FROM
  MASTER_TABLE MT
 ,CHILD_TABLE CT
WHERE CT.PARENT_ID = MT.ITEM_ID

答案 6 :(得分: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
)

答案 7 :(得分:0)

根本不需要递归.... 注意,我将列更改为ItemID和ItemParentID以便于输入...

DECLARE @intLevel INT
SET @intLevel = 1

INSERT INTO TempTable(ItemID, ItemParentID, Level) SELECT ItemID, ItemParentID, @intLevel WHERE ItemParentID IS NULL

WHILE @intLevel < @TargetLevel BEGIN SET @intLevel = @intLevel + 1 INSERT INTO TempTable(ItemID, ItemParentID, Level) SELECt ItemID, ItemParentID, @intLevel WHERE ItemParentID IN (SELECT ItemID FROM TempTable WHERE Level = @intLevel-1) -- If no rows are inserted then there are no children IF @@ROWCOUNT = 0 BREAK END

SELECt ItemID FROM TempTable WHERE Level = @TargetLevel