[.NET] EF LINQ 按时间对数据分类汇总

Posted Qiujuer

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[.NET] EF LINQ 按时间对数据分类汇总相关的知识,希望对你有一定的参考价值。

========================================================
作者:qiujuer
博客:blog.csdn.net/qiujuer
网站:www.qiujuer.net
开源库:Genius-android
转载请注明出处:http://blog.csdn.net/qiujuer/article/details/41868331
========================================================

发现国内弄 MvcWebAPI 的人简直少的可怜啊;前段时间发了大量的 WebAPI 的相关博文看的人不多;可以说是无人问津。

在对数据的操作中,对一堆数据的分类汇总是比较常见的,特别是按照时间进行分类汇总。比如算出某一天,某一月的数据总量等等。而鉴于国内研究的人少所以遇到一些问题简直没法查询,其中就包括:

<ExceptionMessage>
LINQ to Entities 不识别方法“System.String ToString(System.String)”,因此该方法无法转换为存储表达式。
</ExceptionMessage>

现在我有一堆存储在数据库中的数据;其数据简单格式如下:
    public class DataModel
    
        public int Id  get; set; 
        public string Data  get; set; 
        public DateTime Time  get; set; 
    
就只有 一个数据,一个时间,一个标志字段。 现在这样的数据有一大堆,各个时间段的都有;我想要统计分别每天有哪些数据存在。

方案一

最傻,最直接;第一种方法你首先查询该数据库中所有数据; 然后使用for 或者 foreach 循环遍历整个数据,然后判断加入到不同的 List 列表中,然后打包返回。 该方法未使用 LINQ,而是使用的简单的操作来实现;这里就不贴出代码了。

方案二

思路清晰。在这中方法中在进行 SQL 查询时就把返回的时间 改成 “天” ,而没有时间的格式;然后再进行分类汇总,最后返回想要的字段。
            var note2 = db.Notes
                // 先进行了时间字段变更为String字段,切只保留到天
                .Select(n => new  Data = n.Data, Time = n.Time.ToString("yyyy-MM-dd") )
                // 分类
                .GroupBy(n => n.Time)
                // 返回汇总样式
                .Select(n => new  Time = n.Key, Datas = n.ToList() ).ToList();
方法似乎很正确,但是运行后:
<Error>
<Message>出现错误。</Message>
<ExceptionMessage>
LINQ to Entities 不识别方法“System.String ToString(System.String)”,因此该方法无法转换为存储表达式。
</ExceptionMessage>
<ExceptionType>System.NotSupportedException</ExceptionType>
<StackTrace>...</StackTrace>
</Error>
这时就出现了这样的情况,是我们的思路错了?不是,思路完全正确。 只是在 EF 中的 LINQ 不能识别 ToString 而已。

方案三

思路不变,任然才有方案二的思路,以及大部分代码,只更改一小部分。
            var note3 = db.Notes
                // 先进行了时间字段变更为String字段,切只保留到天
                // 采用拼接的方式
                .Select(n => new  Data = n.Data, Time = n.Time.Year + "-" + n.Time.Month + "-" + n.Time.Day )
                // 分类
                .GroupBy(n => n.Time)
                // 返回汇总样式
                .Select(n => new  Time = n.Key, Datas = n.ToList() ).ToList();
既然不能返回,那我们就来自己拼接;这种方式能够正确执行并返回正确数据。 推荐!!!

方案四

换个思路,既然 EF LINQ 中无法使用 ToString ,那么单独的 LINQ 呢?
            // 先进行数据查询,返回数据
            var datas = await db.Notes.ToListAsync();
            // 然后在内存中使用 LINQ
            var note4 = datas.Select(n => new  Data = n.Data, Time = n.Time.ToString("yyyy-MM-dd") )
                .GroupBy(n => n.Time)
                .Select(n => new  Time = n.Key, Datas = n.ToList() ).ToList();
至于分类汇总的思路不变;只是把 ToString 放到了独立的 LINQ 中使用而已;实践证明,这个是可行的方案。 同样推荐!!!

区别是啥?

第二种思路无法成功,主要是 EF 识别不了 ToString 中的操作,这个很好理解,因为 EF 最终是需要换成 SQL 语句进行执行的。如果我们加上了 ToString 看似可以,但是其实完全不合理, 因为电脑不知道我们 ToString 中究竟有些啥,所以它不知道应该转换为什么样的 SQL 语句。 所以我们在 方案三中直接告诉了他,就是使用时间字段的对应参数进行组装,所以他能正确执行。 至于 方案四 这个就更加好说了,因为 ToString 是放在查询后,所以它无需进行对应的 SQL 语句的操作了,所以这个时候随你怎么弄都不为过。 方案三与方案四 结果完全一样,代码也几乎一样;但是其执行上却相差甚远! 为何?
  • 因为 方案三 基本上是把所有的工作放在了 SQL 上执行,这边只负责接收成果就OK 了!
  • 而 方案四 则是获取数据是在 SQL 操作,但是数据的处理是放在服务器端内存中进行的

其优劣性

  • 如果你的 SQL 数据性能远远大于 你的服务器后台 配置,那么才有第三方案 是比较划算的。
  • 如果你的 服务器的内存不错,分析也不错;能完全胜任其数据处理的话 第四方案 是你的不二选择。
这个其处理的时间就不太好估计了,毕竟每个人的电脑都不尽相同;所以看个人估计吧!一般情况下两种都差距不大。至于没有说第一种,因为第一种的性能上有待考虑;当然如果你的 分析算法够OK 的话,那就可以与 第四方案 拼个你死我活。

实际使用

<ArrayOfNoteInfo xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://schemas.datacontract.org/2004/07/GeniusSpace.Controllers.Api">
<NoteInfo>
<Changes>
<SNote>
<Content>似的撒旦</Content>
<Time>2014-12-11</Time>
</SNote>
<SNote>
<Content>sewrwfqf</Content>
<Time>2014-12-11</Time>
</SNote>
<SNote>
<Content>sgewhsdwqd</Content>
<Time>2014-12-11</Time>
</SNote>
</Changes>
<Time>2014-12-11</Time>
</NoteInfo>
<NoteInfo>
<Changes>
<SNote>
<Content>asgafasgre</Content>
<Time>2014-12-10</Time>
</SNote>
<SNote>
<Content>dgwetwet</Content>
<Time>2014-12-10</Time>
</SNote>
</Changes>
<Time>2014-12-10</Time>
</NoteInfo>
<NoteInfo>
<Changes>
<SNote>
<Content>rwqyhtrjhdf</Content>
<Time>2014-12-09</Time>
</SNote>
</Changes>
<Time>2014-12-09</Time>
</NoteInfo>
</ArrayOfNoteInfo>
这是我这边使用的情况,当然其参数与上面的参数不对;但是其分类汇总是完成了的。
采用的是 第三方案SQL 语句如下:
SELECT 
    [Project2].[C2] AS [C1], 
    [Project2].[C1] AS [C2], 
    [Project2].[C4] AS [C3], 
    [Project2].[Content] AS [Content], 
    [Project2].[C3] AS [C4]
    FROM ( SELECT 
        [Distinct1].[C1] AS [C1], 
        1 AS [C2], 
        [Extent2].[Content] AS [Content], 
        CASE WHEN ([Extent2].[Content] IS NULL) THEN CAST(NULL AS varchar(1)) ELSE CASE WHEN (DATEPART (year, [Extent2].[Published]) IS NULL) THEN N'' ELSE  CAST( DATEPART (year, [Extent2].[Published]) AS nvarchar(max)) END + N'-' + CASE WHEN (DATEPART (month, [Extent2].[Published]) IS NULL) THEN N'' ELSE  CAST( DATEPART (month, [Extent2].[Published]) AS nvarchar(max)) END + N'-' + CASE WHEN (DATEPART (day, [Extent2].[Published]) IS NULL) THEN N'' ELSE  CAST( DATEPART (day, [Extent2].[Published]) AS nvarchar(max)) END END AS [C3], 
        CASE WHEN ([Extent2].[Content] IS NULL) THEN CAST(NULL AS int) ELSE 1 END AS [C4]
        FROM   (SELECT DISTINCT 
            CASE WHEN (DATEPART (year, [Extent1].[Published]) IS NULL) THEN N'' ELSE  CAST( DATEPART (year, [Extent1].[Published]) AS nvarchar(max)) END + N'-' + CASE WHEN (DATEPART (month, [Extent1].[Published]) IS NULL) THEN N'' ELSE  CAST( DATEPART (month, [Extent1].[Published]) AS nvarchar(max)) END + N'-' + CASE WHEN (DATEPART (day, [Extent1].[Published]) IS NULL) THEN N'' ELSE  CAST( DATEPART (day, [Extent1].[Published]) AS nvarchar(max)) END AS [C1]
            FROM [dbo].[Notes] AS [Extent1] ) AS [Distinct1]
        LEFT OUTER JOIN [dbo].[Notes] AS [Extent2] ON ([Distinct1].[C1] = (CASE WHEN (DATEPART (year, [Extent2].[Published]) IS NULL) THEN N'' ELSE  CAST( DATEPART (year, [Extent2].[Published]) AS nvarchar(max)) END + N'-' + CASE WHEN (DATEPART (month, [Extent2].[Published]) IS NULL) THEN N'' ELSE  CAST( DATEPART (month, [Extent2].[Published]) AS nvarchar(max)) END + N'-' + CASE WHEN (DATEPART (day, [Extent2].[Published]) IS NULL) THEN N'' ELSE  CAST( DATEPART (day, [Extent2].[Published]) AS nvarchar(max)) END)) OR (([Distinct1].[C1] IS NULL) AND (CASE WHEN (DATEPART (year, [Extent2].[Published]) IS NULL) THEN N'' ELSE  CAST( DATEPART (year, [Extent2].[Published]) AS nvarchar(max)) END + N'-' + CASE WHEN (DATEPART (month, [Extent2].[Published]) IS NULL) THEN N'' ELSE  CAST( DATEPART (month, [Extent2].[Published]) AS nvarchar(max)) END + N'-' + CASE WHEN (DATEPART (day, [Extent2].[Published]) IS NULL) THEN N'' ELSE  CAST( DATEPART (day, [Extent2].[Published]) AS nvarchar(max)) END IS NULL))
    )  AS [Project2]
    ORDER BY [Project2].[C1] ASC, [Project2].[C4] ASC
从语句可以看出,其分类汇总,以及查询全部都是在SQL端完成。 按天分类实现了,按月、按年、按小时那还用说么? 如果你有更好的思路,还希望能写在下面一起交流交流。 ========================================================
作者:qiujuer
博客:blog.csdn.net/qiujuer
网站:www.qiujuer.net
开源库:Genius-Android
转载请注明出处:http://blog.csdn.net/qiujuer/article/details/41868331
========================================================

以上是关于[.NET] EF LINQ 按时间对数据分类汇总的主要内容,如果未能解决你的问题,请参考以下文章

EF(Linq)框架使用过程中的小技巧汇总 dbfunctions

Linq,EF Core - 按一个字段分组并使用其他字段从其他表中获取数据列表

ASP.NET EF(LINQ/Lambda查询)

ASP.NET MVC4.0+EF+LINQ+bui+网站+角色权限管理系统

ASP.NET 5 和 EF - 用 LINQ 替换子列表

.Net开源数据库设计工具Mr.E For Linq (EF 6.1) 教程