T-SQL递归选择循环依赖

Fra*_*Man 5 t-sql recursion sql-server-2008

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

这个小提琴说明了我的问题.如果某些用户引入了循环引用,则我的递归选择将会突然停止.我一直在绞尽脑汁试图找到解决这个问题的方法.应该注意的是,虽然我已经在小提琴中引入了外键,但外键实际上并没有被我正在使用的系统所尊重(与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
Run Code Online (Sandbox Code Playgroud)

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

Mic*_*son 8

可以设置MAXRECURSIONCTE,这将阻止无限循环,但你仍然会得到奇怪的结果,因为查询将继续在循环中运行,直到命中最大递归.

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

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

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
Run Code Online (Sandbox Code Playgroud)

Sql小提琴链接