You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用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不匹配的小问题,调整后就能正常工作:

  1. 修正关联字段:原SQL是用d.deckguid = de.deckguid做关联,但你的代码里写了d.DeckSourceId equals de.DeckGuid——而DeckSource实体里根本没有DeckSourceId字段,应该改成ds.DeckGuid equals d.DeckGuid。
  2. 修正分组依据:原SQL是按d.deckguid分组,你代码里写的d.DeckSourceId同样是不存在的字段,要改成按ds.DeckGuid分组。
  3. 简化计数逻辑:左连接后匹配不到的项会是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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.09 06:27:47