[.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
========================================================

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章