在 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(通过推断,Expression
s)在技术上是可行的,但pretty much insane。我还希望任何解析器(L2S、EF 等)除了 可能 LINQ-to-Objects 都会疯狂地试图解开它。
简而言之:您最好将其视为Expression
不受支持的机制。
最后,请注意,仅仅因为您正在编写 Expression
并不意味着您正在使用 C# 执行它 - 事实上,很可能恰恰相反:如果您正在积极编写 Expression
(而不是委托或程序代码)我假设它会发送到解析器(除非您使用 .AsQueryable()
将其推送到 LINQ-to-Objects)。
【讨论】:
以上是关于在 C# 中模拟 CTE 递归的主要内容,如果未能解决你的问题,请参考以下文章