Evil 域

当Evil遇上先知

导航

VB9.0新特性之LINQ(三) - Join和Group

Posted on 2008-07-12 11:33  Saar  阅读(2268)  评论(0编辑  收藏  举报

《VB9.0新特性之LINQ(二) - 常用关键字》一文中,我们涉及到了LINQ中最基本的、针对于单表的查询关键字。这些“调味品”在日常使用中已经足够。但我们对美食的追求无止境,让我们来看看LINQ调味品中的两剂猛料——Join和Group。
用Join之前,我们先懂明白,什么是Join,什么时候用Join。“Join就是SQL中的INNER JOIN”。好了,会SQL的朋友下一小节可略去,以下全是废话了
Join,中文叫做“连接”,在LINQ中,特指内连接。简单的说,就是把两个关系(或者称两个表)做迪卡尔乘积,然后,根据筛选条件筛选出值相等的记录。 是不是很绕啊?还是来看个例子好了:
 

关系A

A

111

A

222

B

333

C

444

关系B

A

123

C

456

根据第一列做迪卡尔积得到4*2行结果:

A

111

A

123

<- A1+B1

A

222

A

123

<- A2+B1

B

333

A

123

<- A3+B1

C

444

A

123

<- A4+B1

A

111

C

456

<- A1+B2

A

222

C

456

<- A2+B2

B

333

C

456

<- A3+B2

C

444

C

456

<- A4+B2

筛选出列相同的结果:

A

111

A

123

A

222

A

123

C

444

C

456


再一句话,两个表中找出你感兴趣部分相同的记录并拼接起来。好,让我们来实战演习一把。
本系列一开始就引入了一个Notebook类,用来抽象笔记本电脑(具体参考VB 9.0新特性之局部类型推理(Local Type Inference) 一文中对Notebook类的定义)。为了形成两个关系,现在假定笔记本要暑促——就是打折销售啦。但是,今天这个打折,明天那个打折,折扣又不定,我们把它放到一个字典中来管理:

1        'Create a dictionay act like another table
2        Dim cutPricesDict = New Dictionary(Of StringDouble)
3        cutPricesDict.Add("Lenovo"0.8)
4        cutPricesDict.Add("DELL"0.75)

我们建立了一个cutPriceDict的Dictionary的对象,用来记录打折品牌以及其折扣率。这样一来,我们就可以查询出所有正在打折的本本的信息:
1        'All cut off laptops
2        Console.WriteLine(vbCrLf & "All cut off laptops")
3        Dim allCutOffs = From aLaptop In nbStorage, cutPriceObj In cutPricesDict _
4                       Where aLaptop.Brand = cutPriceObj.Key _
5                       Select aLaptop
6
7        For Each laptop In allCutOffs
8            Console.WriteLine(laptop.ToString())
9        Next

输出结果如下:
All cut off laptops
Laptop Lenovo(X300) is 
1.8kg and at the price of 27000
Laptop DELL(D630) is 
2.5kg and at the price of 7300

为什么得出这样的结果应该还是比较容易理解的:nbStorage中虽然有4款本本,但是,在降价中的却只有Lenovo和DELL,通过内连接,就只出现了两款本本的结果。等等,这个结果好像跟Join没有什么关系啊,哪儿用于Join了?哪儿都没有用到。嘿嘿,少安毋躁。孔乙己说,“回香豆的回有四样写法”……啊~ 回过来说,这个Join和Where之间是可以互相转换的,我们马上就让它变成Join:
 1'All cut off laptops
 2        Console.WriteLine(vbCrLf & "All cut off laptops")
 3        'Dim allCutOffs = From aLaptop In nbStorage, cutPriceObj In cutPricesDict _
 4        '               Where aLaptop.Brand = cutPriceObj.Key _
 5        '               Select aLaptop
 6        Dim allCutOffs = From aLaptop In nbStorage Join cutPriceObj In cutPricesDict _
 7                        On aLaptop.Brand Equals cutPriceObj.Key _
 8                        Select aLaptop
 9
10        For Each laptop In allCutOffs
11            Console.WriteLine(laptop.ToString())
12        Next
13

 第6至第7行,From关键字后跟出了两个数据源:nbStorage和cutPricesDict,它们之间用Join连接。紧随其后的是On关键字,用来引出“感兴趣”的列,即迪卡尔积后需要相等的列。Equals就见名知意啦,要求aLaptop.Brand和curPriceObj.Key相同——这个就是筛选条件啦。

细心的朋友可能已经发现了,这样做:
* 输出的结果中价格并没有打折
* 输出结果中只涉及到了一张表的数据
* 其实用Join没有带来什么操作上的方便性,用In关键字都能实现

嗯,那让我们来涉及一下查询结果中两表的运算,让我们把折扣价算出来:

 1        'Inner Join with join keyword
 2        Console.WriteLine(vbCrLf & "Inner Join with join keyword")
 3        Dim cutLaptops2 = From aLaptop In nbStorage Join cutPriceObj In cutPricesDict _
 4                          On aLaptop.Brand Equals cutPriceObj.Key _
 5                          Select New Notebook With {.Brand = aLaptop.Brand, _
 6                          .Price = aLaptop.Price * cutPriceObj.Value, _
 7                          .Type = aLaptop.Type, _
 8                          .Weight = aLaptop.Weight}
 9        For Each laptop In cutLaptops2
10            Console.WriteLine(laptop.ToString())
11        Next

我们看到第5行,输出结果仍然是一个Notebook类的对象,其品牌仍然是aLaptop对应的品牌、型号仍然是aLaptop对应的型号……大家注意一下第6行代码,这里新的.Price等于aLaptop.Price * cutPriceObj.Value。还记得cutPriceObj.Value里存的是什么吗?对了,折扣率。因此,新的价格已经诞生:
Inner Join with join keyword
Laptop Lenovo(X300) is 
1.8kg and at the price of 21600
Laptop DELL(D630) is 
2.5kg and at the price of 5475

好,笔记本打折到此为止。家长们常常把同一地区不同品牌的大学放在一起为孩子选择这一区的品牌高校;超市常常把同一品牌不同剂量的产品摆在相同货柜上供顾客选择;我们常常把品牌相同的笔记本放在一起挑选败家的目标:-)……分组,是数据应用中相当重要的一种分检比较的方法。因此,我们来看LINQ中分组关键字GROUP。
首先贴出代码及运行结果:
 1        'Group by
 2        Console.WriteLine(vbCrLf & "Group By")
 3        Dim laptopsByBrand = From aLaptop In nbStorage _
 4                             Select aLaptop _
 5                             Group By aLaptop.Brand _
 6                             Into lapsInGroup = Group
 7
 8        For Each brandGroup In laptopsByBrand
 9            Console.WriteLine("Brand : {0}", brandGroup.Brand)
10            For Each laptop In brandGroup.lapsInGroup
11                Console.WriteLine(vbTab & laptop.ToString())
12            Next
13        Next

运行结果:
Group By
Brand : Lenovo
        Laptop Lenovo(X300) is 
1.8kg and at the price of 27000
Brand : HP
        Laptop HP(V3742TU) is 
2.3kg and at the price of 7500
        Laptop HP(DV2730TX) is 
2.2kg and at the price of 7800
Brand : DELL
        Laptop DELL(D630) is 
2.5kg and at the price of 7300
Press any key to continue . . .


代码第3行,指定选择的数据源,当然依然是nbStorage。第4行的Select说明了要分组的对象;第5行Group By说明分组的条件;第6行,Into ... = Group说明把Select的对象分组到哪个访问器中去,方便查询执行时进行访问。
对于这样一个组里套了本本的层次结构,需要一个两重循环用于输出:
第一重,输出brandGroup的Brand属性,这样就会循环输出查询设计时指定的Group By的内容;
第二重,对于每一个品牌,输出对象的本本的对象。

Join和Group By关键字就简单介绍到此。东西不多,但因为涉及到多表或者层次操作,可能不那么容易理解。再加上我本身能力有限,不能把它们写得很通俗,如果有不妥之处,还望大家不吝赐教。