2013-10-07 165 views
21

我目前正在解決一個問題,我想運行一個查詢,並根據所選日期對結果進行分組。Linq - 按日期分組和計數

在這個例子中,假設一個簡單的模型,像這樣:

public class User 
{ 
     public DateTime LastLogIn {get; set;} 
     public string Name {get; set;} 
} 

我正在尋找解決的辦法是讓用戶的計數登錄的日期。 在數據庫中,DateTime與日期和時間組件一起存儲,但對於此查詢,我真的只關心日期。

我現在有這就是:

context.Users 
      .Where((x.LastLogIn >= lastWeek)  
       && (x.LastLogIn <= DateTime.Now)) 
      .GroupBy(x => EntityFunctions.TruncateTime(x.LastLogIn)) 
      .Select(x => new 
      { 
       Value = x.Count(), 
       Day = (DateTime)EntityFunctions.TruncateTime(x.Key) 
      }).ToList(); 

以上但返回一個空列表。

的最終目標是有對象的列表,其中包含一個值(用戶數登錄的某一天)和天(當日有問題)

有什麼想法?

在改變查詢:

context.Users 
      .Where((x.LastLogIn >= lastWeek)  
       && (x.LastLogIn <= DateTime.Now)) 
      .GroupBy(x => EntityFunctions.TruncateTime(x.LastLogIn)) 
      .Select(x => new 
      { 
       Value = x.Count(), 
       Day = (DateTime)x.Key 
      }).ToList(); 

現在返回一個單一的項目列表,取值爲where子句匹配用戶的總數,以及日是第一天。它似乎還沒有能夠按天分組

注意:原來上面的代碼是正確的,我只是在做別的錯誤。

的Sql,它是生成的(注意可能是很輕微的語法錯誤在這裏與我調整它的例子):

SELECT 
1 AS [C1], 
[GroupBy1].[A1] AS [C2], 
CAST([GroupBy1].[K1] AS datetime2) AS [C3] 
FROM (SELECT 
     [Filter1].[K1] AS [K1], 
     COUNT([Filter1].[A1]) AS [A1] 
     FROM (SELECT 
       convert (datetime2, convert(varchar(255), [Extent1].[LastLogIn], 102) , 102) AS [K1], 
       1 AS [A1] 
       FROM [dbo].[Users] AS [Extent1] 
       WHERE (([Extent1].[LastLogIn] >= @p__linq__1) AND ([Extent1].[LastLogIn] <= @p__linq__2) 
     ) AS [Filter1] 
     GROUP BY [K1] 
) AS [GroupBy1] 
+0

該查詢看起來OK。當你用'Count()'代替'GroupBy'時,你會得到一個非零值嗎? – dasblinkenlight

+0

用直計數運行返回計數預期匹配where子句,它似乎是分組正在殺死它 – Thewads

+2

您是否嘗試用'Day ='替換'Day =(DateTime)EntityFunctions.TruncateTime(x.Key)'' x.Key'?它應該是相同的,因爲日期已被截斷用於分組。 – dasblinkenlight

回答

21

你不需要第二TruncateTime在那裏:

context.Users 
    .Where((x.LastLogIn >= lastWeek) && (x.LastLogIn <= DateTime.Now)) 
    .GroupBy(x => EntityFunctions.TruncateTime(x.LastLogIn)) 
    .Select(x => new 
    { 
     Value = x.Count(), 
     // Replace the commented line 
     //Day = (DateTime)EntityFunctions.TruncateTime(x.Key) 
     // ...with this line 
     Day = (DateTime)x.Key 
    }).ToList(); 

GroupBy有TRU已從DateTime開始計時,所以您不需要再次調用它。

要使用EntityFunctions.TruncateTime你需要引用的程序集System.Data.Entity然後包括using System.Data.Objects;

+0

'.GroupBy(x => EntityFunctions.TruncateTime(x.LastLogIn))'是考慮日期分組的重要因素。 –

+0

@dasblinkenlight嗨,我需要在你的解決方案中做些什麼改變才能包含count = 0的日子?即使沒有登錄,我也需要在時間範圍內返回所有日子。謝謝。 – Patrick

+0

@Patrick我不認爲你可以這樣做:LINQ不會返回結果,除非它在數據庫中。您可以將結果轉換爲字典,然後遍歷一天中的日期範圍,並在字典可用時從字典中獲取數據。 – dasblinkenlight

1

試試這個:

.GroupBy(x => new {Year = x.LastLogIn.Year, Month = x.LastLogIn.Month, Day = x.LastLogIn.Day) 
       .Select(x => new 
       { 
        Value = x.Count(), 
        Year = x.Key.Year, 
        Month = x.Key.Month, 
        Day = x.Key.Day 
       }) 
+0

嘿,這是在上述相同的情況下,與它返回一個單一的項目,其中所有項目的計數 – Thewads

+0

可能會工作(雖然以某種方式討厭),但爲什麼原始查詢不起作用? – Alireza

1

您可以輕鬆地做到這一點:

yourDateList.GroupBy(i => i.ToString("yyyyMMdd")) 
      .Select(i => new 
      { 
       Date = DateTime.ParseExact(i.Key, "yyyyMMdd", CultureInfo.InvariantCulture, DateTimeStyles.None), 
       Count = i.Count() 
      }); 
+0

這個「似乎」不那麼有效,但我很多次都用這種方法取得了巨大的成功,特別是從「yyyyMMdd」是普遍接受的日期格式SQL Server傳遞數據時。 – pimbrouwers

1

你也可以做到這一點的一條線。

var b = (from a in ctx.Candidates select a) 
      .GroupBy(x => x.CreatedTimestamp.Date).Select(g => new { Date=(g.Key).ToShortDateString(), Count = g.Count() });