技术开发 频道

老树新芽 体验Visual Basic 9.0新功能(3)

【IT168 技术文档】

  查询综合
 
  查询综合提供了一个和SQL非常类似的语言集成语法来进行查询,不但和VB融合的很好,而且和新的.net语言集成查询框架平滑结合。
 
  熟悉SQL实现的人就会发现,在当前的.NET框架的序列操作符中,许多关系代数的操作符如发射,选择,叉乘,分组和排序这些查询处理中的查询符号。
 
  因为查询综合的语义是通过把它们转化为序列操作符来定义的,这些操作符将会和任何在此范围内的序列操作符绑定。这暗示了通过引进一个特殊的实现,查询综合语法可以有效地被用户重新绑定。特别的,查询综合可以和一个使用Dlinq基础结构的序列操作符实现重新绑定,或者和试图分布式的在多个本地或者远程的数据源执行查询的一个本地查询优化器绑定。这种序列操作符的重新绑定和经典的COM提供者模型在本质上类似,这样相同接口上的不同实现可以无须修改底层应用代码来支持多个操作和部署。

  基本的Select...From...Where...综合过滤出所有满足Where子句中条件的值,以下是一个例子显示如何查找到人口少于100万的国家:

Dim SmallCountries = Select Country _ From Country In Countries _ Where Country.Population < 1000000
  在一个序列操作符中,操作符It和当前"行"绑定。It的成员如Me是自动进入这个范围的。It的概念和XQuery的上下文项"."相关联,它可以像SQL中"*"中一样被使用。比如,我们想使用如下的查询返回所有的国家和其首都的集合对象:
Dim CountriesWithCapital = _  Select It _   From Country In Countries, City In Capitals _   Where Country.Name = City.Country
  
  这个本地声明的推断类型是IEnumerable(Of { Country As Country, City As City }).
 
   使用Order By子句,我们可以根据任何数量的排序关键字对查询结果排序。下面的例子中,查询语句按照它们的经度升序和人口降序的方式返回所有的国家名字列表:
Dim Sorted = Select Country.Name _  From Country In Countries, City In Capitals _  Where Country.Name = City.Country  Order By City.Longtitude Asc, Country.Population Desc
  
  集合操作符如Min, Max, Count, Avg, Sum...操作集合对象并"聚合"它们成一个单值。我们可以计算所有的小国家:
Dim N As Integer = _  Select Count(Country) _  From Country In Countries _  Where Country.Population < 1000000
  
  像SQL一样,我们提供了特殊的聚合语法,这对同时进行多个聚合操作是非常方便的。比如,我们可以使用一条语句,统计所有的小国家数目,并且计算它们的平均人口密度:
Dim R As { Total As Integer, Density As Double } = _  Select New { .Total = Count(Country), _   .Density = Avg(Country.Population/Country.Area) } _  From Country In Countries _  Where Country.Population < 1000000
  
  这种形式的聚合对应用一个编译器产生的聚合函数对没有进行聚合的自然结果集合的结果是非常方便的。聚合函数一般经常出现在分割源集合对象时使用。举例来说,我们可以根据是否处于热带来对所有国家进行分组,然后统计每个组的数目。为了这样做,我们引进一下Group By子句。IsTropical是对判断国家是否处于热带的一个封装函数:
Partial Class Country  Function IsTropical() As Boolean   Return TropicOfCancer =< Me.Latitude _    AndAlso Me.Latitude >= TropicOfCapricorn  End Function End Class
  
  通过这个函数,我们可以使用和上面一样的聚合,但是首先得按照Country.IsTropical得结果是否一样来对输入得Country和Capital对的集合对象进行分割。在这个例子里有两个这样的分类:一个包含热带国家帕劳群岛,伯利兹城和马达加斯加岛,另一个包含非热带国家摩纳哥。
 
   关键字             国家    城市
          Country.IsTropical() = True  Palau   Koror
   Country.IsTropical() = True  Belize   Belmopan
   Country.IsTropical() = True  Madagascar Antanarivo
   Country.IsTropical() = False  Monaco   Monaco
 
  然后,我们通过计算总数和平均密度来对这些组进行值的聚合。返回类型是一个对Total As Integer和 Density As Double的集合对象:
Dim CountriesByClimate _  As IEnumerable(Of Total As Integer, Density As Double }) =_ Select New { .Total = Count(Country), _  .Density = Avg(Country.Population/Country.Area) } _  From Country In Countries, City In Capitals _  Where Country.Name = City.Country  Group By Country.IsTropical()
  
  以上的查询隐藏了相当复杂的操作,因为Group By子句的结果实际上是一个组值的集合对象,其类型是IEnumerable(Of Grouping(Of { Boolean, { Country As Country, City As City })),和上面讨论的很相象。每个这样的Grouping项包含一个从key提取表达式Country.IsTropical()衍生的Key成员和一个包含单个key提取表达式值相同的国家和城市的集合对象。VB编译器合成用户定义的聚合函数,即给定一个组,通过聚合每个分割来计算所要的结果。
 
    注意前面的例子里每个Group同时包含Country和Capital,我们只需要Country来计算最后的查询结果。Group By子句允许这些组的一个预先选择。比如,我们可以根据国家的气候对国家名进行分类:
Dim ByHemisphere As IEnumerable(Of Grouping(Of Boolean, String)) = _  Select It _    From Country In Countries, City In Capitals _    Where Country.Name = City.Country    Group Country.Name By City.Latitude >= 0
  
  这会返回集合对象{ New Grouping { .Key = False, .Group = { "Madagascar", "Belize" }}, New Grouping { .Key = True, .Group = { "Palau" }}。
 
  查询综合在Visual Basic 8.0里是完全组合式的,意思就是说查询综合可以任意的嵌套。组合特性使得通过每个孤立的子表达式来理解大的查询变得很容易。组合特性也使得定义语义和语言的规则变得更加容易。组合特性,作为一个设计原则,和SQL的设计原则有所不同。SQL语言不是完全组合的,程序员还必须通过日积月累的社区里的经验积累来处理很多个例。由于却反完全的组合特性,所以通过个体片断来理解一个复杂的SQL查询总体上来说是不可能的。
 
  SQL缺乏组合特性的原因是关系数据模型就不是组合式的。比如,表可能不包含子表;也就是说,所有的表都必须是平面的。结果,SQL程序员只能写单片的结果为平面表的表达式来适应SQL的数据模型,而不是将大的表达式分解为小的。引用Jim Gray的话来说"计算机科学里任何不是递归的东西都不是好的"因为Visual Basic是基于CLR类型系统,并没有限制哪种类型作为其他类型的组件出现。除去静态类型规则,并没有限制表达式作为其他类型的组件出现。因此,不但是行,对象,XML,还有活动目录,文件,注册表项等等都是查询源和查询结果的"一等公民"。

 

0
相关文章