【问题标题】:How to get item value and item count in linq c#如何在linq c#中获取项目值和项目数
【发布时间】:2021-08-19 12:43:26
【问题描述】:

我有一个名为 hat 的 sql 数据库表, 我想通过 linq 查询获取每个项目的名称及其计数 那是我的代码:

var qLocation = (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() })
.OrderByDescending(o => o.count).ToList();

var l = qLocation[0].HatoLocation;
var c = qLocation[0].count;

它给了我项目名称;但显示任何项目计数的 0 结果 请告诉我我的代码哪里出了问题?

更新

在反馈后我捕获了以下输出,有趣的是它只是集合中的最后一条记录的计数为零:

【问题讨论】:

标签: c# linq


【解决方案1】:

您的代码看起来不错,我发现查询本身没有语法问题,您需要一些技巧来帮助您调试。

当您使用 In-Memory 记录集运行它时,它的行为与预期一样,这意味着问题出在生成的 SQL 中,您的 Linq 查询被翻译成通过 DbContext。

作为 In-Memory 的证明,请查看此小提琴:https://dotnetfiddle.net/Widget/jxKNG5

虽然对于生产代码来说这不是一个好的做法,但一种解决方法,并证明这个问题是一个 SQL 问题,是在执行 group by 之前将数据读入内存。 IQueryable&lt;T&gt; 表达式的结果可以使用 .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()。零项表示组中没有记录,这通常会阻止组头返回,实际上我尝试了很多不同的角度,真的无法弄清楚。

手动调试这样的查询有两个复杂的因素:

  1. Linq / C# group by 与 SQL GROUP BY 大不相同。在 C# 中,分组只是将结果拆分为子数组,所有记录仍在输出中,但在 SQL 中,GROUP BY 不会返回所有记录,它只返回聚合组结果。要正确执行此操作,分组应在 SQL 中实现为嵌套查询,它不一定总是涉及 SQL GROUP BY

    • 无论哪种方式,生成的 SQL 不会像这样简单:

      SELECT HatoLocation, COUNT(*)
      FROM Hato
      WHERE HatoRecDate >= '2021-05-21' AND HatoRecDate <= '2021-05-24'
      GROUP BY HatoLoction
      
  2. 您正在按过滤器内的聚合结果进行排序。这并不总是一个交易,但如果您也使用像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 与生成的 enumeratedIn-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 查询,但它显示您的代码还有其他问题。我们要么需要查看更多数据、更多架构,要么需要查看没有分组的数据副本(如小提琴所示),以便我们提供更多帮助。

【讨论】:

  • 谢谢亲爱的 Chris Schaller。问题已在您的帮助下得到解决。再次感谢您
  • 如果有帮助,请将其标记为答案。不过我仍然很好奇,你是如何具体解决的,我很想看看 SQL,我只是看不到你是如何得到零的计数,除非它是一个空值比较问题。
【解决方案2】:

试试这个... 执行 .ToList() ,然后执行分组。

【讨论】:

  • 'var qryLst = (从 L in db.Hato where L.HatoRecDate >= startDate && L.HatoRecDate o.count).ToList();字符串 l = qryCnt[0].location; int c = qryCnt[0].count;'
  • 我这样做了,但结果没有改变
猜你喜欢
  • 1970-01-01
  • 2016-08-03
  • 2019-10-13
  • 1970-01-01
  • 2020-05-14
  • 2018-06-13
  • 2013-09-24
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多