实体框架-带有排序和分组的Linq查询

本文关键字:Linq 查询 排序 框架 实体 | 更新日期: 2023-09-27 18:05:33

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

我想写一个有效的linq查询DbContext

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

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

DbContext本身有一个DbSet<Measurement>,叫做Measurements,用来保存所有的Measurement对象。

我提出了以下查询,结果是一个正确排序的组列表,但在中间缺少一些组。

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

如何正确选择Measurement s的"最近"组?

我使用实体框架4.4与MySQL数据库(MySQL连接器/Net 6.6.4)。这个例子是简化的,如果有必要,我可以更详细地说明和/或给出一个例子。

谢谢!

实体框架-带有排序和分组的Linq查询

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

更新后的评论

.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();

您可以尝试先将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]

尝试将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();

您的要求到处都是,但这是我对它们的理解的解决方案:

按引用属性分组:

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.s CreationTime"):

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

按平均创建时间排序组,使用DateTime结构体的Ticks属性:

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