2013-03-12 13 views
12

Ich habe dieses Stück Linq geschrieben, um mit einem CROSS Join umzugehen, genau wie eine Datenbank zwischen mehreren Listen.Warum ist dieses Cross Join in Linq so langsam?

Aber aus irgendeinem Grund ist es extrem langsam, wenn eine der Listen mehr als 3000 gehen. Ich würde auf 30s warten? Diese Listen könnten sehr groß sein.

Diese Abfrage wird für jede Beziehung mit den Daten der anderen Liste geloopt, die von ColumnDataIndex stammen.

Irgendwelche Tipps?

UPDATE ** - Die Daten werden in normale Listen eingefügt, die zuvor aus den konfigurierten Quellen erstellt wurden. Dies ist im Moment alles in Erinnerung.

RunningResult[parameter.Uid] = (from source_row in RunningResult[parameter.Uid] 
          from target_row in ColumnDataIndex[dest_key] 
          where GetColumnFromUID(source_row, rel.SourceColumn) == GetColumnFromUID(target_row, rel.TargetColumn) 
          select new Row() 
          { 
           Columns = MergeColumns(source_row.Columns, target_row.Columns) 

          }).ToList(); 

Die 2 zusätzlichen Funktionen:

MergeColumns: Nehmen die Spalten aus den 2 Elementen und führen sie zu einem einzigen Array.

public static Columnn[] MergeColumns(Column[] source_columns, Column[] target_columns) 
{ 
     Provider.Data.BucketColumn[] new_column = new Provider.Data.BucketColumn[source_columns.Length + target_columns.Length]; 
     source_columns.CopyTo(new_column, 0); 
     target_columns.CopyTo(new_column, source_columns.Length); 
     return new_column; 
    } 

GetColumnFromUID: Liefert den Wert der Spalte in der Item-Anpassung der Säule uid gegeben.

private static String GetColumnFromUID(Row row, String column_uid) 
    { 
     if (row != null) 
     { 
      var dest_col = row.Columns.FirstOrDefault(col => col.ColumnUid == column_uid); 
      return dest_col == null ? "" + row.RowId : dest_col.Value.ToString().ToLower(); 
     } 
     else return String.Empty; 

    } 

Update:

Beendet die Daten nach oben bewegt und die Abfrage einer Datenbank. Dies reduziert sich auf die Geschwindigkeit auf eine Anzahl von ms. Ich hätte eine optimierte Loop-Funktion schreiben können, aber das war der schnellste Weg für mich.

+0

Wo Sie Ihre Datenquellen definieren, innerhalb oder außerhalb der Schleife.Wenn sie sich außerhalb befinden, sind sie abfragbare Quellen oder Listen. –

+0

Ich baue sie außerhalb der Schleife siehe oben –

+11

Mein Rat: ** Führen Sie einen Profiler. ** Alles andere ist raten. –

Antwort

4

Sie müssen keinen Cross Join durchführen. Kreuzverbindungen sind inhärent teure Operationen. Sie sollten das nicht tun, wenn Sie es wirklich brauchen. In deinem Fall brauchst du nur einen inneren Join. Sie führen einen Cross-Join aus, der viele Werte ergibt, die Sie gar nicht benötigen, und dann filtern Sie einen großen Prozentsatz dieser Werte aus, um Ihnen die wenigen benötigten Werte zu geben. Wenn Sie von Anfang an nur einen inneren Join ausgeführt haben, berechnen Sie nur die Werte, die Sie benötigen. Das erspart Ihnen das Erstellen einer ganzen Reihe von Zeilen, die Sie nicht brauchen, nur um sie wegwerfen zu lassen.

LINQ hat seine eigene innere Join-Operation, Join, so dass Sie brauchen noch nicht einmal Ihre eigene schreiben:

RunningResult[parameter.Uid] = (from source_row in RunningResult[parameter.Uid] 
           join target_row in ColumnDataIndex[dest_key] 
           on GetColumnFromUID(source_row, rel.SourceColumn) equals 
            GetColumnFromUID(target_row, rel.TargetColumn) 
           select new Row() 
           { 
            Columns = MergeColumns(source_row.Columns, target_row.Columns) 

           }).ToList(); 
+0

Beendet diese Abfrage in eine Datenbank verschoben, aber diese Antwort hat die Abfrage beschleunigt, während ich es getestet habe. –

0

Sie machen keine Kreuzverbindung, sondern eine innere Verbindung mit einer ON-Klausel, nur in Ihrem Fall die ON-Klausel im Wo-Prädikat.

eine innere Verknüpfung wird in der Regel mit zwei Hash-Sets/Tabellen durchgeführt, so dass Sie schnell die Zeile in der Serie X basierend auf dem Wert in Zeile Y.

So ‚weston Antwort ist OK finden, aber Sie müssen Verwenden Sie Wörterbücher/Hashtables, um es wirklich schnell zu machen. Beachten Sie, dass es möglicherweise mehr Zeilen pro Schlüssel gibt. Sie können dafür ein mehrwertiges Hashtable/Dictionary verwenden: https://github.com/SolutionsDesign/Algorithmia/blob/master/SD.Tools.Algorithmia/GeneralDataStructures/MultiValueDictionary.cs

+1

Er macht tatsächlich einen Cross-Join. Er braucht * nur * einen inneren Join zu machen, wenn man sein Beispiel anführt, und ein innerer Join würde die Performance stark verbessern. Die Tatsache, dass er ein Cross-Join durchführt, um die gleichen Ergebnisse wie ein Inner-Join zu erzielen, ist der Grund für die Performance-Probleme. Beachten Sie, dass LINQ über einen Join-Operator verfügt, mit dem er eine innere Verknüpfung ausführen kann. Er muss dafür keine eigenen Hashtabellen erstellen (obwohl er es sicherlich könnte). – Servy

+0

Ich schaute nur auf das Beispiel, nichts mehr. Das Beispiel tut dies: SELECT ... FROM X, Y WO X.field = Y.field; Gleiche Sache. Die from .. from-Klauseln würden tatsächlich einen Kreuz-Join implizieren, doch die Where-Klausel macht es semantisch nicht zu einem Cross-Join. Oder besser: seine Abfrage mit dem from ... from construct ist nicht optimal für das, was er machen möchte. Ich weiß, dass Linq einen Join-Operator hat, glaub mir;) –

+0

Die Tatsache, dass er eine SelectMany gefolgt von einem Where verwendet, bedeutet, dass er tatsächlich eine Cross-Join-Funktion ausführt. Das ist die Definition einer SelectMany *, auf die das 'From X from Y'' verweist. Er macht einen Cross-Join und filtert dann die Ergebnisse in die Ergebnisse eines inneren Joins. Das ist viel zeitaufwändiger als ein Join von Anfang an, da Sie viel unnötigen Overhead vermeiden. – Servy