【问题标题】:Entity Framework - Linq query with order by and group by实体框架 - 具有 order by 和 group by 的 Linq 查询
【发布时间】:2013-03-22 13:44:17
【问题描述】:

我有 Measurement 具有相关属性的对象 CreationTime (DateTime) 和 Reference (String) 和一些其他值。

我想写一个高效的 linq 查询到 DbContext

  • 按给定的Reference 对我的Measurement 对象进行分组
  • 按组中第一个MeasurementCreationTime 属性或CreationTime 属性的平均值对组进行排序
  • 将查询限制为最近的numOfEntries

(在后面的步骤中,我计算了这些返回组的平均值,但我想这与我的问题无关。)

DbContext 本身有一个名为MeasurementsDbSet<Measurement> 持有所有Measurement 对象。

我想出了以下查询,它会生成一个正确排序的组列表,但中间缺少一些组。

var groupByReference = (from m in context.Measurements
                          orderby m.CreationTime
                          group m by new { m.Reference } into g
                          select g).Take(numOfEntries).ToList();

如何正确选择Measurements 的“最新”组?

我正在使用 Entity Framework 4.4 和 MySQL 数据库 (MySql Connector/Net 6.6.4)。这个例子是简化的,我可以更详细地说明和/或在必要时给出一个例子。

谢谢!

【问题讨论】:

  • 您是否考虑过从给定点计算时间跨度,然后确定这些时间跨度的平均值,然后根据给定点和平均值计算结果日期,而不是计算“CreationTime 的平均值”时间跨度?

标签: c# mysql linq entity-framework


【解决方案1】:

它是方法语法(我觉得它更容易阅读)但这可能会做到

更新后的评论

使用.FirstOrDefault() 而不是.First()

关于平均日期,您可能不得不暂时放弃该订单,因为我目前无法访问 IDE

var groupByReference = context.Measurements
                              .GroupBy(m => m.Reference)
                              .Select(g => new {Creation = g.FirstOrDefault().CreationTime, 
//                                              Avg = g.Average(m => m.CreationTime.Ticks),
                                                Items = g })
                              .OrderBy(x => x.Creation)
//                            .ThenBy(x => x.Avg)
                              .Take(numOfEntries)
                              .ToList();

【讨论】:

  • 感谢您的建议,但我得到了一个 NotSupportedException 试图确定 g.First() 因为 g 不是最终查询操作。尝试平均 CreationTime 属性失败,因为它不是 int
  • 我已经更新了我的答案,但在我使用 IDE 之前我无法解决平均日期问题
  • 感谢您的努力。与此同时,我尝试了这个,还添加了一个“Ticks”属性,它将 CreationTime 存储为很长,以便“.Average()”应该工作。现在我收到 MySQL 异常,显示“'where 子句'中的未知列'Distinct1.Reference'”(FirstOrDefault)或“'字段列表'中的未知列'GroupBy1.K2'”(平均)。 MySql 连接器错误是可能的原因吗? (我应该提一下,真正的查询要复杂一些,我不直接查询 DbSet,而是使用另一个查询作为源。)
【解决方案2】:

您可以尝试先将 GroupBy 和 Take 的结果转换为 Enumerable,然后处理其余部分(基于 NinjaNye 提供的解决方案

var groupByReference = (from m in context.Measurements
                              .GroupBy(m => m.Reference)
                              .Take(numOfEntries).AsEnumerable()
                               .Select(g => new {Creation = g.FirstOrDefault().CreationTime, 
                                             Avg = g.Average(m => m.CreationTime.Ticks),
                                                Items = g })
                              .OrderBy(x => x.Creation)
                              .ThenBy(x => x.Avg)
                              .ToList() select m);

您的 sql 查询看起来与此类似(取决于您的输入)

SELECT TOP (3) [t1].[Reference] AS [Key]
FROM (
    SELECT [t0].[Reference]
    FROM [Measurements] AS [t0]
    GROUP BY [t0].[Reference]
    ) AS [t1]
GO

-- Region Parameters
DECLARE @x1 NVarChar(1000) = 'Ref1'
-- EndRegion
SELECT [t0].[CreationTime], [t0].[Id], [t0].[Reference]
FROM [Measurements] AS [t0]
WHERE @x1 = [t0].[Reference]
GO

-- Region Parameters
DECLARE @x1 NVarChar(1000) = 'Ref2'
-- EndRegion
SELECT [t0].[CreationTime], [t0].[Id], [t0].[Reference]
FROM [Measurements] AS [t0]
WHERE @x1 = [t0].[Reference]

【讨论】:

    【解决方案3】:

    尝试将order by 移动到group by 之后:

    var groupByReference = (from m in context.Measurements
                            group m by new { m.Reference } into g
                            order by g.Avg(i => i.CreationTime)
                            select g).Take(numOfEntries).ToList();
    

    【讨论】:

    • 我试过了,但得到一个错误,DateTime无法转换为int。我还尝试使用 int 属性进行测试,并得到一个 MySqlException 说类似“'字段列表'中的未知列'GroupBy1.K2'”
    【解决方案4】:

    您的要求无处不在,但这是我对它们的理解的解决方案:

    按引用属性分组:

    var refGroupQuery = (from m in context.Measurements
                group m by m.Reference into refGroup
                select refGroup);
    

    现在你说你想通过“最近的 numOfEntries”来限制结果 - 我认为这意味着你想限制返回的测量值......在这种情况下:

    var limitedQuery = from g in refGroupQuery
                       select new
                       {
                           Reference = g.Key,
                           RecentMeasurements = g.OrderByDescending( p => p.CreationTime ).Take( numOfEntries )
                       }
    

    按第一次测量创建时间对组进行排序(注意您应该对测量进行排序;如果您想要最早的 CreationTime 值,请将“g.SomeProperty”替换为“g.CreationTime”):

    var refGroupsOrderedByFirstCreationTimeQuery = limitedQuery.OrderBy( lq => lq.RecentMeasurements.OrderBy( g => g.SomeProperty ).First().CreationTime );
    

    要按平均 CreationTime 对组进行排序,请使用 DateTime 结构的 Ticks 属性:

    var refGroupsOrderedByAvgCreationTimeQuery = limitedQuery.OrderBy( lq => lq.RecentMeasurements.Average( g => g.CreationTime.Ticks ) );
    

    【讨论】:

      猜你喜欢
      • 2017-02-06
      • 2011-06-28
      • 2019-01-21
      • 1970-01-01
      • 2021-11-17
      • 1970-01-01
      • 2012-12-31
      • 1970-01-01
      • 2017-06-14
      相关资源
      最近更新 更多