|
我在代码中使用它DBML自动生成的LINQ to/ ^/ z/ o9 Q' U: e- ?
SQL因此,适当选择和插入数据将是很好的。这是另一篇文章中建议的一种方法。在下面的例子中,e_activeSession是DataContext中表自动生成的表示形式:
, z" ^( ^- K7 Hvar statistics = from record in startTimes group record by record.startTime into g select new e_activeSession workerId = wcopy, startTime = g.Key.GetValueOrDefault()totalTasks = g.Count()totalTime = g.Max(o => o.record.timeInSession).GetValueOrDefault()minDwell = g.Min(o => o.record.dwellTime).GetValueOrDefault(), percent80 = Convert.ToDouble(g.Sum(o => Convert.ToInt16(o.record.correct80)))( g.Sum(o => Convert.ToInt16(o.record.correct80) Convert.ToInt16(o.record.wrong80)) }; percent80 = Convert.ToDouble(g.Sum(o => Convert.ToInt16(o.record.correct80))) / g.Sum(o => Convert.ToInt16(o.record.correct80) Convert.ToInt16(o.record.wrong80)) };因此,我尝试了以下方法:
0 o! y# h' N7 ]* m8 Z3 J, V% j; Qvar groups = from record in startTimes group record by record.startTime into g select g;var statistics = groups.ToList().Select( g => new e_activeSession workerId = wcopy, startTime = g.Key.GetValueOrDefault(), totalTasks = g.Count(), totalTime = g.Max(o => o.record.timeInSession).GetValueOrDefault(), minDwell = g.Min(o => o.record.dwellTime).GetValueOrDefault(), percent80 = Convert.ToDouble(g.Sum(o => Convert.ToInt16(o.record.correct80)))( g.Sum(o => Convert.ToInt16(o.record.correct80) Convert.ToInt16(o.record.wrong80)) percent80 = Convert.ToDouble(g.Sum(o => Convert.ToInt16(o.record.correct80))) / g.Sum(o => Convert.ToInt16(o.record.correct80) Convert.ToInt16(o.record.wrong80)) });但是,这种方法ToList效率似乎很低,只会让我的代码长时间坐在那里。有更好的方法吗?
4 R# H8 l t- f2 u: s& W# e
& q+ p" f8 Q0 v/ x7 V* A 解决方案:
$ Y# t, M$ F6 X, l. h! Q AsEnumerable()``ToList()介绍处理linq-to-( A% Z" S, ~+ x1 t
object它会做同样的事情,但它不会浪费时间和内存来存储所有这些对象。相反,当你遍历它时,它会一次创建一个对象。
+ }' h& f6 ? M. }. h. {6 ]4 D应通常使用AsEnumerable()将操作从另一个源移动到内存的方法,而不是ToList()除非你真的想要一个列表(例如,如果你想多次击中相同的数据,它将被用作缓存)。& K/ E* X( \9 X- s8 ]3 J
到目前为止,我们有:9 j+ P% s' w/ v# v; J0 M; S9 r/ T& _
var statistics = ( from record in startTimes group record by record.startTime into g select g; ).AsEnumerable().Select( g => new e_activeSession workerId = wcopy, startTime = g.Key.GetValueOrDefault(), totalTasks = g.Count(), totalTime = g.Max(o => o.record.timeInSession).GetValueOrDefault(), /* ... */ };但还有一个更大的问题。你也应该小心groupby。当与聚合法一起使用时,通常是可以的,但否则最终可能会成为许多数据库调用(一次获取不同的键值,然后每个值一次)。# Q/ V% O4 z7 X6 Z6 I' z+ F
考虑到以上内容(我省略了不提及每一列的内容)。如果不使用AsEnumerable()(或不使用ToList()你拥有的),因为wcopy可能根本不在查询范围内(我看不到它的定义位置),是第一个生成的SQL将是(如果允许),类似:: D, v9 o. o# F/ Z0 q ~
select startTime,count(id),max(timeInSession),/* ... */from tasksgroup by startTime数据库应该处理得很有效(如果没有,请检查索引并在生成的查询中操作数据库引擎优化顾问)。
) U6 j4 e* o# j4 s# H' O1 Q然而,在内存中分组时,它很可能首先执行:2 K. C& x# `* X6 _! M; r2 f
select distinct startTime from tasks接着
8 Y; n2 U. i( r" w, ?% Eselect timeInSession,/* ... */from taskswhere startTime = @p0对于startTime发现的每一个不同点都被用作传递@p0.无论其他代码的效率如何,都会很快造成灾难性的后果。6 B- s# d F+ K% |6 B& }# b& f
我们有两个选择。哪个最好根据情况而异,所以我给两者,尽管这里的第二个最有效。
0 c) w, _* s. ?% `有时候,我们最好的办法就是加载所有相关行并在内存中分组:# ~8 _" o1 p7 d
var statistics = from record in startTimes.AsEnumerable() group record by record.startTime into g select new e_activeSession { workerId = wcopy, startTime = g.Key.GetValueOrDefault(), totalTasks = g.Count(), totalTime = g.Max(o => o.record.timeInSession).GetValueOrDefault(), /* ... */ };我们可选择我们关心的列,我们可能会使它更有效率(如果上述内容仍然使用表中的每一列,则无关紧要)
8 Z. F) K& {" V8 I6 P6 Nvar statistics = from record in from dbRec in startTimes select new {dbRec.startTime,dbRec.timeInSession,/*...*/}).AsEnumerable() group record by record.startTime into g select new e_activeSession workerId = wcopy, startTime = g.Key.GetValueOrDefault(), totalTasks = g.Count(), totalTime = g.Max(o => o.record.timeInSession).GetValueOrDefault(), /* ... */ };我认为这不是最好的情况。我会用它来列举组,然后列出每个组。如果你想汇总每个组,而不是列出它们,最好把汇总保留在数据库中。数据库擅长这一点,它将大大减少通过网络发送的总数。在这种情况下,我能想到的最好方法是强迫一个新对象,而不是镜像它的物理类型,但它不能被识别为物理类型。你可以为此创建一种类型(如果你想做多个变体,它是非常有用的),否则匿名类型:2 r" T% `2 o. C. C
var statistics = ( from record in startTimes group record by record.startTime into g select new{ startTime = g.Key.GetValueOrDefault(), totalTasks = g.Count(), totalTime = g.Max(o => o.record.timeInSession).GetValueOrDefault(), /* ... */ }).AsEnumerable().Select( d => new e_activeSession workerId = wcopy, startTime = d.startTime, totalTasks = d.totalTasks, * ... */ };显而易见的缺点是冗长冗长。然而,它将使操作在db在不浪费时间和内存的情况下内存ToList(),不会像e_activeSession像往常一样反复击中db,并将创建的内容从linq2sql拖入linq2objects因此,应允许这样做。
w/ `0 ? y' _- { l顺便说一句,.NET该协议以大写字母开始。没有技术原因,但这意味着你将匹配更多人的代码,包括BCL以及您使用的其他库的代码)。, P! S2 w) V+ o
编辑:顺便说一句;我刚看到你的其他问题。请注意,在某种程度上,AsEnumerable()这是导致这个问题确切原因的变体。清楚了,你会对不同的事情有所不同linq查询提供程序之间的界限令人困惑。 |
|