Это работает медленно, потому что это в LINQ, или потому что я делаю это плохим способом?

1

У меня есть таблица данных со следующей структурой:

Foo1    Foo2    Value
A       2       5
B       4       20
C       6       30
B       6       4

У меня также есть список списков, который выглядит так:

Foobar
    Foo1
        A
        B
        Other
    Foo2
        4
        6
        Other

(Примечание. В foobar будет указано неизвестное количество столбцов/записей)

Мне нужно выполнить итерацию по моему списку, и для каждого элемента найдите среднее значение в таблице данных, которое соответствует значению подэлементов. Например, в Foo1 среднее значение для A в таблице данных составляет 5, B - 17,5, другое - 30.

Foobar
    Foo1
        A (average is 5)
        B (average is 17.5)
        Other (average is 30)
    Foo2
        4 (average is 20)
        6 (average is 17)
        Other (average is 5)

В действительности таблица данных имеет ~ 50 столбцов и некоторое количество строк. Я довольно неопытен с SQL/LINQ, поэтому я не уверен, как решить эту проблему так, чтобы это не было ужасно медленным. Специально для "Другого" ковша. Моя текущая стратегия состояла в том, чтобы просто выполнить вложенный foreach, а затем запрос LINQ для выбора значений, соответствующих текущему подэлементу, и сделать среднее. Что-то вроде этого:

foreach (var foo in foobar)
{
    foreach (var bucket in foo.buckets) 
    {
        var hits = myDataTable.Where(n => n[foo.name].ToString() == bucket.name);
            if (hits.Any())
            {
                bucket.average = hits.Select(x => x["Value"]).Average();
            }
    }
}

Это очень медленно. Это потому, что это в LINQ или потому, что я делаю это плохо?

редактировать

Я внес некоторые изменения, которые улучшили скорость, но они все еще довольно медленные.

foreach (var foo in foobar)
{
    var pairs = myDataTable.Select(
        n => new {Name = n[foo.name], Value = n["Value"]});

    foreach (var bucket in foo)
    {
        var temp = pairs.Where(n => bucket.name == n.Name);
        bucket.average = temp.Any() ? temp.Select(x => x.Value).Average() : 0;
    }
}
Теги:
linq

3 ответа

5

Это очень медленно, потому что вы делаете запрос каждый раз, когда вы попадаете в самую внутреннюю строку, поэтому вместо того, чтобы получать все, что вы хотите в 1 запросе, вы получаете N запросов назад и вперед в базу данных (N - foobar.Count * foo. buckets.Count)

Это не может быть весь ваш код (что-то хранить в локальной переменной и ничего не делать с ним), пожалуйста, напишите весь свой метод, и я буду рад предоставить вам гораздо более быструю альтернативу.

  • 0
    Обновлен OP с немного большим количеством кода.
2

Что-то вроде этого должно получить все результаты, которые вы хотите в Linq.

var res1 = from f in foo1
           group f by f.Name into g
           select new {Name = "Foo1 " + g.Key, Avg = g.Average(v=>v.Value)};

var res2 = from f in foo2
           group f by f.Name into g
           select new {Name = "Foo2 " + g.Key, Avg = g.Average(v=>v.Value)};


var result = res1.Union(res2).OrderBy(r=>r.Name);

РЕДАКТИРОВАТЬ:

потому что оба foo1 и foo2 находятся в одной таблице, вы можете сделать это

var res1 = from t in table
           group t by t.Foo1 into g
           select new {Name = "Foo1 " + g.Key, Avg = g.Average(v=>v.Value)};

var res2 = from t in table
           group t by t.Foo2 into g
           select new {Name = "Foo2 " + g.Key, Avg = g.Average(v+>v.Value)};

var result = res1.Union(res2).OrderBy(r=>r.Name);
  • 1
    Правильно. Техника слабо обозначена как «Группировка по совокупности».
  • 1
    Это в среднем
Показать ещё 9 комментариев
1

Ну лично я никогда не позволял никому когда-либо проходить через ряды! Это самый худший способ доступа к данным в базе данных.

Запрос sql:

select 'Foo1' as FooName, foo1, avg(value)
from mytable 
group by Foo1
UNion all
select 'Foo2' as FooName, foo2, avg(value)
from mytable 
group by 2

Не уверен, как перевести это на linq. Вы не говорите, что у вас есть бэкэнд, но это для SQL-сервера. Я полагаю, что большинство других dbs имеют аналогичную функцию avg, но это может быть не совсем то же самое. Затем используйте пользовательский интерфейс, чтобы управлять тем, как появляются данные.

  • 0
    Что если вы не знаете, сколько столбцов у вас будет?
  • 0
    Ты не знаешь базу данных?
Показать ещё 1 комментарий

Ещё вопросы

Сообщество Overcoder
Наверх
Меню