2012-07-17 30 views
2

從傳統的SQL語句是這樣的:LINQ的等值分組選擇

SELECT Id, Owner, MIN(CallTime) 
FROM traffic 
WHERE CallType = "IN" 
GROUP BY Owner; 

其中CallTime是一個日期時間字段,我要的是屬於每個Owner最早的記錄。

Linq如何實現?

這是我嘗試(我使用實體框架和context是實體實例):

var query = context.traffic.Where(t => t.CallType == "IN"); 
var results = query 
    .GroupBy(t => t.Owner) 
    .Select(g => new { CallTime = g.Min(h => h.CallTime) }); 

但我也需要訪問IdOwner領域,而現在我只有CallTime訪問。

+0

您可以使用[MinBy(http://code.google.com/p/morelinq/source/browse/MoreLinq/MinBy.cs) – 2012-07-17 10:55:10

+0

請問你傳統的SQL實例實際運行? – AakashM 2012-07-17 10:58:00

+1

ANSI SQL不允許使用選擇列表中不屬於「group by」組成部分的列; MySQL允許這樣做(除非你在ANSI模式下運行它)。 – dasblinkenlight 2012-07-17 10:58:43

回答

3

您無法在給定的代碼中訪問Id b因爲你按照所有者進行分組,並且組的關鍵將是所有者而不是「交通」對象。

如果按流量組對象,你需要一些方法來告訴GROUPBY如何將它們正確(由所有者即組)比較,這可以用的IEqualityComparer

例如完成

private class Traffic { 
    public int Id { get; set; } 
    public string Owner { get; set; } 
    public DateTime CallTime { get; set; } 
} 

private class TrafficEquaityComparer : IEqualityComparer<Traffic> { 
    public bool Equals(Traffic x, Traffic y) { 
      return x.Owner == y.Owner; 
    } 

    public int GetHashCode(Traffic obj) { 
     return obj.Owner.GetHashCode(); 
    } 
} 


private static TrafficEquaityComparer TrafficEqCmp = new TrafficEquaityComparer(); 

private Traffic[] src = new Traffic[]{ 
    new Traffic{Id = 1, Owner = "A", CallTime = new DateTime(2012,1,1)}, // oldest 
    new Traffic{Id = 2, Owner = "A", CallTime = new DateTime(2012,2,1)}, 
    new Traffic{Id = 3, Owner = "A", CallTime = new DateTime(2012,3,1)}, 
    new Traffic{Id = 4, Owner = "B", CallTime = new DateTime(2011,3,1)}, 
    new Traffic{Id = 5, Owner = "B", CallTime = new DateTime(2011,1,1)}, //oldest 
    new Traffic{Id = 6, Owner = "B", CallTime = new DateTime(2011,2,1)}, 
}; 

[TestMethod] 
public void GetMinCalls() { 
    var results = src.GroupBy(ts => ts, TrafficEqCmp) 
         .Select(grp => { 
          var oldest = grp.OrderBy(g => g.CallTime).First(); 
          return new { Id = oldest.Id, 
             Owner = grp.Key.Owner, 
             CallTime = oldest.CallTime }; 

         }); } 

這給

ID : Owner : MinCallTime 

1 : A : (01/01/2012 00:00:00) 
5 : B : (01/01/2011 00:00:00) 

的結果。

+0

這正是我所需要的,但不幸的是,實體框架似乎不喜歡GroupBy定製,因爲它啓動了一個非常長的錯誤消息:LINQ to Entities不能識別方法System.Linq.IQueryable'1 [System.Linq.IGrouping' 2 [ns.traffic,ns.traffic]] GroupBy [traffic,traffic](System.Linq.IQueryable'1 [ns.traffic],System.Linq.Expressions.Expression'1 [System.Func'2 [ns.traffic] ,ns.traffic]],System.Collections.Generic.IEqualityComparer'1 [ns.traffic])'方法,並且這個方法不能被翻譯成商店。「} – JPG 2012-07-17 11:55:08

+0

@JPG不是,它不會喜歡它,但如果你不擔心在數據庫上運行查詢,你可以嘗試'var query = context.traffic.Where(t => t.CallType ==「IN」)。ToList();'。這應該強制查詢將被解析,你可以在結果列表上運行該組。 – AlanT 2012-07-17 12:37:50

+0

好的,這將是可能的,高效的我認爲你的代碼不能正常工作,因爲它返回一個不相關的Id。對於所有者'B'它返回Id = 4,而不是Id = 5,但CallTime是好的。 – JPG 2012-07-17 13:02:09

2

您的SQL查詢對我來說看起來並不合法:您正在使用Id,但未按它進行分組。我假設你想組IdOwner

var results = query 
    .GroupBy(t => new {Id = t.Id, Owner = t.Owner}) 
    .Select(g => new { Id = g.Key.Id, Owner = g.Key.Owner, CallTime = g.Min(h => h.CallTime) }) 
    .ToList(); 

如果你想獲得最老的(最小)ID,而不是通過分組是:

var results = query 
    .GroupBy(t => t.Owner) 
    .Select(g => new { Id = g.Min(x => x.Id), Owner = g.Key, CallTime = g.Min(h => h.CallTime) }) 
    .ToList(); 
+0

如果我按ID,然後在結果中包含每用戶數的記錄,而我需要的最老的(唯一的)基於CallTime現場每一位業主的紀錄。 – JPG 2012-07-17 11:08:17

+0

你確定他想通過Id進行分組嗎?不應該是唯一的? – sasjaq 2012-07-17 11:08:45

+0

我可以想象兩種情況(對於同一個「所有者」或「Id」唯一的'Id')。更改我的答案以反映這個其他選項,因爲它更適合@ JPG的需求 – Zruty 2012-07-17 11:19:37

0

// custQueryIEnumerable<IGrouping<string, Customer>>

var custQuery = 
    from cust in customers 
    group cust by cust.City into custGroup 
    where custGroup.Count() > 2 
    orderby custGroup.Key 
    select custGroup; 

在您選擇的那個例子組