2014-05-16 92 views
0

我有一个用户表,分组到会话中。我想选择一个阵列对于由多项任务的每个用户他们在每一届有:LINQ to SQL:在一个查询中选择整数数组

var taskCounts = 
    from session in gzClasses.e_userLongSessions 
    orderby session.workerId ascending, session.startTime ascending 
    group session by session.workerId 
    into record 
    select record.Select(s => s.totalTasks).ToArray(); 
int[][] result = taskCounts.ToArray(); 

上述理论上的作品,但它导致在一个单独的SQL查询为每个用户如下图所示。由于数据库不是本地的,这需要相当长的时间。有没有办法在一个查询中获取所有数据,并减少运行大量单个查询的开销?

enter image description here

同时,我想确保它仅通过线路传输totalTasks整数值,而不是发送整个数据库记录的效率。换句话说,我想从一个远程数据库中获取一组分组整数,并将它们排列成C#程序中的数组。这听起来很简单,但我很难让LINQ来做到这一点。

+0

您的ef上下文中是否启用了延迟加载?这可以解释这样的行为,因为对于每个将被枚举的对象,它将执行不同的查询。 –

+0

@DimitrisKalaitzis我不知道那是什么 - 你能给一个指示器显示如何关闭它,我会再次尝试这个查询吗? –

+0

你使用实体框架吗?如果你确实可以改变你设置gzClasses的设置。例如gzClasses.ContextOptions.LazyLoadingEnabled = false; –

回答

1

取决于你有多少条记录回来,你可以返回最小的数据量,并在内存中做分组部(很快,因为它会已进行排序):

使用方法的语法:

gzClasses.e_userLongSessions 
     .OrderBy(s => s.workerId) 
     .ThenBy(s => s.startTime) 
     .Select(s => new { s.workerId, s.totalTasks }) 
     .ToList() 
     .GroupBy(x => x.workerId) 
     .Select(g => g.Select(x => x.totalTasks).ToArray()) 
     .ToArray(); 

使用查询语法:

var query = from session in gzClasses.e_userLongSessions 
      orderby session.workerId ascending, session.startTime ascending 
      select new { Id = s.workerId, s.totalTasks }; 

var taskCounts = from worker in query.ToList() 
       group worker by worker.Id into g 
       select g.Select(x => x.totalTasks).ToArray(); 

var result = taskCounts.ToArray(); 

我看到Linqpad(LINQ到SQL默认)相同的行为,但我觉得好像我见过的LINQ到实体处理GroupBy然后是group.Select(x => x.Something)而不会导致n + 1查询......可能是想象的东西,虽然(不知道SQL看起来会达到什么样的)。

+0

是的,在内存中执行此操作可能是一种很好的解决方法。这是LINQ到SQL,我相信(虽然我不熟悉Windows的术语)。我也不知道SQL会怎样做,所以这可能是正确的方法,除非有人提出了一些我不知道的魔法。 –

+0

优秀。这个查询几乎是即时的,但我不得不编辑你的答案。你能否看看是否有更简洁的方式来编写上面的语法?你的回答不完全正确,因为你需要抓住'g.Select(e => e.totalTask​​s).toArray()'。 –

+0

正如我在之前的编辑中提到的那样,在将其转换为数组之前,您需要在某个时间点将'totalTask​​s'字段从分组中取出,否则它将不是'int [] []'。 –

-2

您的ToArray()的位置会导致LINQ比应该更加渴望,导致您的许多查询。我认为,这将导致在短短的一个查询:

var taskCounts = 
    from session in gzClasses.e_userLongSessions 
    orderby session.workerId ascending, session.startTime ascending 
    group session by session.workerId 
    into record 
    select record.Select(s => s.totalTasks); 
int[][] result = taskCounts.Select(x => x.ToArray()).ToArray(); 
+0

您的查询在功能上与OP的查询完全相同。 – Servy

+0

'Select'仍然在'IQueryable'上,可能会产生他看到的相同结果。 taskCounts.ToList()。选择(x => x.ToArray())。ToArray()'可能会做到这一点。 – Ocelot20

+0

这实际上会导致相同的行为,因为我认为它在功能上是相同的查询。在调用'toArray'之前,'IQueryable'不会被评估,所以构造方法并不重要。 –

-1

也许如果你更换“从会议gzClasses.e_userLongSessions”,由“从gzClasses.e_userLongSessions.AsEnumerable()会议”?

+0

如何不在数据库上执行任何*查询以确保在数据库上执行* entire *查询? – Servy

+0

我不明白这是干什么的,因为它将IQueryable转换为IEnumerable,如果有的话,它基本上会通过线路拉动整个数据库来回答查询。 –

+0

完全安德鲁,它会带来来自gzClasses.e_userLongSessions的所有记录,但无论如何,这个组......通过...进入不像SQL组那样,SQL没有一个命令来组成一个列表中,EF将其转换为SQL命令的方式是向每个workerId发出一个select,将所有记录带入多个SELECT中,而不是将所有记录都放入一个SELECT中,并使.Net执行此操作。顺便说一句AsEnumerable的工作? – EduardoS

0

字典不会比数组数组更有用吗?

gzClasses.e_userLongSessions.OrderBy(s => s.workerId) 
    .ThenBy(s => s.startTime) 
    .GroupBy(s => s.workerId, t => t.TotalTasks).ToArray() 
    .ToDictionary(g => g.Key, h => h.ToArray()); 

这应该返回一个以workerId为关键字的Dictionary和一个任务数量数组作为值。

+0

我认为这同样有效,但它并不真正回答原始问题。或者,至少,它隐含地做着@ Ocelot20的回答,把所有内容都放在第一位。 –

+0

这是一个LINQ to SQL如何实现的问题,并不是您可以轻松解决的问题。最好的方法是强制执行初始语句,然后将数据分组到内存中。 EF中的实施似乎更适合您的需求。 http://stackoverflow.com/questions/12645767/linq-to-sql-simple-group-by-generating-many-sql-queries – user1417835

+0

我不认为我完全掌握了LINQ to SQL和实体之间的区别框架。我将不得不再看看更多。 –