2011-06-03 116 views
10

說具有以下CTE返回,我已經(從Hierarchical data in Linq - options and performance拍攝)一些樹數據(鄰接模型)的水平:模擬CTE遞歸在C#

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來提高性能。任何人都可以告訴我如何執行CTE用遞歸C#函數執行的相同工作,假設我有一個IQueryable,其中Tree是表示分層表中的條目的實體?一些沿線:

public void RecurseTree(IQueryable<Tree> tree, Guid userId, Guid parentId, int level) 
{ 
    ... 
    currentNode.level = x 
    ... 
    Recurse(tree... ,level + 1) 
} 

看到這很容易做到使用lambda表達式很酷。

回答

5

遞歸在SQL Server是comparsion窘況緩慢,但它確實工作。

我不得不說,T-SQL是比較有限的,但它從來就沒有打算做所有這些操作在首位。我不相信有什麼辦法可以做到這一點有一個IQueryable如果你inted反對你的SQL Server實例上運行這一點,但你可以在內存中的計算機上運行使用LINQ到對象在相對代碼緊湊的方式。

這裏有一個辦法做到這一點:

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)) 
     ); 
} 
+0

像一個魅力工作。所需時間從3秒縮短到<1。謝謝:) – woggles 2011-06-03 10:25:42

5

真正的遞歸lambdas(和推斷,Expression s)在技術上是可能的,但是pretty much insane。我還期望任何解析器(L2S,EF等),除了也許 LINQ到對象只是去瘋狂試圖拆散這一點。

簡而言之:你應該好好考慮這個爲Expression不支持的機制。

最後,需要注意的是執行它在C#只是因爲你正在編寫一個Expression並不意味着 - 事實上,很可能是相反的:如果你正在積極編寫一個Expression(而不是委託或程序代碼)我假設它將發送到解析器(除非您使用.AsQueryable()將它推送到LINQ到對象)。

+0

+1,在'遞歸拉姆達expressions'優秀的鏈接! – 2011-06-03 10:06:26