我的数据库中有自我依赖的实体(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完成。
答案 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