程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> C# >> C#入門知識 >> lin to sql grouping 還有糖衣炮彈,lingrouping

lin to sql grouping 還有糖衣炮彈,lingrouping

編輯:C#入門知識

lin to sql grouping 還有糖衣炮彈,lingrouping


在linq to sql中grouping很簡單、很靈活,但是如果不注意則會中了Microsoft的糖衣炮彈。

本文使用的數據模型如下圖:

1. 用linq to sql寫一個最簡單的group語句:
查詢Students表並按ClassID分組,輸出每個班級的學生數。下面的代碼是糖衣版。

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 static void Main(string[] args) {     using (var writer = new StreamWriter(WatchSqlPath, false, Encoding.UTF8))     {         using (DbAppDataContext db = new DbAppDataContext())         {             //打印sql             db.Log = writer;               //最簡單的group by ,按ClassID對Students進行分組             var query = from s in db.Students                         group s by s.ClassID;               foreach (var item in query)             {                 //輸出班級編號和班級人數                 Console.WriteLine("class id = {0} student count = {1}",item.Key,item.Count());             }         }     }     Console.ReadLine(); }

Linq to sql的寫法很靈活,我們隨時可以調用Count等一大堆方法,上例中我在輸出時調用Count方法,導致了linq to sql真正執行的sql有一大坨,如下:

+ View Code

這個不能怪linq to sql,我們這樣寫它就必須得那樣執行;只能我們自己使用時處處小心。上例中正確的寫法應該是:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 static void Main(string[] args) {     using (var writer = new StreamWriter(WatchSqlPath, false, Encoding.UTF8))     {         using (DbAppDataContext db = new DbAppDataContext())         {             //打印sql             db.Log = writer;               //最簡單的group by ,按ClassID對Students進行分組             //var query = from s in db.Students             //            group s by s.ClassID;               var query = from s in db.Students                         group s by s.ClassID into sg                         select new {                             ClassID = sg.Key,                             Count = sg.Count()                         };               foreach (var item in query)             {                 //輸出班級編號和班級人數                 Console.WriteLine("class id = {0} student count = {1}", item.ClassID, item.Count);             }         }     }     Console.ReadLine(); }

這樣執行時才是那個我們以前寫t-sql時經常看到的那個group by語句。

2. 對分組聚合進行排序輸出,我忘記了用let
請看下面代碼,輸出每個班級體重最大的同學,並要求最大體重得大於39,並按照體重大下,對分組結果進行排序。

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 static void Main(string[] args) {     using (var writer = new StreamWriter(WatchSqlPath, false, Encoding.UTF8))     {         using (DbAppDataContext db = new DbAppDataContext())         {             //打印sql             db.Log = writer;               var query = from s in db.Students                         group s by s.ClassID into gS                         where gS.Max<Student>(s => s.WeightInKg) > 39                         orderby gS.Max<Student>(s => s.WeightInKg) descending                         select new                         {                             ClassID = gS.Key,                             MaxWeight = gS.Max<Student>(s => s.WeightInKg)                         };               foreach (var item in query)             {                 Console.WriteLine("class id = {0} student max weight = {1}", item.ClassID, item.MaxWeight);             }         }     }     Console.ReadLine(); }

上例中,在query變量聲明時我用了三次gS.Max<Student>(s => s.WeightInKg),我們知道在t-sql中如果使用聚合值,必須得用表達式,在linq to sql中用會不會有問題呢,看下執行上述代碼生成的sql吧:

1 2 3 4 5 6 7 8 9 10 SELECT [t1].[ClassID], [t1].[value3] AS [MaxWeight] FROM (     SELECT MAX([t0].[WeightInKg]) AS [value], MAX([t0].[WeightInKg]) AS [value2], MAX([t0].[WeightInKg]) AS [value3], [t0].[ClassID]     FROM [dbo].[Student] AS [t0]     GROUP BY [t0].[ClassID]     ) AS [t1] WHERE [t1].[value] > @p0 ORDER BY [t1].[value2] DESC -- @p0: Input Float (Size = -1; Prec = 0; Scale = 0) [39] -- Context: SqlProvider(Sql2005) Model: AttributedMetaModel Build: 4.0.30319.1

Linq to sql忠實的執行了我寫的代碼,我的query變量聲明中用了三次Max,在t-sql的嵌套表中也有三個MAX對應,這個不是我想要的,我又錯了,呼叫let,看看使用let之後linq to sql會怎麼執行吧:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 static void Main(string[] args) {     using (var writer = new StreamWriter(WatchSqlPath, false, Encoding.UTF8))     {         using (DbAppDataContext db = new DbAppDataContext())         {             //打印sql             db.Log = writer;               // group by with having             var query = from s in db.Students                         group s by s.ClassID into gS                         let mw = gS.Max<Student>(s => s.WeightInKg)                         where mw > 39                         select new                         {                             ClassID = gS.Key,                             MaxWeight = mw                         };             foreach (var item in query)             {                 Console.WriteLine("class id = {0} student max weight = {1}", item.ClassID, item.MaxWeight);             }         }     }     Console.ReadLine(); }

這次的sql語句比上一個好多了,但是還沒有足夠好:

1 2 3 4 5 6 7 8 9 SELECT [t1].[ClassID], [t1].[value] AS [MaxWeight] FROM (     SELECT MAX([t0].[WeightInKg]) AS [value], [t0].[ClassID]     FROM [dbo].[Student] AS [t0]     GROUP BY [t0].[ClassID]     ) AS [t1] WHERE [t1].[value] > @p0 -- @p0: Input Float (Size = -1; Prec = 0; Scale = 0) [39] -- Context: SqlProvider(Sql2005) Model: AttributedMetaModel Build: 4.0.30319.1

還是多嵌套了一層沒有必要的select。如何才能不嵌套這一層呢?如果你知道,請告訴我。

3. 使用linq to sql Group by多個字段:
這次我們先寫一個標准的t-sql

1 2 3 4 SELECT ClassID,Hometown,count(*) Cn FROM Student GROUP BY ClassID,Hometown ORDER BY ClassID,Hometown;

這個t-sql對Student表按照ClassID和Hometown兩個字段進行分組,並輸出每個班級中某個地方的學生數

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 static void Main(string[] args) {     using (var writer = new StreamWriter(WatchSqlPath, false, Encoding.UTF8))     {         using (DbAppDataContext db = new DbAppDataContext())         {             //打印sql             db.Log = writer;               // group by with having             var query = from s in db.Students                         group s by new { s.ClassID, s.Hometown } into gS                         let cn = gS.Count<Student>()                         select new                         {                             ClassID = gS.Key.ClassID,                             Hometown = gS.Key.Hometown,                             Count = cn                         };             foreach (var item in query)             {                 Console.WriteLine("class id = {0} hometown {1} student count = {2}", item.ClassID, item.Hometown,item.Count);             }         }     }     Console.ReadLine(); }

這一次linq to sql給了我一個驚喜,它用的t-sql和我開始寫的一樣。

4. 在3的基礎上加一點點需求,要求分組後的結果按照count排序,query的聲明代碼如下:

1 2 3 4 5 6 7 8 9 10 11 // group by with having var query = from s in db.Students             group s by new { s.ClassID, s.Hometown } into gS             let cn = gS.Count<Student>()             orderby cn descending             select new             {                 ClassID = gS.Key.ClassID,                 Hometown = gS.Key.Hometown,                 Count = cn             };

這次link to sql使用的sql語句多了一層沒有必要的select嵌套

1 2 3 4 5 6 7 8 SELECT [t1].[ClassID], [t1].[Hometown], [t1].[value] AS [Count] FROM (     SELECT COUNT(*) AS [value], [t0].[ClassID], [t0].[Hometown]     FROM [dbo].[Student] AS [t0]     GROUP BY [t0].[ClassID], [t0].[Hometown]     ) AS [t1] ORDER BY [t1].[value] DESC -- Context: SqlProvider(Sql2005) Model: AttributedMetaModel Build: 4.0.30319.1

看來使用linq to sql少多總要付出點代價的,linq to sql給了我們查詢的方便、靈活,也給了我們性能的陷阱。

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved