您的代码看起来不错,我发现查询本身没有语法问题,您需要一些技巧来帮助您调试。
当您使用 In-Memory 记录集运行它时,它的行为与预期一样,这意味着问题出在生成的 SQL 中,您的 Linq 查询被翻译成通过 DbContext。
作为 In-Memory 的证明,请查看此小提琴:https://dotnetfiddle.net/Widget/jxKNG5
虽然对于生产代码来说这不是一个好的做法,但一种解决方法,并证明这个问题是一个 SQL 问题,是在执行 group by 之前将数据读入内存。 IQueryable<T> 表达式的结果可以使用 .ToList() 加载到内存中。
而不是在整个表上调用.ToList(),如果过滤条件没有问题,请在过滤条件之后调用.ToList()。如果您在调试会话后不小心将其留在代码中,则其影响将小于从数据库中读取每条记录的影响
#region A safer way to bring the recordset into memory for debugging
// Build the query in 2 steps, first create the filtered query
var filteredHatoQuery = from L in db.Hato
where L.HatoRecDate >= startDate && L.HatoRecDate <= endDate
select L;
// you could also consider only projecting the columns you need
// select new { L.HatoRecDate, L.HatoLocation };
// then operate on the data
var qLocation = (from L in filteredHatoQuery.ToList() // remove the .ToList() to query against the DB
group L by L.HatoLocation into g
select new { HatoLocation = g.Key, count = g.Count() })
.OrderByDescending(o => o.count).ToList();
#endregion A safer way to bring the recordset into memory for debugging
说实话,我很难重新创建一个查询,您可能会得到一个为零的Count()。零项表示组中没有记录,这通常会阻止组头返回,实际上我尝试了很多不同的角度,真的无法弄清楚。
手动调试这样的查询有两个复杂的因素:
-
Linq / C# group by 与 SQL GROUP BY 大不相同。在 C# 中,分组只是将结果拆分为子数组,所有记录仍在输出中,但在 SQL 中,GROUP BY 不会返回所有记录,它只返回聚合组结果。要正确执行此操作,分组应在 SQL 中实现为嵌套查询,它不一定总是涉及 SQL GROUP BY。
-
您正在按过滤器内的聚合结果进行排序。这并不总是一个大交易,但如果您不也使用像TOP这样的限制因素,它通常会导致SQL复杂化。作为一般建议,如果排序仅影响渲染输出,而不影响功能逻辑,那么您应该将排序过程留给渲染器。或者至少,排序 In-Memory,而不是 SQL。
原始查询的计算结果类似于以下 SQL:
(我已经替换了开始和结束参数@p_linq_0和@p_linq_1)
SELECT
[Project1].[C2] AS [C1],
[Project1].[HatoLocation] AS [HatoLocation],
[Project1].[C1] AS [C2]
FROM ( SELECT
[GroupBy1].[A1] AS [C1],
[GroupBy1].[K1] AS [HatoLocation],
1 AS [C2]
FROM ( SELECT
[Extent1].[HatoLocation] AS [K1],
COUNT(1) AS [A1]
FROM [dbo].[Hato] AS [Extent1]
WHERE ([Extent1].[HatoRecDate] >= '2021-05-21') AND ([Extent1].[HatoRecDate] <= '2021-05-24')
GROUP BY [Extent1].[HatoLocation]
) AS [GroupBy1]
) AS [Project1]
ORDER BY [Project1].[C1] DESC
但即便如此不会导致计数为零。我只能假设 OPs 运行时环境或数据库引入了在此探索中没有考虑到的其他一些因素。
在 Linq to Entities 中,您只需在查询上调用 .ToString() 或使用调试会话期间的检查器工具。这个帖子里有很好的讨论Get SQL query from LINQ to SQL?
出于调试目的,最好将 linq query 与生成的 enumerated 或 In-Memory 结果集分开,同样在此示例中,我们专门隔离了 sort 以在.ToList() 之后发生,并且 SQL 已写入调试输出。
var qLocationQuery = from L in db.Hato
where L.HatoRecDate >= startDate && L.HatoRecDate <= endDate
group L by L.HatoLocation into g
select new { HatoLocation = g.Key, count = g.Count() };
System.Diagnostics.Debug.WriteLine("Hato Query SQL:");
System.Diagnostics.Debug.WriteLine(qLocationQuery.ToString());
var qLocation = qLocationQuery.ToList();
// now perform the sort, this simulates leaving the sort to the rendering logic.
qLocation = qLocation.OrderByDescending(o => o.count).ToList();
请使用生成的 SQL 更新您的帖子,以便我们进一步探索!
更新
我已经用实际的 DbContext 实现更新了小提琴,但我仍然无法生成计数为零的分组。
https://dotnetfiddle.net/G4RvUV
这显示了如何提取 SQL 查询,但它显示您的代码还有其他问题。我们要么需要查看更多数据、更多架构,要么需要查看没有分组的数据副本(如小提琴所示),以便我们提供更多帮助。