在 C# 中模拟 CTE 递归

Posted

技术标签:

【中文标题】在 C# 中模拟 CTE 递归【英文标题】:Simulating CTE recursion in C# 【发布时间】:2011-09-07 16:22:15 【问题描述】:

假设具有以下 CTE,它返回我拥有的某些树数据(邻接模型)的级别(取自 Hierarchical data in Linq - options and performance):

WITH hierarchy_cte(id, parent_id, data, lvl) AS
(
    SELECT id, parent_id, data, 0 AS lvl
    FROM dbo.hierarchical_table
    WHERE (parent_id IS NULL)

    UNION ALL

    SELECT t1.id, t1.parent_id, t1.data, h.lvl + 1 AS lvl
    FROM dbo.hierarchical_table AS t1 
    INNER JOIN hierarchy_cte AS h ON t1.parent_id = h.id
)
SELECT id, parent_id, data, lvl
FROM hierarchy_cte AS result

我想知道通过在 C# 中而不是 SQL 中进行递归是否会提高性能。假设我有一个 IQueryable 其中 Tree 是表示层次表中条目的实体,谁能告诉我如何执行 CTE 使用递归 C# 函数所做的相同工作?大致如下:

public void RecurseTree(IQueryable<Tree> tree, Guid userId, Guid parentId, int level)

    ...
    currentNode.level = x
    ...
    Recurse(tree... ,level + 1)

看到使用 lambda 表达式很容易做到这一点会很酷。

【问题讨论】:

【参考方案1】:

相比之下,SQL Server 中的递归非常慢,但它确实有效。

我不得不说 T-SQL 有一定的局限性,但它从一开始就没有打算完成所有这些操作。如果您打算对 SQL Server 实例运行它,我不相信有任何方法可以通过 IQueryable 实现这一点,但是您可以在运行代码的机器上的内存中使用 LINQ-to-Objects 相对紧凑的方式。

这是一种方法:

class TreeNode

    public int Id;
    public int? ParentId;


static void Main(string[] args)

    var list = new List<TreeNode>
        new TreeNode Id = 1 ,
            new TreeNode Id = 4, ParentId = 1 ,
            new TreeNode Id = 5, ParentId = 1 ,
            new TreeNode Id = 6, ParentId = 1 ,
        new TreeNode Id = 2 ,
            new TreeNode Id = 7, ParentId= 2 ,
                new TreeNode Id = 8, ParentId= 7 ,
        new TreeNode Id = 3 ,
    ;

    foreach (var item in Level(list, null, 0))
    
        Console.WriteLine("Id=0, Level=1", item.Key, item.Value);
    


private static IEnumerable<KeyValuePair<int,int>> Level(List<TreeNode> list, int? parentId, int lvl)

    return list
        .Where(x => x.ParentId == parentId)
        .SelectMany(x => 
            new[]  new KeyValuePair<int, int>(x.Id, lvl) .Concat(Level(list, x.Id, lvl + 1))
        );

【讨论】:

就像一个魅力。所用时间从 3 秒减少到 【参考方案2】:

真正的递归 lambdas(通过推断,Expressions)在技术上是可行的,但pretty much insane。我还希望任何解析器(L2S、EF 等)除了 可能 LINQ-to-Objects 都会疯狂地试图解开它。

简而言之:您最好将其视为Expression 不受支持的机制。

最后,请注意,仅仅因为您正在编写 Expression 并不意味着您正在使用 C# 执行它 - 事实上,很可能恰恰相反:如果您正在积极编写 Expression(而不是委托或程序代码)我假设它会发送到解析器(除非您使用 .AsQueryable() 将其推送到 LINQ-to-Objects)。

【讨论】:

以上是关于在 C# 中模拟 CTE 递归的主要内容,如果未能解决你的问题,请参考以下文章

在CTE中使用最大递归来生成CTE,而不是仅在最终的SELECT语句中生成CTE

递归 CTE 通过多个级别更新父记录

SQL Server CTE 递归查询全解

sql递归cte在视图中不能正常工作

表数据子集上的递归 CTE

sql server使用cte递归查询获取树形的父节点/子节点