2013-03-12 1 views
12

Я написал этот фрагмент Linq, чтобы обрабатывать CROSS Join, так же как и база данных между несколькими списками.Почему этот Cross Join настолько медленный в Linq?

Но почему-то очень медленно, когда какой-либо из списков превышает 3000. Я бы подождал 30? Эти списки могут быть очень большими.

Этот запрос зациклирован для каждой связи с данными другого списка, исходящими от ColumnDataIndex.

Любой совет?

ОБНОВЛЕНИЕ ** - Данные вставляются в обычные списки, которые создаются вручную из сконфигурированных источников. Это все в памяти на данный момент.

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

2 Дополнительные функции:

MergeColumns: Принимает Колонны из 2 пунктов и объединяет их в один массив.

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: Возвращает значение столбца в совпадающем элементе столбец UID дан.

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:

Законченное перемещение данных и запрос к базе данных. Это уменьшено до скорости до нескольких мс. Мог бы написать оптимизированную петлевую функцию, но это был самый быстрый выход для меня.

+0

Где вы определяя свои источники данных, внутри или вне цикла.Если они являются внешними, это запрашиваемые источники или списки. –

+0

Я строю их вне цикла см. Выше –

+11

Мой совет: ** запустить профайлер. ** Что-нибудь еще угадывает. –

ответ

4

Вам фактически не нужно выполнять перекрестное соединение. Перекрестные соединения являются по своей сути дорогостоящими операциями. Вы не должны этого делать, если вам это действительно не нужно. В вашем случае то, что вам действительно нужно, - это просто внутреннее соединение. Вы выполняете кросс-соединение, которое приводит к множеству значений, которые вам вообще не нужны, и затем вы отфильтровываете огромный процент этих значений, чтобы оставить вас с теми немногими, которые вам нужны. Если вы только что сделали внутреннее соединение с самого начала, вы бы вычислили только те значения, которые вам нужны. Это избавит вас от необходимости создавать целые ряды, которые вам не нужны, просто чтобы их выбросили.

LINQ имеет свою собственную внутреннюю операцию соединения, Join, так что вам даже не нужно писать свои собственные:

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

Закончилось перемещение этого запроса в базу данных, но этот ответ ускорил запрос во время его тестирования. –

0

Вы не выполняете перекрестное соединение, а внутреннее соединение с предложением ON, только в вашем случае, предложение ON в предикате.

внутреннее соединение, как правило, делается с двумя хэш-наборы/таблиц, так что вы можете быстро найти строку в множестве X на основе значения в строке Y.

Так «ответ Уэстона в порядке, но вы должны используйте словари/хеш-таблицы, чтобы сделать их действительно быстрыми. Имейте в виду, что может быть больше строк на ключ. Вы можете использовать многозначный хэш-таблицу/словарь, подобный этому: https://github.com/SolutionsDesign/Algorithmia/blob/master/SD.Tools.Algorithmia/GeneralDataStructures/MultiValueDictionary.cs

+1

Он на самом деле делает крест. Ему только нужно * сделать внутреннее соединение, учитывая его пример, и сделать внутреннее соединение значительно повысит производительность. Тот факт, что он делает крест, чтобы получить те же результаты, что и внутреннее соединение, является причиной проблем с производительностью. Обратите внимание, что LINQ имеет оператор 'join', который он может использовать для выполнения внутреннего соединения; ему не нужно создавать свои собственные хеш-таблицы, чтобы сделать это (хотя он, конечно, мог). – Servy

+0

Я просто посмотрел на пример, не более того. Пример: SELECT ... FROM X, Y WHERE X.field = Y.field; То же самое. От ... от предложений действительно подразумевается перекрестное соединение, но предложение where делает семантически не крест-соединение. Или лучше: его запрос с использованием ... от конструкции не является оптимальным для того, что он хочет делать. Я знаю, что у Linq есть оператор объединения, поверьте мне;) –

+0

Тот факт, что он использует SelectMany, а затем «Где» означает, что он действительно делает крест-соединение. Это определение SelectMany *, к которому относится «From X from Y ...». Он делает перекрестное соединение, а затем фильтрует результаты, какими будут результаты внутреннего соединения. Это * много * больше времени, чем занятие Присоединиться с самого начала, так как вы избегаете много ненужных накладных расходов. – Servy