【问题标题】:Converting an SQL with Left Join , Group and Sum to a Linq Query将带有 Left Join、Group 和 Sum 的 SQL 转换为 Linq 查询
【发布时间】:2024-05-01 10:35:01
【问题描述】:

我正在尝试在 C# 和 Linq 中复制以下 SQL,但我对 Linq 的基础知识并没有太多经验。

SELECT SUM(coalesce(cd.minutesspent,0))
FROM timelabels t
LEFT JOIN challengedetails cd on cd.createddate = t.date 
GROUP BY t.date 

这会给我每个日期的分钟总和,如果有空条目,则为零。我认为非常简单。

// timelabels structure 
DateTime start = DateTime.Now.AddDays(-14);
DateTime end = DateTime.Now;
List<DateTime> timelabels =  new List<DateTime>();
for (var dt = start; dt <= end; dt = dt.AddDays(1))
{
    timelabels.Add(dt);
}

// ChallengeDetails structure & sample data
class ChallengeDetails(){
    DateTime? Createddate
    int? MinutesSpent 
}
List<ChallengeDetails> ChallengeDetails = new List<ChallengeDetails >();
  List<ChallengeDetails> ChallengeDetails = new List<ChallengeDetails>();
            ChallengeDetails.Add(new ChallengeDetails { MinutesSpent = 44, Createddate = DateTime.Now.AddDays(-10) });
            ChallengeDetails.Add(new ChallengeDetails { MinutesSpent = 31, Createddate = DateTime.Now.AddDays(-7) });
            ChallengeDetails.Add(new ChallengeDetails { MinutesSpent = 13, Createddate = DateTime.Now.AddDays(-3) });
            ChallengeDetails.Add(new ChallengeDetails { MinutesSpent = 77, Createddate = DateTime.Now.AddDays(-2) });


// The Actual Code 
List<string> timedata = (from a in timelabels
                         join al in ChallengeDetails on a equals al.Createddate into All
                         from al in All.DefaultIfEmpty()
                         group al by a into grouped
                         select grouped.Sum(m => m.MinutesSpent ?? 0).ToString()
                        ).ToList();

看看它应该可以工作 - 我有 DefaultIfEmpty 应该复制左连接。在总和内我有'?? 0' 这应该是没有 ChallengeDetail 的任何日期的后备。

但我得到 System.NullReferenceException: 'Object reference not set to an instance of an object。' m 为空。我不应该得到这个,因为我覆盖了空条目,或者我把这一切都弄错了?

【问题讨论】:

  • 首先,使用您正在使用的数据层执行此操作(哪个?)。 LINQ to object 不同于 LINQ to SQL 提供程序。第二件事可能适用:使用导航属性,而不是join。这样做 ORM 将 1. 取消空引用,因为它是 SQL 和 2. 自己找出左连接。
  • 尝试以下操作:选择 grouped.Sum(m => (m == null) ? 0 :m.MinutesSpent).ToString()
  • 也许我的SQL to LINQ Recipe 可以帮助你。您使用的是哪个 LINQ:LINQ to SQL / EF 6.x / EF Core 2.0 / 2.1 / 3.x / 5.x / 6.x?哪个数据库提供商?
  • 在 LINQ to Objects(来自 DefaultIfEmpty())中,有时 al 将是 null,在这种情况下,m 将是 null,而 m.MinutesSpent 将是 NullReferenceException。对于 LINQ to Objects,您需要 m?.MinutesSpent ?? 0。这与 LINQ to a database 不同。
  • @NetMage 你对 m 为空是正确的 - ` m?.MinutesSpent ?? 0`照顾它。您的 SQL to LINQ 配方正是我正在寻找的!

标签: c# linq


【解决方案1】:

在“实际代码”部分下,只需在 Sum 函数中添加一个括号,如下所示 它应该处理 null ,甚至在 Sum 工作之前。

select grouped.Sum(m => (m.MinutesSpent ?? 0)).ToString()

【讨论】:

    【解决方案2】:

    这会为你做的,使用 LINQ - Lambda 表达式:

    var TotalMinutesSpentGroupedList = timelabels
             .GroupJoin(challengedetails,
                        t => date,
                        cd => createddate,
                        (t,cd) => new 
                        { 
                           MinutesSpent = cd.minutesspent ?? 0,
                           CreateDate = cd.createddate ?? DateTime.Now
                        })
             .SelectMany(tcd=> challengedetails.DefaultIfEmpty(),
                         (t, cd) => new 
                         { 
                            MinutesSpent = cd.minutesspent ?? 0,
                            CreateDate = cd.createddate ?? DateTime.Now
                         })
             .Select(s => new 
                         { 
                            MinutesSpent = cd.minutesspent ?? 0,
                            CreateDate = cd.createddate ?? DateTime.Now
                         })
             .GroupBy(g => g.createddate).Select(item => item.Sum(s => s.minutesspent));
    

    【讨论】:

      最近更新 更多