LINQ to SQL 系列五 grouping having

在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有一大坨,如下:


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

SELECT [t0].[ClassID] AS [Key]

FROM [dbo].[Student] AS [t0]

GROUP BY [t0].[ClassID]

-- Context: SqlProvider(Sql2005) Model: AttributedMetaModel Build: 4.0.30319.1

SELECT [t0].[StudentID], [t0].[Name], [t0].[Hometown], [t0].[Gender], [t0].[Birthday], [t0].[ClassID], [t0].[WeightInKg], [t0].[HeightInCm], [t0].[Desc] AS [Desc]

FROM [dbo].[Student] AS [t0]

WHERE @x1 = [t0].[ClassID]

-- @x1: Input Int (Size = -1; Prec = 0; Scale = 0) [1]

-- Context: SqlProvider(Sql2005) Model: AttributedMetaModel Build: 4.0.30319.1

SELECT [t0].[StudentID], [t0].[Name], [t0].[Hometown], [t0].[Gender], [t0].[Birthday], [t0].[ClassID], [t0].[WeightInKg], [t0].[HeightInCm], [t0].[Desc] AS [Desc]

FROM [dbo].[Student] AS [t0]

WHERE @x1 = [t0].[ClassID]

-- @x1: Input Int (Size = -1; Prec = 0; Scale = 0) [2]

-- Context: SqlProvider(Sql2005) Model: AttributedMetaModel Build: 4.0.30319.1

SELECT [t0].[StudentID], [t0].[Name], [t0].[Hometown], [t0].[Gender], [t0].[Birthday], [t0].[ClassID], [t0].[WeightInKg], [t0].[HeightInCm], [t0].[Desc] AS [Desc]

FROM [dbo].[Student] AS [t0]

WHERE @x1 = [t0].[ClassID]

-- @x1: Input Int (Size = -1; Prec = 0; Scale = 0) [3]

-- Context: SqlProvider(Sql2005) Model: AttributedMetaModel Build: 4.0.30319.1

这个不能怪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给了我们查询的方便、灵活,也给了我们性能的陷阱。

linq to sql相关随笔:

时间: 2024-12-08 02:34:37

LINQ to SQL 系列五 grouping having的相关文章

LINQ to SQL 系列 如何使用LINQ to SQL插入、修改、删除数据

LINQ to SQL 系列 如何使用LINQ to SQL插入.修改.删除数据 http://www.cnblogs.com/yukaizhao/archive/2010/05/13/linq_to_sql_1.html http://blog.csdn.net/pan_junbiao/article/details/7015633 http://blog.sina.com.cn/s/blog_95cfa64601019vs1.html

一步一步学Linq to sql系列文章 转lovecherry

http://www.cnblogs.com/lovecherry/archive/2007/08/13/853754.html 现在Linq to sql的资料还不是很多,本人水平有限,如果有错或者误导请指出,谢谢. 一步一步学Linq to sql(一):预备知识 一步一步学Linq to sql(二):DataContext与实体 一步一步学Linq to sql(三):增删改 一步一步学Linq to sql(四):查询句法 一步一步学Linq to sql(五):存储过程 一步一步学L

LINQ to SQL 系列 如何使用LINQ to SQL插入、修改、删除数据 (转)

http://www.cnblogs.com/yukaizhao/archive/2010/05/13/linq_to_sql_1.html LINQ和 LINQ to SQL 都已经不是一个新事物了,但是我接触的比较晚,本着绝知此事要躬行的态度,决定写这个系列. 本文使用的测试环境是VS 2010,和sql server 2005数据库. 1.  从CUD开始,如何使用LINQ  to SQL插入.修改.删除数据 2.  查询 使用LINQ to SQL做简单查询 3.  查询 延迟加载与立即

LINQ to SQL系列四 使用inner join,outer join

先看一个最简单的inner join,在读取Student表时inner join Class表取的对应的Class信息: static void Main(string[] args) { using (var writer = new StreamWriter(WatchSqlPath, false, Encoding.UTF8)) { using (DbAppDataContext db = new DbAppDataContext()) { db.Log = writer; //inne

LINQ to SQL系列三 使用DeferredLoadingEnabled,DataLoadOption指定加载选项

本文中举例用到的数据模型如下: Student和Class之间是多对一关系,Student和Course之间是多对多关系. DataContext的DeferredLoadingEnabled属性指定是否需要延时加载,其默认值为true.以Student为例,其延时加载的对象是指Class和对应的Course.设定延时加载为true时,当访问到Student实例的Class属性或者StudentCourse属性时会自动加载Class表和StudentCourse表中的数据,如下示例代码: 1 2

SQL Server 2008空间数据应用系列五:数据表中使用空间数据类型

原文:SQL Server 2008空间数据应用系列五:数据表中使用空间数据类型 友情提示,您阅读本篇博文的先决条件如下: 1.本文示例基于Microsoft SQL Server 2008 R2调测. 2.具备 Transact-SQL 编程经验和使用 SQL Server Management Studio 的经验. 3.熟悉或了解Microsoft SQL Server 2008中的空间数据类型. 4.具备相应(比如OGC)的GIS专业理论知识. 5.其他相关知识. 通过前面几篇文章介绍了

LINQ系列:LINQ to SQL Join连接

1. 一对多 var expr = context.Products .Where(p => p.Category.CategoryName == "LINQ to SQL" && p.UnitPrice > 10m) .Select(p => new { p.ProductID, p.ProductName }); var expr = from p in context.Products where p.Category.CategoryName

Linq To Sql进阶系列(六)用object的动态查询与保存log篇

动态的生成sql语句,根据不同的条件构造不同的where字句,是拼接sql 字符串的好处.而Linq的推出,是为了弥补编程中的 Data != Object 的问题.我们又该如何实现用object的动态查询呢? 1,用object的查询是什么?我们可以简单的举这么一个例子.我们到公安局查找一个人.首先,我们会给出他的一些特征,比如,身高多少,年龄多少,性别,民族等.那么,我们把这个人的一些特征输入电脑.我们希望,电脑能给我们返回这个人的信息.而实际上,有相同特征的人太多了,常常返回一个集合.那让

LINQ系列:LINQ to SQL Where条件

1. 单一条件查询 var expr = context.Products .Where(p => p.ProductName == "LINQ to SQL"); SELECT [Extent1].[ProductID] AS [ProductID], [Extent1].[CategoryID] AS [CategoryID], [Extent1].[ProductName] AS [ProductName], [Extent1].[UnitPrice] AS [UnitPr