SQL - 如何使用CTE

时间:2017-02-10 12:35:58

标签: sql-server recursion

我有一个包含用户及其经理的表格:

|ID  | Title     | Manager |
|1   | Manager 1 | 4       |
|2   | Manager 2 | 1       |
|3   | Manager 3 | 1       |
|4   | Manager 4 | 2       |
|5   | Manager 5 | 3       |
...
|10  | Manager 10| NULL    |
|11  | Manager 11| 10      |

我有一个简单的递归查询,它返回层次结构中所有经理ID,从给定的最高管理者ID开始,然后是:

DECLARE @Managers TABLE (ManagerID int)
DECLARE @ManagerID int = 1
        BEGIN
            ;WITH ManagerCTE AS (
                                SELECT ID FROM tblUsers WHERE ID = @ManagerID
                                UNION ALL
                                SELECT chld.ID FROM tblUsers chld 
                                INNER JOIN ManagerCTE items ON chld.Manager = items.ID
                                )
            INSERT INTO @Managers
            SELECT ID FROM ManagerCTE
        END
SELECT * FROM @Managers

如果管理者的层次结构组织得很好,它就可以正常工作。但在某些情况下,我们的结构混乱,下级经理恰好是高级经理的经理: enter image description here

在这种情况下,递归查询进入循环,并且在语句完成之前,最大递归100正在耗尽。我需要从查询中排除这些管理器,如果它们被选择到结果表中,以避免这些循环。

我该怎么做?

另一种可能的解决方案是在达到某个级别时从递归中退出,或者它,例如,5。但是选项(maxrecursion 5)只是设置限制,如果达到限制,查询会产生错误。

如何逐步退出递归并继续执行脚本而没有任何错误?

2 个答案:

答案 0 :(得分:1)

正如我在评论中所说,你可以通过将访问节点as I do it here存储在增长的字符串路径中来检查它们,或者你可以通过递归来限制递归的深度CTE是这样的:

SELECT 1 AS CurrentLevel,ID FROM tblUsers WHERE ID = @ManagerID
UNION ALL
SELECT items.CurrentLevel+1,chld.ID FROM tblUsers chld 
INNER JOIN ManagerCTE items ON chld.Manager = items.ID
WHERE items.CurrentLevel<=5

答案 1 :(得分:0)

WITH

中试试
SELECT ID, ID::text as ids FROM tblUsers WHERE ID = @ManagerID
UNION ALL
SELECT chld.ID FROM tblUsers chld, items.ids || ',' || chld.ID::text as ids
INNER JOIN ManagerCTE items ON chld.Manager = items.ID AND chld.ID::text not like '%'||items.ids||'%'

代码是在postgresql下编写的。根据您的数据库更改字符串的功能。