这是因为它在LINQ中运行缓慢,还是因为我做得不好?

时间:2014-06-06 16:10:12

标签: c# sql linq

我有一个具有以下结构的数据表:

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;
    }
}

3 个答案:

答案 0 :(得分:5)

它非常慢,因为每次到达最里面的行时您都会进行查询,因此您不必在1个查询中获得所需内容,而是获得N个查询&到数据库(N是foobar.Count * foo.buckets.Count)

这可能不是您的整个代码(将某些内容存储到本地变量中并对其执行任何操作)请发布您的整个方法,我们很乐意为您提供更快的替代方案。

答案 1 :(得分: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);

编辑:

因为foo1foo2都在同一个表格中,所以你可以这样做

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

答案 2 :(得分:1)

个人而言,我永远不会让任何人遍历行!这是访问数据库中数据的最糟糕方式。

sql Query将是:

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。你没有说你有什么db后端,但这个是针对SQL服务器的。我想大多数其他dbs都有类似的平均功能,但它可能不完全相同。 然后使用用户界面来操纵数据的显示方式。