避免 SQL Server 中的 while 循环

Posted

技术标签:

【中文标题】避免 SQL Server 中的 while 循环【英文标题】:Avoiding while loop in SQL Server 【发布时间】:2013-08-29 04:31:12 【问题描述】:

我有一个表,其中包含对象的标识符及其父对象的标识符。它用于保存以树的形式呈现的数据对象。我想查询表以构建树中对象的完整路径。树的最大深度可能永远不会超过 20 个对象。有没有办法在没有while循环的情况下做到这一点......或者是否值得尝试避免这种类型/大小的工作的while循环。

表格如下所示:

创建表 [dbo].[tblObj] ( [ObjectId] [int] 非空, [对象名] [nvarchar](50) 非空, [ParentId] [int] NULL, )

这样的数据

插入 tblObj (ObjectId, ObjectName) 值 (1, 'Root') 插入 tblObj (ObjectId, ObjectName, ParentId) 值 (2, 'Middle1', 1) 插入 tblObj (ObjectId, ObjectName, ParentId) 值 (3, 'Middle2', 2) 插入 tblObj (ObjectId, ObjectName, ParentId) 值 (4, 'Leaf', 3)

期望的结果是使用对象/父关系来构建一个包含反映完整路径的对象名称的字符串。上面的数据会导致路径“Root\Middle1\Middle2\Leaf”

【问题讨论】:

预期结果示例?无论如何,您可能需要递归 CTE。 【参考方案1】:

递归 CTE 非常常用于此类任务。下面的查询将给出dbo.tblObj 中所有行的 (ObjectId, Path) 对列表:

;WITH cte (ObjectId, ParentID, [ObjectName], Path)
AS(
    SELECT [ObjectId], [ParentID], [ObjectName], cast([ObjectName] as nvarchar(max))
    FROM dbo.tblObj
    WHERE [ParentID] is NULL
    UNION ALL
    SELECT t.[ObjectId], t.ParentID, t.[ObjectName], cte.Path + '\' + t.[ObjectName]
    FROM cte
        JOIN dbo.tblObj t ON t.ParentID = cte.[ObjectId]
)
select ObjectID, Path
from cte

如果您需要获取特定对象的路径,您可以这样做:

declare @objId int
set @objId = 4

;WITH cte (ObjectId, ParentID, [ObjectName], Path)
AS(
    -- here code is the same as above
)
select Path
from cte
where ObjectID = @objId

或者,或者,作为:

declare @objId int
set @objId = 4

;with PathSteps(ObjectId, ParentID, ObjectName, Level)
as
(
    select ObjectId, ParentID, ObjectName, 0
    from dbo.tblObj
    where ID = @id
    union all
    select t.ObjectId, t.ParentID, t.ObjectName, p.Level - 1
    from dbo.tblObj t
        join PathSteps p on p.ParentID = t.ID
),
Path(ObjectPath) as (
    select stuff(
        (select '\' + ObjectName
        from PathSteps
        order by Level
        for xml path(''), type).value('text()[1]', 'nvarchar(max)'), 1, 1, '')
)
select ObjectPath
from Path

【讨论】:

就是这样 - 谢谢。并感谢提供找到特定对象路径的第二部分。这正是我必须做的。 @Ken,我不确定 第二部分 的性能(可能是它对单个对象做的太多),所以我更新了答案并添加了替代方案方法。如果您遇到性能问题,您可以尝试其他方法。【参考方案2】:

实现结果的一种简单方法是使用:

DECLARE @var VARCHAR(MAX) = ''
SELECT @var = CASE WHEN @var = '' THEN '' ELSE @var+'/' END + ObjectName 
FROM tblObj ORDER BY ObjectId
PRINT @var

【讨论】:

以上是关于避免 SQL Server 中的 while 循环的主要内容,如果未能解决你的问题,请参考以下文章

SQL Server 基于集合的 while 循环避免万圣节保护 (LAHP) 用于层次结构透视将某些结果减半

在需要计数器时避免 SQL 中的 while 循环

sql server中的while循环语句

如何删除 MS SQL Server 存储过程中的 While 循环? [关闭]

SQL Server中while循环中的事务

SQL Server 2008 中的 While 循环遍历日期范围,然后插入