2013年2月28日星期四

SQL to LINQ的GroupBy解說

SQL中的GROUP BY用法,大家都知道。
但若果轉移到LINQ上,或許會感到迷惑,因為寫SQL都久了,把SQL語法的直覺性放在LINQ可是不行。

聽起來很抽象吧? 來看看示範
AdventureWorks 2008 LT Sample DB 配合 LINQPad做示範。

我想找出SalesOrderID : 71920的貨物總和/價值總和,SQL就很容易會寫出這樣,亦得出正確的結果。
SELECT SalesOrderID, SUM(OrderQty) as TotalQty, SUM(UnitPrice) as TotalPrice 
FROM SalesLT.SalesOrderDetail detail
WHERE detail.SalesOrderID = '71920'
GROUP BY  detail.SalesOrderID;



來到LINQ,如果把SQL的編寫概念放在LINQ,或許你會跟我一樣寫出 :
from detail in SalesOrderDetails
where detail.SalesOrderID.Equals(71920) 
group detail by detail.SalesOrderID into g
select g

但當然,結果是怪怪的。
得出來的是 IOrderedQueryable<IGrouping<Int32,SalesOrderDetail>>
因為在這裡的Group...By...其實只是意味著把SalesOrderID和SalesOrderDetail集合至IGrouping的"g"。
結果其實與下方Comment的差不多。



正確的寫法應該是用new keyword去建立新的class。
from detail in SalesOrderDetails
 where detail.SalesOrderID.Equals(71920) 
 group detail by detail.SalesOrderID into g
 select new { 
    /*Distinct() return Enumerable<Int32> , Sum() & First() to return Int32 */
  SalesOrderID = g.Select(item =>item.SalesOrderID).Distinct().First(), 
 TotalQty =  g.Select(item =>Convert.ToDouble(item.OrderQty)).Sum(),
 TotalPrice = g.Select(item =>item.UnitPrice).Sum()
}

上述的new keyword跟平時的功用相似 ( FileInfo fInfo = new FileInfo(); ),但在這裡是建立新的Class或Structs,而且是Anonymous Type,帶有SalesOrderID / TotalQty / TotalPrice 的Properties。

得出來的結果就會是 :


如果覺得語法太冗長,可以改寫成Method-Based的Syntax :
SalesOrderDetails.Where(sod => sod.SalesOrderID == 71920)
                 .GroupBy(sod => sod.SalesOrderID)
                 .Select(g => new {
                               SalesOrderId = g.Key,
                               TotalQty = g.Sum(i => Convert.ToDouble(i.OrderQty)),
                               TotalPrice = g.Sum(i => i.UnitPrice)
                   })


參考:
Method-Based Query Syntax
http://msdn.microsoft.com/en-us/library/bb669086.aspx
Query Syntax and Method Syntax in LINQ (C#)
http://msdn.microsoft.com/en-us/library/vstudio/bb397947.aspx

沒有留言:

發佈留言