2016-03-29 21 views
0

Ich habe eine Liste von Seriennummern und möchte die Zeilen aus einer Datentabelle entsprechend diesen Zahlen und gegen eine ID extrahieren. Ich verwende die folgenden LINQ-Abfrage für gleiche:So optimieren Sie die Linq-Manipulation, um Zeilen zu erhalten

//list of serial numbers 
var serialNumAlreadyExisted = [1,2,3]; 
var varID = 2; 

//get the corresponding rows for these serial numbers 
var duplicateRows = (from row in dt.AsEnumerable() 
    where row.Field<int>("ID") == varID && 
    serialNumAlreadyExisted.Any(sr => sr == row.Field<string>("SERIAL_NUMBER")) 
    select row).ToList(); 

Der obige Code funktioniert gut für 1-2K Reihen, aber viel Zeit in Anspruch nimmt, wenn es 50K Seriennummern und 50K Datensätze in der Datentabelle sind.

Gibt es eine Möglichkeit, es zu optimieren und die Verarbeitungszeit zu reduzieren?

+0

Wie wäre es 'serialNumAlreadyExisted.Contains (row.Field (" SERIAL_NUMBER "))'? –

+1

Sie könnten die beiden "beitreten", das ist wahrscheinlich das, was ich tun würde, aber ich bezweifle, dass die Leistungsverbesserung bei einem In-Memory-Set auffällig ist. In der Tat, ich bin ziemlich überrascht, dass Sie einen Unterschied mit allem In-Memory überhaupt bemerken. Kann ich mich nicht erinnern, dass 'DataTable' s eifrig geladen sind? Gibt es heutzutage eine faule Version von ihnen? Ich würde definitiv glauben, dass das Laden der Daten noch etwas länger dauert, aber ich gehe davon aus, dass dies ausgeschlossen wurde. –

+0

versuchen Sie, 'DataTable.Select' Methode zu verwenden. z.B. (fest codiertes Beispiel, um die Leistung zu testen): 'DataRow [] dupes = dt.Select (String.Format (" Id = 2 UND SERIAL_NUMBER IN ('1', '2', '3') "));' – ASh

Antwort

2

Ich bin mir nicht sicher, ob Sie Ihre Seriennummern bestimmt eine Liste von int oder string, sondern nehmen, was Sie buchstäblich haben, ist die Performance wahrscheinlich hilft eine einmalige Umwandlung in string, wie dies zu tun:

var serialStringsAlreadyExisted = serialNumAlreadyExisted.Select(x => x.ToString()).ToList(); 

Dann können Sie mit einem Join gehen, die als Contains odereffizienter sein sollten:

var duplicateRows = (
    from row in dt.AsEnumerable() 
    where row.Field<int>("ID") == varID 
    join serial in serialStringsAlreadyExisted on row.Field<string>("SERIAL_NUMBER") equals serial 
    select row) 
    .ToList(); 

bearbeiten

Bin gerade einen schnellen Geschwindigkeitstest. Unter Verwendung von join wird in etwa die Hälfte der Zeit verglichen mit dem ursprünglichen Code für eine Million Zeilen. Wenn Sie die Anzahl der Elemente in serialNumAlreadyExisted auf 20 erhöhen, dauert die Verwendung von join näher an 20% der Zeit der Baseline-Methode.

1

Ich würde vorschlagen, Kombination von HashSet und Contains Anruf, der O(1) Zugriff gibt. Any geht in diesem Fall durch die Sammlung. Im schlimmsten Fall erfordert es 3 Vergleiche.

HashSet<int> serialNumAlreadyExisted = new HashSet<int>(); 

serialNumAlreadyExisted.Add(1); 
serialNumAlreadyExisted.Add(2); 
serialNumAlreadyExisted.Add(3); 

var duplicateRows = 
    (from row in dt.AsEnumerable() 
    where row.Field<int>("ID") == varID && 
      serialNumAlreadyExisted.Contains(row.Field<string>("SERIAL_NUMBER")) 
    select row).ToList(); 
1

Da die Anzahl der Zeilen zu erhöhen, finden Sie es langsam in der Leistung aufgrund einer linearen Suche zur Zeit durchgeführt werden, da es eine Zahl mit dem folgenden Code zu finden versucht:

serialNumAlreadyExisted.Any(sr => sr == row.Field<string>("SERIAL_NUMBER")) 

AKTL Komplexität O(n^2), to make it O(n), ist es möglich, dass Sie ein Wörterbuch aus var serialNumAlreadyExisted = [1,2,3];

Unter der Annahme folgenden erstellen, ist der Code, können Sie es besser machen können, vor allem das Wörterbuch Wert

var testDictionary = serialNumAlreadyExisted.ToDictionary(x=>x,x=>x); 

Schluss Code wird wie folgt sein:

var duplicateRows = 
    (from row in dt.AsEnumerable() 
    where row.Field<int>("ID") == varID && 
      testDictionary.ConatinsKey(row.Field<string>("SERIAL_NUMBER")) 
    select row).ToList(); 
1

Erstens, Sie sind linqing gegen Datentabelle, was bedeutet, dass Ihre Datensätze alle im Speicher sind. Wenn Sie also Ihre Logik in eine gespeicherte Prozedur verschieben und der serialNumAlreadyExisted mit Zieltabelle beitreten, werden Sie eine hervorragende Leistungsverbesserung erzielen. Aber während Sie datatabale Tag verwenden, scheint es keine Option zu sein.

Zweitens ist serialNumAlreadyExisted ein int-Array, also wie vergleichen Sie es über string?

In Ihrem kurzen Snippet gibt es jedoch nicht viele Optionen.Mit Any oder Contains oder In-Memory-Daten zu verbinden, macht nicht viel anders. Die Verwendung eines HashSet für serialNumAlreadyExisted kann ein wenig helfen. Aber, noch einmal, bewegen Sie den Join zu einer gespeicherten Prozedur, und Sie werden den Unterschied sehen.

Verwandte Themen