2016-04-15 10 views
1

Ich verwende EF, Linq, TSQL. Ich habe zwei Tabellen:Verbinden Sie 2 Tabellen nach dem ungefähren Zeitstempel

Positions: 
Id [int] 
Location [geography, nullable] 
TimestampLocal [datetimeoffset(7)] 
VehicleId [int] 

Rawdata: 
Id [int] 
TimestampLocal [datetimeoffset(7)] 
Data1 [string] 
VehicleId [int] 

Ich brauche die Informationen aus den beiden Tabellen durch Zeitstempel für eine bestimmte VehicleID, mit Zeitstempel zu verbinden, die nahe sind, aber nicht identisch. Ich verwende MoreLinq, um diese Daten zu erhalten. Momentan nehme ich die Positionen in einer Liste, dann die Rawdata in einer anderen Liste, iteriere dann über sie, um den Speicherort eines Rawdata-Beispiels zu erhalten, und tue etwas mit diesen Informationen (berechne einen engstenPos.Location.Intersects (Polygon). ..).

var posList = entities.Positions.Where(z => (z.VehicleId == SearchedVehicleId && z.Location != null) 
&& z.TimestampLocal.Day == SearchedDay && z.TimestampLocal.Month == SearchedMonth && z.TimestampLocal.Year == SearchedYear).AsEnumerable().OrderBy(k => k.TimestampLocal); 

    var rawdatalist=entities.Rawdata.Where(k => (k.VehicleId == SearchedVehicleId) 
&& k.TimestampLocal.Day == SearchedDay && k.TimestampLocal.Month == SearchedMonth && k.TimestampLocal.Year == SearchedYear).OrderBy(k => k.TimestampLocal).ToList(); 


    foreach (Rawdata r in rawdatalist){ 
    var closestPos = posList.MinBy(t => Math.Abs((t.TimestampLocal- r.TimestampLocal).Ticks)); 
    //do something with the location 
    ComputeRawdataforLocation(closestPos, r); 
    } 

Die Daten aus der Datenbank (auch mit AsEnumerable(), ToList()) ist schnell. Das Problem ist, dass es ungefähr 10k Positionen und 100k Rawdata-Werte gibt.

Wie kann ich die Dinge beschleunigen? Ein anderer Weg in Linq? Vielleicht eine TSQL-Prozedur, die ich nennen könnte, die den Werten beitreten würde? Ich habe jedoch keine Ahnung, wie man das in TSQL macht.

Antwort

0

Die Daten aus der Datenbank (auch mit AsEnumerable(), ToList()) ist schnell.

Ok, dann müssen Sie die langsame Suche optimieren (MinBy ist O (N) Zeit Komplexität).

Vergessen Sie LINQ. Sie können die Tatsache nutzen, dass Ihre posList sortiert ist, was bedeutet, dass die lineare Suche durch viel schnellere O (log (N)) binäre Suche ersetzt werden kann.

Die Methoden BCL BinarySearch können für ein solches Szenario nicht verwendet werden. Aber es ist nicht schwer, die Methode von Grund auf neu zu schreiben, wie folgt aus:

static Positions FindClosest(List<Positions> posList, DateTimeOffset value) 
{ 
    int lo = 0, hi = posList.Count - 1; 
    while (lo <= hi) 
    { 
     int pos = lo + (hi - lo)/2; 
     var item = posList[pos]; 
     int compare = value.CompareTo(item.TimestampLocal); 
     if (compare < 0) hi = pos - 1; 
     else if (compare > 0) lo = pos + 1; 
     else return item; 
    } 
    var next = lo < posList.Count ? posList[lo] : null; 
    var prev = lo > 0 ? posList[lo - 1] : null; 
    if (next == null) return prev; 
    if (prev == null) return next; 
    return (value - prev.TimestampLocal).Ticks <= (next.TimestampLocal - value).Ticks ? prev : next; 
} 

und es dann statt MinBy verwenden:

foreach (Rawdata r in rawdatalist) 
{ 
    var closestPos = FindClosest(posList, r.TimestampLocal); 
    //do something with the location 
    ComputeRawdataforLocation(closestPos, r); 
} 
+0

Thank you! Ich musste nur eine .ToList() am Ende von posList anwenden, damit es funktioniert. Dies reduzierte die Zeit im Durchschnitt um 10%. Ich schaute jedoch in meinen Code und bemerkte, dass ich einige zusätzliche Abfragen hatte, die noch einige Daten auf dem SQL-Server behielten (ich habe kein AsEnumerable für sie angewendet), also mischte ich lokale IIS-Daten (mit AsEnumerable) mit DB-Abfragedaten (prob. gehalten auf dem DB-Server). Dies beließ die CPU bei 5%. Nach dem Ziehen aller Daten auf dem IIS (mit AsEnumberable) ging die CPU auf 40% und die Rechenzeit nahm drastisch ab, noch mehr mit Ihrem Code. – user3546827

+0

Ich habe übersehen, dass 'posList' keine Liste ist. Aber ich würde vorschlagen, dass Sie 'ToList' am Ende der' var posList = entities .... ToList(); 'aufrufen und dann wie in der Antwort verwenden (nicht' ToList' Aufrufe innerhalb der inneren Schleife)). Es sollte wirklich viel mehr als 10% geben - wenn Sie 10K-Positionen haben, wird die binäre Suche ~ 13 Vergleiche verwenden, während das Original ungefähr tun wird. 5-10K Vergleiche, so sollte die neue Methode mal schneller sein. –

Verwandte Themen