这个递归 CTE 有啥问题,更重要的是,我无法理解啥一般概念?

Posted

技术标签:

【中文标题】这个递归 CTE 有啥问题,更重要的是,我无法理解啥一般概念?【英文标题】:What's wrong with this recursive CTE and more importantly, what general concept am I failing to comprehend?这个递归 CTE 有什么问题,更重要的是,我无法理解什么一般概念? 【发布时间】:2015-09-04 20:05:16 【问题描述】:

好的,我正在研究将一组记录转换为分隔列表的问题。这是一个老问题,有几种方法,在很多情况下,我什至不应该在数据库中这样做。但是,在 this 的情况下,我想并且我想使用递归 CTE。

正如我的标题所暗示的那样,我对真正掌握这个概念的困难感到沮丧。过去,我用书籍或互联网帖子中的代码 sn-ps 解决了这个问题,并对其进行了改编并让它们工作。但这总是很难,而且我并没有真正掌握这项技术。我寻找了该技术的一个非常基本的实现,并得到了我用作模型的答案:https://***.com/a/9726839/13748

所以现在,我有这个临时表#POSO,内容如下:

id  inspectionLogKey PO            SO
--- ---------------- ------------- ---------
1   7                374534-6988   SO37047
2   7                374534-5464   SO34110
3   7                374534-7135   SO37377
4   7                374534-5284   SO33863
5   7                374534-6710   SO36506
6   7                374534-5084   SO33565

根据这些数据,我想生成一个以逗号分隔的 PO 列值列表。我正在尝试用这个(以及一堆令人沮丧的排列)来做到这一点:

WITH POlists(id,POs) AS
(
    SELECT p1.id, CONVERT(VARCHAR(MAX),p1.PO) as POs --anchor
    FROM #POSO p1
    WHERE p1.id = 1

    UNION ALL

    SELECT p2.id, POs + ',' + p2.PO --recursive
    FROM #POSO p2
        join POlists ON p2.id + 1 = POlists.id
)
SELECT * FROM POlists;

返回这个:

id  POs
--- -----------
1   374534-6988

我知道你们中的一个人将能够在接下来的八分钟内指出代码错误。但我希望有人可以重新定义它正在做什么,这样我就可以真正理解它并将它从这次遭遇中带走,作为未来使用的工具。

也许一旦发生这种情况,我就可以自己回答这个问题了,但是这个查询能否同时处理 PO 和 SO 列,产生两个字段,每个字段都是逗号分隔的列表两个各自的列?

感谢您的宝贵时间!

【问题讨论】:

您说您正在尝试创建以逗号分隔的值列表。与使用递归 cte 相比,您可以更容易地做到这一点。您可以使用 FOR XML 来更轻松、更快速地完成此操作。 sqlservercentral.com/articles/comma+separated+list/71700 @SeanLange - OP 似乎知道不同的方法,但声明“在这种情况下,我想要,并且我想要使用递归 CTE。” 但是递归 cte 是一种效率低下的工具。仅仅因为它可以这样做并不意味着它应该这样做。 没错,我过去也为此使用过 FOR XML PATH。 @SeanLange 当您说“更快”时,您是指更快地编写代码还是更快地执行代码? 写起来肯定更快,根据我的经验,执行起来也更快。当然要看深度。 :) 【参考方案1】:

p2.id + 1 = POlists.id 应该是 p2.id - 1 = POlists.id 唯一的错误是代数。

您的第一个 id 是 1。没有行与 p2.id + 1 =1 一样,因为您没有 id 为 0 的行,所以它停在那里。

虽然p2.id = POlists.id + 1 可能会允许在递归部分进行索引查找。

您可能希望在其中设置一个级别列来返回完整的字符串。

WITH POlists(id,POs, Lvl) AS
(
    SELECT p1.id, CONVERT(VARCHAR(MAX),p1.PO) as POs, 0 AS Lvl
    FROM #POSO p1
    WHERE p1.id = 1

    UNION ALL

    SELECT p2.id, POs + ',' + p2.PO, Lvl + 1
    FROM #POSO p2
        join POlists ON p2.id = POlists.id + 1
)
SELECT TOP 1 * 
FROM POlists
ORDER BY Lvl DESC;

【讨论】:

哇!我只是对此视而不见。那么,关于级别,为什么我不能只取与最高 ID 关联的结果呢?就像SELECT TOP 1 POs FROM POlists ORDER BY id DESC 我的意思是,它现在可以工作,但是还有其他一些我没有想到的情况吗?我认为不是因为我在执行此查询之前在该临时表中创建了 ID。 @clweeks - 好点 - 其余代码依赖于 id 无论如何都是连续的,所以你不妨使用它。从技术上讲,标识列不能保证顺序,但对于临时表,如果您在单个语句中插入,我从未见过差距。

以上是关于这个递归 CTE 有啥问题,更重要的是,我无法理解啥一般概念?的主要内容,如果未能解决你的问题,请参考以下文章

递归 CTE 如何逐行运行?

递归 CTE 存在性能问题,需要建议以优化查询

防止递归 CTE 多次访问节点

表数据子集上的递归 CTE

如何正确应用递归 CTE?

将 SQL Server 中的递归 CTE 转换为 netezza