2012年11月8日星期四

LinQ学习笔记(三)

LinQ学习笔记(三)

前言                    


      继续前面的LinQ学习,话说好几天没有学习。晚上有空就在LOL,哎~其实晚上抽出一个小时来总结下是很不错的!这几天的工作都在准备着项目的部署,有空贴出来一起学习。这一次是LinQ基础的最后篇。然后在向高级点学习,贵在灵活的使用。毕竟别人写的东西还是以基础学习为主。OK!今天的的学习为集合之间的运算。

 

正文                


1、集合运算(Intersect()的方法——交集Except()的方法——差集Union()的方法——并集

   1:  using System;
   2:  using System.Collections.Generic;
   3:  using System.Linq;
   4:  using System.Text;
   5:  using System.Threading.Tasks;
   6:   
   7:  namespace ConsoleApplication1
   8:  {
   9:      class LinQTest3
  10:      {
  11:          static void Main(String[] args)
  12:          {
  13:              List<Student> students = new List<Student>
  14:              {
  15:                  new Student{Id=123,ClassId=1, Name="张三",MathResult=90,EnglishResult=81},
  16:                  new Student{Id=125,ClassId=1, Name="李四",MathResult=89,EnglishResult=75},                
  17:                  new Student{Id=124,ClassId=1, Name="张五",MathResult=88,EnglishResult=68},
  18:                  new Student{Id=127,ClassId=2, Name="张六",MathResult=78,EnglishResult=91},
  19:                  new Student{Id=128,ClassId=1, Name="张一",MathResult=71,EnglishResult=82},
  20:                  new Student{Id=121,ClassId=1, Name="张七",MathResult=75,EnglishResult=87},
  21:                  new Student{Id=122,ClassId=2, Name="张八",MathResult=93,EnglishResult=77},
  22:                  new Student{Id=126,ClassId=1, Name="张九",MathResult=91,EnglishResult=67},
  23:                  new Student{Id=133,ClassId=1, Name="李二",MathResult=88,EnglishResult=61},
  24:                  new Student{Id=132,ClassId=2, Name="李五",MathResult=91,EnglishResult=82},
  25:                  new Student{Id=135,ClassId=2, Name="王三",MathResult=61,EnglishResult=95},
  26:                  new Student{Id=136,ClassId=1, Name="王一",MathResult=96,EnglishResult=97},
  27:                  new Student{Id=137,ClassId=1, Name="张二",MathResult=74,EnglishResult=84}
  28:              };
  29:              List<ClassRoom> classes = new List<ClassRoom>
  30:              {
  31:                 new ClassRoom{Id=1,Name="一班"},
  32:                 new ClassRoom{Id=2,Name="二班"}
  33:              
  34:              };
  35:              //////////////////////////////////////////////////////////////////////////
  36:              var mathNames =
  37:                  from s in students
  38:                  where s.MathResult > 80 //查询出数学成绩大于80的人
  39:                  select s.Name;
  40:              var englishNames =
  41:                  from s in students
  42:                  where s.EnglishResult > 80 //查询出英语成绩大于80的人
  43:                  select s.Name;
  44:   
  45:              //1、使用Intersect()的方法——交集
  46:              var studentWithIntersect = mathNames.Intersect(englishNames);
  47:              Console.WriteLine("1、使用Intersect()的方法——交集的结果(数学>80且英语>80):");
  48:              foreach(var item in studentWithIntersect)
  49:              {
  50:                  Console.Write("{0} ",item);
  51:              }
  52:              Console.WriteLine();
  53:   
  54:   
  55:              //2、使用Except()的方法——差集
  56:              var studentWithExcept = mathNames.Except(englishNames);
  57:              Console.WriteLine("2、使用Except()的方法——差集的结果(数学>80且英语<80):");
  58:              foreach (var item in studentWithExcept)
  59:              {
  60:                  Console.Write("{0} ", item);
  61:              }
  62:              Console.WriteLine();
  63:   
  64:   
  65:              //3、使用Union()的方法——并集
  66:              var studentWithUnion = mathNames.Union(englishNames);
  67:              Console.WriteLine("3、使用Union()的方法——并集的结果(数学>80或英语>80):");
  68:              foreach (var item in studentWithUnion)
  69:              {
  70:                  Console.Write("{0} ", item);
  71:              }
  72:              Console.WriteLine();
  73:   
  74:             
  75:             ////////////////////////////////////////////////////////////////////////////
  76:              Console.ReadKey();
  77:   
  78:          }
  79:      }
  80:      class Student
  81:      {
  82:          public int Id { set; get; }
  83:          public int ClassId { get; set; }
  84:          public string Name { set; get; }
  85:          public float MathResult { set; get; }
  86:          public float EnglishResult { set; get; }
  87:      }
  88:      class ClassRoom
  89:      {
  90:          public int Id { set; get; }
  91:          public string Name { set; get; }
  92:   
  93:      }
  94:  }

结果:

1、使用Intersect()的方法——交集的结果(数学>80且英语>80):
张三 李五 王一
2、使用Except()的方法——差集的结果(数学>80且英语<80):
李四 张五 张八 张九 李二
3、使用Union()的方法——并集的结果(数学>80或英语>80):
张三 李四 张五 张八 张九 李二 李五 王一 张六 张一 张七 王三 张二

2、联合查询(join()的方法

   1:              ////////////////////////////////////////////////////////////////////////////
   2:              var queryResults =
   3:                  from s in students
   4:                  join c in classes on s.ClassId equals c.Id
   5:                  where s.MathResult>90
   6:                  select new { ClassName = c.Name, StudentName = s.Name, s.MathResult, s.EnglishResult, AllResult = s.EnglishResult + s.MathResult };
   7:              foreach (var item in queryResults)
   8:              {
   9:                  Console.WriteLine(item);
  10:              }
  11:              ////////////////////////////////////////////////////////////////////////////


结果:

{ ClassName = 二班, StudentName = 张八, MathResult = 93, EnglishResult = 77, AllResult = 170 }
{ ClassName = 一班, StudentName = 张九, MathResult = 91, EnglishResult = 67, AllResult = 158 }
{ ClassName = 二班, StudentName = 李五, MathResult = 91, EnglishResult = 82, AllResult = 173 }
{ ClassName = 一班, StudentName = 王一, MathResult = 96, EnglishResult = 97, AllResult = 193 }


说明与想法:

1、    join c in classes on s.ClassId equals c.Id 它的格式是join….on 条件(注意是用equals连接)完全可以类比于sql的join

         s.ClassId equals c.Id中s和c的位置不能调换不然会出错,即 c.Id equals s.ClassId 是错的

   //至于select new{}可以随心所欲的使用得到之间想要的字段

2、记得数据库中还有左连接的,那么LinQ中该怎么实现呢?

在上面的classes对象中再添一条数据 new ClassRoom{Id=3,Name="三班"}

2.1、那么,讲左连接开始之前先讲下group join。还记得在使用groupby()方法的我们使用了into这个关键字。对into关键字就是将结果按分组投影到新的一个结果集。

   1:              ////////////////////////////////////////////////////////////////////////////
   2:              var queryResults =
   3:                  from c in classes
   4:                  join s in students on c.Id equals s.ClassId into stds
   5:                  select new { c,stds };//查询出结果集教室,和按教室分组的学生
   6:              foreach (var item in queryResults)
   7:              {
   8:                  Console.WriteLine(item.c.Id+" "+item.c.Name+"有学生:");
   9:                  foreach(var s in item.stds)
  10:                  {
  11:                      Console.Write("  {0}",s.Name);
  12:                  }
  13:                  Console.WriteLine("");
  14:              }
  15:              
  16:              ///////////////////////////////////////////////////////////////////////////

结果:

1 一班有学生:
  张三  李四  张五  张一  张七  张九  李二  王一  张二
2 二班有学生:
  张六  张八  李五  王三
3 三班有学生:

      通过此例从结果可以清楚的看到:含有into子句被join子句分组连接(特别注意下上面的stds是一个集合)。分组联接产生分层数据结构,它将第一个集合中的每个元素与第二个集合中的一组相关元素进行匹配。在查询结果中,第一个集合中的元素都会出现在查询结果中。如果第一个集合中的元素在第二个集合中找到相关元素,则使用被找到的元素,否则使用空。可以看到结果集中 3 三班有学生: 是空的。讲到这里你是否看到了左连接的影子了吗?

       三班有学生:这个结果是空的就是证据。以上稍作调整即可,ok继续添代码

2.2、左外链接 left join

   1:              var queryResults =
   2:                  from c in classes
   3:                  join s in students on c.Id equals s.ClassId into stds
   4:                  from cs in stds.DefaultIfEmpty(new Student{Name="无"})//如果教室中没有学生就new出一个学生
   5:                  select new { c, cs };//查询出结果集教室,学生名字
   6:              foreach (var item in queryResults)
   7:              {
   8:                  Console.Write(item.c.Id + " " + item.c.Name + "有学生:");
   9:                  Console.Write("  {0}", item.cs.Name);
  10:                  Console.WriteLine("");
  11:              }
  12:   
  13:              /////////////////////////////////////////////////////////////////////////////

结果:

1 一班有学生:  张三
1 一班有学生:  李四
1 一班有学生:  张五
1 一班有学生:  张一
1 一班有学生:  张七
1 一班有学生:  张九
1 一班有学生:  李二
1 一班有学生:  王一
1 一班有学生:  张二
2 二班有学生:  张六
2 二班有学生:  张八
2 二班有学生:  李五
2 二班有学生:  王三
3 三班有学生:  无

     从结果中可以看到左外连接与SQL语句中的LEFT JOIN子句比较相似,它将返回第一个集合中的每一个元素,而无论该元素在第二个集合中是否具有相关元素。

注意:

      LINQ查询表达式若要执行左外连接,往往与DefaultIfEmpty()方法与分组连接结合起来使用。如果第一个集合中的元素没有找到相关元素时,DefaultIfEmpty()方法可以指定该元素的相关元素的默认元素。

我们可以看到Left Outer Join 的语法进一步的复杂化了,结果也有细微的不同。

比较group join 和 left join:

(1).从语法上(left join比group join多了下面一句):

from cs in stds.DefaultIfEmpty(new Student{Name="无"})

查询方法DefaultIfEmpty 用于定义当查询记录为空时,预定义默认值。再从其集合中取出子元素。

      (2).从结果上:

我们在遍历查询结果时可以发现Left Join相似于Inner Join结果都是“平面”的,然而Group Join返回的结果具有层次性(stds是一个集合,需要遍历才可看到元素)。

 

附注:                    


      完工,联合查询还有很多的奥秘,有空继续探究。以上都是常用的




TAG: