Entity Framework - запрос Linq с порядком и группой по
У меня есть Measurement
Объекты с соответствующими свойствами CreationTime
(DateTime) и Reference
(String) и некоторые другие значения.
Я хотел бы написать эффективный запрос linq к DbContext
, который
- группирует объекты
Measurement
по заданному Reference
- упорядочивает группы с помощью свойства
CreationTime
первого Measurement
из группы или среднего значения свойств 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
?
Я использую Entity Framework 4.4 с базой данных MySQL (MySql Connector/Net 6.6.4). Этот пример упрощен, я могу более подробно и/или привести пример, если это необходимо.
Спасибо!
Ответы
Ответ 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();
Ответ 2
Вы можете попытаться выполнить результат GroupBy и сначала принять в 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();
Ответ 4
Ваши требования повсюду, но это решение моего понимания:
Сгруппировать по свойству Reference:
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, используйте свойство Ticks структуры DateTime:
var refGroupsOrderedByAvgCreationTimeQuery = limitedQuery.OrderBy( lq => lq.RecentMeasurements.Average( g => g.CreationTime.Ticks ) );