J'ai des objets Measurement
avec les propriétés pertinentes CreationTime
(DateTime) et Reference
(String), ainsi que d'autres valeurs.
Je voudrais écrire une requête linq efficace à un DbContext
qui
Measurement
par une Reference
donnéeCreationTime
de la première Measurement
du groupe ou de la moyenne des propriétés CreationTime
numOfEntries
(Dans une étape ultérieure, je calcule les valeurs moyennes sur ces groupes, mais je suppose que cela n’est pas pertinent pour mon problème.)
Le DbContext lui-même a un DbSet<Measurement>
appelé Measurements
holding tous les objets Measurement
.
Je suis venu avec la requête suivante, qui résulte en une liste de groupes qui est ordonnée correctement mais il manque certains groupes entre les deux.
var groupByReference = (from m in context.Measurements
orderby m.CreationTime
group m by new { m.Reference } into g
select g).Take(numOfEntries).ToList();
Comment sélectionner les groupes "les plus récents" de Measurement
s correctement?
J'utilise Entity Framework 4.4 avec une base de données MySQL (MySql Connector/Net 6.6.4). Cet exemple est simplifié, je peux entrer plus en détail et/ou donner un exemple si nécessaire.
Merci!
C'est la syntaxe de la méthode (que je trouve plus facile à lire) mais cela pourrait le faire
Mise à jour du commentaire
Utilisez .FirstOrDefault()
au lieu de .First()
En ce qui concerne la moyenne des dates, vous devrez peut-être abandonner cette commande pour le moment car je ne peux pas me rendre à un IDE pour le moment
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();
Vous pouvez essayer de convertir le résultat de GroupBy et de Prendre dans un Enumerable, puis de traiter le reste
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);
Votre requête SQL serait similaire (en fonction de votre saisie) cette
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]
Essayez de déplacer le order by
après le 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();
Vos exigences sont partout, mais voici la solution pour les comprendre:
Pour grouper par propriété de référence:
var refGroupQuery = (from m in context.Measurements
group m by m.Reference into refGroup
select refGroup);
Maintenant, vous dites que vous voulez limiter les résultats par "les plus récents numéros". Je suppose que cela signifie que vous souhaitez limiter les mesures renvoyées ... dans ce cas:
var limitedQuery = from g in refGroupQuery
select new
{
Reference = g.Key,
RecentMeasurements = g.OrderByDescending( p => p.CreationTime ).Take( numOfEntries )
}
Pour classer les groupes par ordre chronologique au moment de la création de la mesure (notez que vous devez commander les mesures; si vous voulez la valeur CreationTime la plus ancienne, remplacez "g.SomeProperty" par "g.CreationTime"):
var refGroupsOrderedByFirstCreationTimeQuery = limitedQuery.OrderBy( lq => lq.RecentMeasurements.OrderBy( g => g.SomeProperty ).First().CreationTime );
Pour classer les groupes en fonction de CreationTime moyen, utilisez la propriété Ticks de la structure DateTime:
var refGroupsOrderedByAvgCreationTimeQuery = limitedQuery.OrderBy( lq => lq.RecentMeasurements.Average( g => g.CreationTime.Ticks ) );