У меня есть таблица данных со следующей структурой:
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;
}
}
Это очень медленно, потому что вы делаете запрос каждый раз, когда вы попадаете в самую внутреннюю строку, поэтому вместо того, чтобы получать все, что вы хотите в 1 запросе, вы получаете N запросов назад и вперед в базу данных (N - foobar.Count * foo. buckets.Count)
Это не может быть весь ваш код (что-то хранить в локальной переменной и ничего не делать с ним), пожалуйста, напишите весь свой метод, и я буду рад предоставить вам гораздо более быструю альтернативу.
Что-то вроде этого должно получить все результаты, которые вы хотите в 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);
Ну лично я никогда не позволял никому когда-либо проходить через ряды! Это самый худший способ доступа к данным в базе данных.
Запрос 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, но это может быть не совсем то же самое. Затем используйте пользовательский интерфейс, чтобы управлять тем, как появляются данные.