T-SQL递归选择循环依赖

时间:2012-12-12 15:40:08

标签: sql-server-2008 tsql recursion

我的数据库中有自我依赖的实体(a),它们是从另一个实体(b)引用的,并且给定一个特定的(b)实体,我需要获得所需的所有(a)实体。这些是多对多的映射,所以我有一个单独的映射表。我认为使用CTE的递归选择是我最好的选择,但我遇到了一个问题:

This Fiddle说明了我的问题。如果某些用户引入了循环引用,则我的递归选择将会突然停止。我一直在绞尽脑汁试图找到解决这个问题的方法。应该注意的是,虽然我已经在小提琴中引入了外键,但外键实际上并没有被我正在使用的系统所尊重(与DBA长期争论) - 我引入它们以使数据流更清晰。

递归查询,适用于那些不想点击小提琴的人:

WITH recur(objID) AS (
    SELECT usesObjID
        FROM #otherObj
        WHERE otherObjID = 1
    UNION ALL
    SELECT slaveObjID
        FROM #objMap
            INNER JOIN recur
                on #objMap.masterObjID = recur.objID
)SELECT objID from recur

有什么想法吗?这个设计没有投入生产,所以我可以稍微改变一下架构,但我不想依赖于在插入时发现循环引用,除非可以通过T-SQL完成。

1 个答案:

答案 0 :(得分:8)

可以设置CTE的MAXRECURSION,这将阻止无限循环,但是你仍会得到奇怪的结果,因为查询将继续在循环中运行,直到达到最大递归为止。< / p>

挑战在于循环涉及多个步骤,因此您不能只检查子项的直接父级,以确定您是否处于循环中。

处理此问题的一种方法是向CTE添加一个额外的列...这个新列tree跟踪到目前为止已包含的所有ID,并在ID重复时停止。< / p>

WITH recur(objID, Tree) AS (
    SELECT 
        usesObjID, 
        CAST(',' + CAST(usesObjID AS VARCHAR) + ',' AS VARCHAR) AS Tree
    FROM otherObj
    WHERE otherObjID = 1
    UNION ALL
    SELECT 
        slaveObjID, 
        CAST(recur.Tree + CAST(slaveObjID AS VARCHAR) + ',' AS VARCHAR) AS Tree
    FROM objMap
        INNER JOIN recur
            ON objMap.masterObjID = recur.objID
    WHERE recur.Tree NOT LIKE '%,' + CAST(slaveObjID AS VARCHAR) + ',%'  
)SELECT objID from recur

Sql Fiddle Link