如何用LINQ实现带计数的左连接查询?附代码示例
将SQL左连接分组查询转为LINQ实现
现有实体类定义
public class DeckOccurrences { public string DeckGuid { get; set; } public int Count { get; set; } } public class Deck { public string DeckGuid { get; set; } public string Name { get; set; } } public class DeckSource { public string DeckGuid { get; set; } public string Name { get; set; } }
数据库查询上下文对象
var decks = App.EF.Db2.Deck.ToList(); var decksources = App.EF.Db2.DeckSource.ToList();
原SQL查询语句
select d.deckguid, count(de.deckguid) as Count from decksource d left join deck de on d.deckguid = de.deckguid group by d.deckguid
我尝试的LINQ代码
List<DeckOccurrences> res = (from d in App.EF.Db2.DeckSource join de in App.EF.Db2.Deck on d.DeckSourceId equals de.DeckGuid into j1 from j2 in j1.DefaultIfEmpty() group j2 by d.DeckSourceId into grouped select new DeckOccurrences() { DeckGuid = grouped.Key, Count = grouped.Count(t => t.DeckGuid != null) }).ToList();
实现思路与修正方案
你其实已经找对了核心方向:LINQ的group join(也就是查询语法里的into写法)就是对应SQL左连接分组的正确方式,只是代码里有几个和实体类、原SQL不匹配的小问题,调整后就能正常工作:
- 修正关联字段:原SQL是用
d.deckguid = de.deckguid做关联,但你的代码里写了d.DeckSourceId equals de.DeckGuid——而DeckSource实体里根本没有DeckSourceId字段,应该改成ds.DeckGuid equals d.DeckGuid。 - 修正分组依据:原SQL是按
d.deckguid分组,你代码里写的d.DeckSourceId同样是不存在的字段,要改成按ds.DeckGuid分组。 - 简化计数逻辑:左连接后匹配不到的项会是
null,原SQL的count(de.deckguid)是统计非空的匹配项,你的计数逻辑是对的,但可以简化成grouped.Count(deck => deck != null),因为只要deck不为null,它的DeckGuid必然有值。
修正后的查询语法版LINQ
List<DeckOccurrences> res = (from ds in App.EF.Db2.DeckSource join d in App.EF.Db2.Deck on ds.DeckGuid equals d.DeckGuid into deckGroup from matchedDeck in deckGroup.DefaultIfEmpty() group matchedDeck by ds.DeckGuid into groupedDecks select new DeckOccurrences { DeckGuid = groupedDecks.Key, Count = groupedDecks.Count(deck => deck != null) }).ToList();
方法链写法的LINQ(可选)
如果你更喜欢链式调用的风格,也可以这样写:
var res = App.EF.Db2.DeckSource .GroupJoin( App.EF.Db2.Deck, ds => ds.DeckGuid, d => d.DeckGuid, (ds, deckGroup) => new { ds.DeckGuid, Decks = deckGroup.DefaultIfEmpty() } ) .SelectMany(x => x.Decks.Select(deck => new { x.DeckGuid, Deck = deck })) .GroupBy(x => x.DeckGuid) .Select(g => new DeckOccurrences { DeckGuid = g.Key, Count = g.Count(x => x.Deck != null) }) .ToList();
核心步骤拆解
- 左连接分组:用
GroupJoin(或查询语法的into)实现SQL的LEFT JOIN,把每个DeckSource对应的所有匹配Deck打包成组。 - 保留空匹配项:通过
DefaultIfEmpty()确保即使某个DeckSource没有对应的Deck,也会保留该DeckSource的记录。 - 分组统计:按照
DeckSource的DeckGuid分组,统计每组中非空的Deck数量,完美对应原SQL的count(de.deckguid)逻辑。
内容的提问来源于stack exchange,提问作者Alan2
相关产品推荐
相关产品推荐

