2013-03-22 16 views
10

Ich habe Measurement Objekte mit den entsprechenden Eigenschaften CreationTime (DateTime) und Reference (String) und einige andere Werte.Entity Framework - Linq Abfrage mit Reihenfolge von und Gruppe von

Ich möchte eine effiziente Linq-Abfrage, um eine meiner MeasurementDbContext dass

  • Gruppen Objekte schreiben von einem bestimmten Reference
  • Aufträge die Gruppen entweder durch die CreationTime Eigentum des ersten Measurement aus der Gruppe oder der Durchschnitt der CreationTime Eigenschaften
  • begrenzt die Abfrage auf die neuesten numOfEntries

(In einem späteren Schritt i berechnen Werte über diese zurück Gruppen gemittelt, aber ich denke, dass für mein Problem nicht relevant ist.)

Die DbContext selbst eine DbSet<Measurement> hat Measurements hält alle Measurement Objekte genannt.

Ich kam mit der folgenden Abfrage, die zu einer Liste von Gruppen führt, die korrekt sortiert ist, aber einige Gruppen dazwischen fehlt.

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

Wie wähle ich die „letzten“ Gruppen von Measurement richtig s?

Ich benutze Entity Framework 4.4 mit einer MySQL-Datenbank (MySql Connector/Net 6.6.4). Dieses Beispiel ist vereinfacht, ich kann mehr ins Detail gehen und/oder ein Beispiel geben, wenn es notwendig ist.

Danke!

+1

Statt der Berechnung der „Average der Creation Haben Sie darüber nachgedacht, die Zeitspanne von einem bestimmten Punkt aus zu berechnen und dann den Durchschnitt dieser Zeitspannen zu bestimmen und dann das resultierende Datum auf der Basis des gegebenen Punktes und der durchschnittlichen Zeitspanne zu berechnen? –

Antwort

0

Versuchen Sie, die order by nach group by bewegen:

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(); 
+0

Ich habe das versucht, aber einen Fehler bekommen, dass 'DateTime' nicht in' int' konvertiert werden kann. Ich versuchte auch mit einer 'int' Eigenschaft für Testzwecke und bekam eine MySqlException, die etwas wie" Unbekannte Spalte 'GroupBy1.K2' in 'Feldliste' " – zufallsfund

9

Es ist Methode Syntax (die ich einfacher zu lesen), aber dies könnte es

Aktualisiert Beitrag Kommentar

Verwenden .FirstOrDefault() stattdessen tun von .First()

Im Hinblick auf die durchschnittlichen Daten, können Sie, dass eine Bestellung für den Moment fallen lassen, wie ich im Moment zu einer IDE bekommen nicht in der Lage ist

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(); 
+1

sagt Vielen Dank für Ihren Vorschlag, aber ich habe eine' NotSupportedException' versucht zu bestimmen g.First(), weil 'g' keine abschließende Abfrageoperation ist. Der Versuch, über die 'CreationTime'-Eigenschaft zu mitteln, ist fehlgeschlagen, weil es kein' int' ist. – zufallsfund

+0

Ich habe meine Antwort aktualisiert, aber ich werde das Problem mit dem durchschnittlichen Datum nicht beheben können, bis ich zu einer IDE komme. – NinjaNye

+0

Vielen Dank für Ihre Mühe. Ich habe das in der Zwischenzeit versucht und auch eine "Ticks" -Eigenschaft hinzugefügt, die CreationTime als Long speichert, so dass ".Average()" funktionieren sollte. Jetzt bekomme ich MySQL Exceptions, die entweder "Unbekannte Spalte 'Distinct1.Reference' in 'Where-Klausel'" (FirstOrDefault) oder "Unbekannte Spalte 'GroupBy1.K2' in 'Feldliste'" (Durchschnitt) sagen. Ist ein MySql Connector Bug die mögliche Ursache dafür? (Ich sollte erwähnen, dass die echte Abfrage etwas komplexer ist, ich frage nicht direkt nach einem DbSet, sondern benutze eine andere Abfrage als Quelle.) – zufallsfund

0

Ihre Anforderungen sind ganz über den Platz, aber das ist die Lösung zu meinem Verständnis von ihnen:

zur Gruppe von Referenzobjekt:

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

Jetzt sagen Sie, Sie durch „jüngsten numOfEntries“ begrenzen Ergebnisse wollen - ich dies Sie die zurückgegebenen Messungen wollen bedeuten nehmen begrenzen ...in diesem Fall:

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

Um Gruppen von ersten Messerstellungszeit zu bestellen (beachten Sie sollten die Messungen bestellen, wenn Sie den frühesten Wert Creation, substitue „g.SomeProperty“ mit „g.CreationTime“ wollen):

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

um Gruppen von durchschnittlich Creation zu bestellen, die Ticks-Eigenschaft der Datetime-Struktur verwenden:

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

Sie können versuchen, das Ergebnis der GroupBy zu werfen und in ein Take n Enumerable zuerst verarbeiten dann den Rest (Gebäude an der Lösung von NinjaNye bereitgestellt

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

Ihre SQL-Abfrage wie folgt aussehen würde (je nach Eingabe), um diese

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] 
Verwandte Themen