具有对丰富数据类型的支持的可扩展查询语言的制作方法

文档序号:6454290阅读:274来源:国知局

专利名称::具有对丰富数据类型的支持的可扩展查询语言的制作方法相关申请的交叉引用本申请要求2006年3月20日提交的美国临时申请第60/784,510号的优先权。背景数据已成为几乎每一应用程序中的重要资产,不论该应用程序是例如浏览产品并生成定单的业务线(LOB)应用程序,还是对各人之间的会议进行时间安排的个人信息管理(PIM)应用程序。应用程序正日益成为数据中心的—它们可以围绕对数据的查询、操纵和呈现来规划其设计和运行时体验的重要部分。这些应用程序中的许多都处理语义上丰富的数据,如结构完整性、数据约束、数据之间的关系等等。当今的应用程序在过程性代码上花费了大量努力来保存数据语义。例如,考虑LOB应用程序。通常,这一应用程序处理顾客(Customer)、定单(Order)、定单行(OrderLine)、供应商(Supplier)、产品(Product)、雇员(Employee)、运货商(Shipper)、发票(Invoice)等等。这些概念中的每一个都表示一具有特定结构的单独的丰富数据类型。例如,顾客类型具有如顾客ID(CustomerID)、公司名称(CompanyName)、联系人名字(ContaceName)以及地址(Address)等内容;定单类型具有如定单ID(OrderID)、顾客ID(CustomerID)、定单日期(OrderDate)、定单行(OrderLine)、到期日(DueDate)等内容。以上的任一个都可具有进一步的要求,例如,地址可能需要邮政编码(PostalCode),而邮政编码当在美国内时必须是五字符长且每一字符是0到9之间的数字的邮递区号。在加拿大,邮政编码必须是“ANANAN”的形式,其中A是字母而N是数字。因此,当对邮政编码建模时,仅仅指定它是串是不够的,必须对该串施加附加的约束以限制其可取的值的范围。此外,在数据之间通常存在关系。例如,定单可以始终具有与其相关联的顾客;这是多(定单)对一(顾客)关系。产品和供应商承载着多对多关系,因为多个产品可由一个供应商提供,而多个供应商可以携有相同的产品。一种数据模型描述了应用程序感兴趣的各个数据的结构和语义以及数据之间的关系。尽管关系型模型和系统在数据管理方面已经非常成功,但是它们仍无法捕捉应用程序数据模型。传统的客户机—服务器应用程序将对其数据的查询和持久操作转交给数据库系统。数据库系统对行和表格形式的数据进行操作,而应用程序按照诸如类和丰富数据类型等更高级编程语言构造来对数据操作。应用程序和数据库层之间的数据操纵服务中的阻抗失配在传统系统中是容许的。随着面向服务的体系结构(SOA)、应用程序服务器和多层应用程序的出现,对与编程环境有良好集成并且能在任何层中操作的丰富数据访问和操纵服务的需求也大量增长。大多数应用程序和应用程序框架在基于关系型数据模型的系统之上运行其自己的数据模型以桥接数据和应用程序编程环境之间的阻抗失配。这是因为大多数应用程序,不论是LOB、PIM、信息工作者还是其它应用程序,都需要如丰富结构、关系、行为和可扩展性等数据模型概念。这些数据模型概念并未得到现有数据模型的适当支持,并且此外,如果数据曾是根据更高级的数据模型来组织的,则当前并不存在对于访问此类数据的适当的查询语言。对于数据元模型的示例性现代候选包括结构化查询语言1999版(SQL99)、公共语言运行库(CLR)、统一建模语言(UML)和XML模式定义(XSD)。然而,CLR是一种面向对象的命令式编程运行库,并且没有本机数据模型,也没有完整性约束、关系或持久性的概念。SQL99缺少如关系等数据建模概念,并且没有良好的编程语言集成。XSD规范不支持如关键字、关系和持久性等概念,并且是复杂的,且到运行库和关系型数据库模型的映射难以使用。UML太过一般它要求应用程序开发者添加精确的语义,尤其是出于持久性的目的。本领域中对于提供对丰富数据类型的更好的应用程序访问的数据模型和相应的支持框架存在未满足的需求。对于具有对这一数据模型可支持的丰富数据类型的支持的可扩展查询语言也存在进一步的需求。概述考虑到上述本领域的缺点,本发明提供了一种具有对丰富数据类型的支持的可扩展查询语言。此处讨论所提供的查询语言的各种示例性特征。此外,系统可包括被配置成根据查询语言来生成查询的应用程序。还提供了一种用于处理根据所提供的查询语言生成的查询的平台。该平台一般包括对象服务层、映射提供者层、以及最终可以与各种市场上可购买的数据库接口的桥层。附图简述用于根据本发明的具有对丰富数据类型的支持的可扩展查询语言的系统和方法将参考附图来进一步描述,附图中图1示出了根据此处所提供的系统和方法的供应用程序访问数据的应用程序支持体系结构。图2示出了CDP体系结构的主要组件,且特别地描述了可以进行使用以下更详细讨论的用于丰富数据类型的可扩展查询语言做出的查询的查询流水线。图3示出了类似于使用根据本发明的计算的方法生成的友好分组视图的按照日期视图的Outlook组。详细描述在以下描述和附图中描述了某些具体细节,以提供对本发明的各个实施例的全面理解.然而,通常与计算和软件技术相关联的某些公知细节将不在以下公开中描述,来避免对本发明的各实施例的不必要的混淆。而且,相关领域的普通技术人员可以理解,他们可以无需以下描述的细节中的一个或多个而实现本发明的其它实施例。最后,尽管在以下公开中参考了步骤和序列来描述各个方法,但是如此的描述是为了提供本发明的实施例的清楚实现,且步骤以及步骤序列不应被认为是实现本发明所必需的。首先,该描述提供了可以在结合了如此处所描述的具有对丰富数据类型的支持的查询语言的系统和方法的示例性实施例中使用的示例性数据模型和相关数据支持机制。接着,强调并讨论了此处所述的示例性查询语言的各种新颖特征和方面。用于示例性查询语言的详细规范在附录A中阐明,而其扩展在附录B中阐明。该详细实现包括本领域的技术人员将理解的大量新颖且有用的特征。示例性数据模型和相关数据支持机制一个示例性数据模型和相关数据支持机制可被结合到诸如用于托管代码的ActiveX数据对象(ADO.NET)平台等一组技术中,该平台被设计成提供对诸如结构化查询语言(SQL)Server等数据源,以及通过用于数据库的对象链接和嵌入(OLEDB)和可扩展标记语言(XML)展示的数据源的一致访问。数据共享消费者应用程序可以使用ADO.NET来连接到这些数据源,并检索、操作和更新数据。ADO.NET清楚地将来自数据操纵的数据访问分解为可单独或协作使用的分立组件。ADO.NET包括用于连接到数据库、执行命令并检索结果的.NET框架数据提供者。这些结果或者被直接处理,或者被置于ADO.NET数据集(DateSet)对象中以便结合来自多个源的数据用特别的方式展示给用户或在各层之间遥控。ADO.NET数据集对象还可独立于.NET框架数据提供者使用来管理对应用程序本地或发源自XML的数据。因此,ADO.NET向编写托管代码的开发者提供了与由本领域的技术人员熟悉的ActiveX数据对象(ADO)技术提供给本地COM开发者的功能相类似的功能。在一个实施例中,ADO.NET平台可被扩展以便为跨诸如PIM框架和LOB框架等各种应用程序框架的应用程序提供用于以与应用程序编程环境良好集成的方式来访问、操纵和管理数据的一组丰富的数据服务。图1示出了该功能在应用程序支持体系结构中的放置。公共数据平台(CDP)100可以实现诸如ADO.NET平台等一组技术。公共数据平台(CDP)100和相关技术在美国专利申请11/171905中有详细讨论。图1的应用程序支持体系结构可包括例如,诸如SQL或数据库等数据源110;为应用程序和应用程序框架提供丰富数据服务的CDP100;扩展并扩充CDP100的功能的一组框架服务,例如UAF120和LOB框架130;封装框架功能和一般的应用程序逻辑的一组数据类,例如122、132、140;以及消费由CDP100和框架120、130和/或类122、132、140提供的功能的任意数量的应用程序150、160、170。CDP100支持的数据模型可包括,例如由华盛顿州雷蒙德市的微软公司开发的实体数据模型(EDM)。EDM扩展了关系型数据模型以适应诸如LOB、PIM、管理等多个应用程序框架。EDM为数据定义了丰富的对象抽象,对诸如数据关系等丰富语义建模,最小化了应用程序结构和数据模型之间的失配,支持特定的应用程序行为,支持基本关系概念、具有继承的丰富类型以及关系,并且一般提供了捕捉独立于数据存储和开发层的数据语义的建模概念。EDM可以被结合到诸如ADO.NET等技术中。图2示出了CDP100体系结构的主要组件,且特别地描述了可以进行使用以下更详细讨论的用于丰富数据类型的可扩展查询语言做出的查询的查询流水线。此处将使用术语“eSQL”来指用于丰富数据类型的可扩展查询语言,并且其一个实施例在以下详细阐明。图2还示出了通过CDP100的不同组件的查询流程。首先,应用程序200向对象服务提供者210发出查询作为eSQL查询。对象服务提供者210可以包括分析该查询并将其转换成正则树的解析器服务211,以及在正则树上执行执行任何映射转换(从应用程序数据模型到此处提供的EDM)的映射变换212。对象服务提供者然后可以将该正则树向下传递到映射提供者220。映射提供者220封装对象关系映射功能。它包括在正则树上执行映射转换的映射变换222。映射提供者将该正则树传递到用于中介服务的桥230。桥230可以包括分解正则树并执行任何数据模型补偿,然后将一个或多个正则树向下传递给存储提供者240的查询补偿服务组件231。存储提供者240将该正则树转换成其本机方言,例如转换成诸如SQL2000或SQL2005等SQL方言或WinFS或嵌入式数据库格式。存储提供者240执行查询,并返回消息,例如可被格式化以便被传递给数据读取器接口(“DataReader”)或从该数据读取器接口传递到桥230的消息。桥230包括在必要时组装来自存储提供者240返回的可能多个数据读取器的结果的结果/值组装服务232。由232执行的该操作的结果是按照EDM空间的单个数据读取器。映射提供者220接着仅仅将数据读取器从桥230返回给对象服务提供者210。对象服务210将来自映射提供者220的结果转换到对象空间。对象服务提供者221包括可任选地将结果物化为对象并将对象高速缓存在身份映射中的组件213。最后,应用程序200消费所得的数据读取器。更具体地转向EDM的几个突出方面,EDM一般根据四个核心概念类型、实例、实体和关系来构造。这些概念可以使用典型的LOB应用程序的示例来示出。这一应用程序处理不同种类的数据,例如定单、顾客、定单行、地址、供应商、产品、雇员等等。在对EDM的示例性使用中,Customer(顾客)数据可被认为是实体。实体表示应用程序用于工作的顶层数据单元。Customer可具有若干字段CustomerID(顾客ID)、CompanyName(公司名称)、ContactName(联系人名字)、Address(地址)和Phone(电话)。每一字段具有一类型,该类型确定了进入该字段的数据的结构。例如,CustomerID可以是固定长度的串。CompanyName和ContactName字段也可以是串类型。Customer本身也可具有类型,因为Customer是实体,该类型可以被称为实体类型。Address字段可以不同于其它字段它拥有诸如City(城市)、State(州)和PostalCode(邮政编码)等其它字段形式的内部结构。在EDM中,诸如Address等字段的类型被称为复杂类型。相反,CustomerID、CompanyName和ContactName的类型都是简单类型。Phone字段可以由几个电话号码组成,每一电话号码都是一个串。这被称为类集(collection)类型。类型指定了数据的结构以及对值的特定约束。实际的数据被储存在这些类型的实例中。熟悉面向对象的编程的任何人都将得到明显的类推类型和实例分别类似于类和对象。Customer和Address两者在其都具有内部结构的意义上是相似的它们都是由多个字段组成的。但是在语义上和操作上,Customer不同于Address。Customer担当对于查询、数据改变操作、事务、持久性和共享的单元。另一方面,Address始终生存在Customer内,并且不能被引用或以其它方式独立操作。在EDM中,将此类顶层数据单元称为实体。所有其它数据都被认为内联到实体。现在考虑示例性Order数据。业务规则可能要求每一定单都具有相应的顾客。这是通过Order实体和Customer实体之间的关系来建模的。EDM支持不同种类的关系。Order和Customer之间的关系被称为关联。关联通常用于对实体之间的对等关系建模。每一定单可以由几个定单行组成。例如,如果在AMAZON.上预定了五本书,则关于每本书的信息是一定单行。这被建模为另一种关系,即合成。合成内的每一OrderLine都是一个实体。eSQL的示例性新颖特征接着,强调并讨论示例性查询语言的各种新颖特征和方面。一般而言,如以下提供的SQL语言及其扩展可以在诸如以上参考图1和2所述的系统中实现,但是本领域的技术人员可以理解,与以下示例性实施例相关联的各种新颖系统和方法也可以在其它上下文中实现。以下所述的eSQL规范包括各种新颖特征,这些特征包括但不限于,按照诸如EDM等具有对丰富数据类型的支持的数据建模语言(丰富数据建模语言)来表示查询和数据操纵语言语句(DML—此处所使用的术语“查询”指的是查询和DML)、表示按照丰富数据建模语言来指定查询和DML的编程方式的正则命令树(C树)、通过操纵正则查询来补偿特定提供者的桥功能、使用视图展开来优雅地统一跨所有丰富数据建模语言的OR映射策略与查询和更新。以及通过从元数据驱动的扩展来扩展核心查询语言的能力。另外,以下所述的eSQL规范的示例性新颖方面包括如下内容。对类集的一级支持此处提供的示例性eSQL实施例被设计为与SQL相类似,并且提供了优于SQL的优点。一般而言,SQL的早先版本(SQL-92和更早的版本)严重地以表为中心。表作为一级公民来对待,而行/列作为二级来对待。并且当然,没有类集的概念。SQL-99和较后的方言提供了对类集的支持,但是这一支持是对SQL-92的改进。例如证据,如去嵌套、应用等的沉闷的添加。相反,在一个实施例中,eSQL将类集作为一级实体来对待。例如,类集表达式在from(来自)子句中是合法的。无需使用去嵌套句法。“in”(在……中)和“exits”(存在)子查询被广义化以处理任何类集—子查询仅仅是一种类集。“e1ine2”和“exists(e)”是执行这些操作的eSQL构造。此外,许多集合运算(并、交、除外)在类集上操作。联接也在类集上操作。所有的一切都是表达式标准SQL总是具有两层系统—子查询(表)和表达式(行、列)。在一个实施例中,为了提供对类集和嵌套类集的一级支持,eSQL采用了更干净的模型—所有一切都是表达式。例如,以下都是合法的eSQL查询1+2*3“abc”row(1asa,2asb){1,3,5}e1unionalle2distinct(e1)对子查询的统一处理从其以表为中心的世界观进行操作,SQL通常趋向于执行对子查询的上下文解释。例如,在SQL中,from子句中的子查询被认为是一多重集(表),而在select(选择)子句中使用的同一子查询被认为是标量子查询。类似地,在in运算符左侧使用的子查询被认为是标量子查询,而右侧的被认为是多重集子查询。在一个实施例中,eSQL消除了这些区别。表达式具有不依赖于使用该表达式的上下文的统一解释。eSQL可将所有的子查询认为是多重集子查询。如果从子查询中期望标量值,则eSQL可提供对类集(在这一情况下为子查询)操作的element(元素)运算符,并且从类集中提取单元素值。避免了不必要的隐式强制上述问题的一个相关副作用是子查询到标量值的隐式转换的概念。具体地,在SQL中,具有单个字段的行的多重集被隐式地转换成其数据类型是该字段的类型的标量值。相反,eSQL的实施例不支持这一隐式强制。eSQL可提供element运算符以从类集中提取单元素值,并提供selectvalue(选择值)子句以避免在查询表达式期间的行包装。选择值—避免隐式行包装SQL在某种程度上关于对来自查询的结果的处理是含糊的。SQL子查询中的select子句隐式地创建了围绕该子句中的项的行包装。这当然意味着不能创建变量或对象的类集—每一类集是行的类集(如有必要,有一个字段)。SQL通过允许具有一个字段的行类型与同一数据类型的单元素值之间的隐式强制来规避这一问题。在一个实施例中,eSQL提供了selectvalue子句来跳过隐式行构造。在selectvalue子句中只能指定一个项。当使用这一子句时,围绕select子句中的项并没有构造任何行包装,并且可产生所需形状的类集。eSQL还提供了row构造函数来构造任意的行。因此,selectvaluerow(e1,e2,...)完全等价于selecte1,e2,...。在另一实施例中,select的语义可避免对单个项的情况的行构造,并且在需要时显式地使用row构造函数。如果select子句包含多个项,则它将继续如在SQL中那样工作。一种替换且更优雅的方法是不允许形式为“selecte1,e2from...”的构造,而是迫使人们总是使用“selectrow(e1,e2)from...”。左相关和别名在标准SQL中,给定范围内的表达式(如select、from等的单个子句)不能引用早先在同一范围内定义的表达式。SQL的某些方言,包括T-SQL不支持from子句中这些有限形式的表达式,但是使用这些构造的句法是沉闷的,且需要apply(应用)和unnest(去嵌套)操作。在一个实施例中,eSQL广义化了from子句中的左相关,并且统一地对待它们。from子句中的表达式可以引用同一子句中较早的定义(左边的定义),而无序特殊的句法。eSQL还对涉及group-by(按……分组)子句的查询施加了额外的限制。此类查询的select子句、having(具有)子句等中的表达式只能经由其别名来引用group-by关键字。在SQL中为合法的如下构造在eSQL中是非法的selectt.x+t.yfromTastgroupbyt.x+t.y在eSQL中做这一步的正确方法是selectkfromTastgroupby(t.x+t.y)ask引用表(类集)的列(特性)在一个实施例中,eSQL中的所有列引用必须用表别名来限定。以下构造(假定“a”是表“T”的一个合法列)在SQL中是合法的,但在eSQL中则不是selectafromTeSQL批准的形式为selectt.aasafromTast未限定的引用有几个问题。可能存在歧义。其在模式进展期间遭受外部/内部捕捉问题,并且除此之外,它们使实现更复杂。SQL中设计良好的查询已经使用了限定列引用来致力于解决这些问题。eSQL所做的是简单地强制实施这一方针,并且这并没有使该语言更复杂。通过对象的导航SQL使用表示法“.”来引用表的(行的)列。在一个实施例中,eSQL扩展了这一表示法(再一次很大程度地借用了编程语言)来支持通过对象特性的导航。例如,如果“p”是Person(人)类型的表达式p.Address.City是用于引用该人的地址的城市的eSQL句法。SQL的许多方言已经支持了这一句法。通过类集的导航SQL没有提供通过嵌套类集导航的简便机制。在一个实施例中,eSQL提供了处理这些情况的句法快捷方式。SQL中的..运算符允许投影来自类集的表达式。例如,“a..b”实际上是对“selectvaluet.bfromaast”的句法美化。类似地,“a..b..(c*2)”是对“selectvaluet2.c*2fromaast1,t1.bast2”的句法美化。eSQL中的“.?”运算符允许用户从类集中剪除元素。这类似于XPath中的“[]”运算符。形式为“a.?p”的构造实际上是对“selectvaluetfromaastwherep”的快捷方式。例如,“p.?(id=1)”实际上意味着“selectvaluep0frompasp0wherep0.id=1”。当用继承来工作时,能够从超类型实例的类集中选出一子类型的实例通常是有用的。eSQL中的oftype(类型为)运算符(类似于C#序列中的oftype)提供了这一能力。逻辑上,oftype(c,T)等价于“selectvaluetreat(xasT)fromcasxwherexisofT”。废除*SQL支持未限定的*句法作为对于整个行的别名,并支持限定的*句法(t.*)作为对该表的字段的快捷方式。另外,SQL允许包括空值的特殊count(*)(计数)聚合。SQL中设计良好的查询不使用这些句法(至少不使用“select*”和“selectt.*”变体)。使用“*”在存在模式进展的情况下是危险的。该问题的一部分是SQL没有使用户能选出整个行的方式。在一个实施例中,eSQL不支持“*”构造。形式为“select*fromT”和“selectT1.*fromT1,T2...”的SQL查询可以在eSQL中分别被表达为“selectvaluetfromTast”和“selectvaluet1fromT1ast1,T2ast2…”。另外,这些构造优雅地处理了继承(值可置换性),而“select*”变体被限于声明的类型的顶层特性。eSQL的实施例也不支持count(*)聚合。相反,它支持count(group)构造来处理这一情况。对按……分组的改变如先前所描述的,在一个实施例中,eSQL支持group-by关键字的别名,以及这些关键字之间的左相关。实际上,select子句和having子句中的表达式必须经由这些别名来引用groupby关键字。group-by子句对每一group-by分区隐式地产生一nest聚合—该表达式被称为“group”。select-list等中的聚合函数应引用这一表达式来执行聚合。例如selectk1,count(group),sum(group..(t.a))fromTastgroupbyt.b+t.cask1是用于以下SQL查询的eSQL句法selectb+c,count(*),sum(a)fromTgroupbyb+c基于类集的聚合SQL聚合是难以理解的。在一个实施例中,eSQL支持两种聚合。基于类集的聚合在类集上操作,并产生聚合的结果。这些可在查询中的任何地方出现,并且不要求对group-by子句的需求。例如,以下eSQL查询是合法的selectt.aasa,count({1,2,3})asbfromTast在一个实施例中,eSQL还支持SQL样式聚合,并且将其隐式地转换成基于类集的聚合(基于“group”(组)类集)。例如,以下eSQL中的查询是合法的selecta,sum(t.b)fromTastgroupbyt.aasa;并且被内部转换成以下形式的查询selecta,sum(group..(tb))asbfromTastgroupbyt.aasa;在一个实施例中,eSQL不支持count(*)聚合。改为使用count(group)构造。插入在一个实施例中,eSQL的INSERT..VALUES语句不同于T-SQL。与T-SQL不同,eSQL不允许在插入中指定列列表。对于此有两个原因。首先,EDM没有列的默认值的概念;其次,列列表方法并不很好地适用于处理继承(值可置换性)。删除、更新与T-SQL不同,在一个实施例中,eSQL不允许delete(删除)和update(更新)语句中额外的from子句。对于delete语句,这并没有问题,因为查询可以用子查询来书写。然而,对于update语句,额外的from子句还有助于产生将在Set(设置)子句中使用的新值。提升的特性和方法查询语言(WinQL)允许经由“.”运算符通过类集的导航,如果类集本身没有相同名称的特性的话。WinQL还允许经由“[]”构造过滤掉类集的元素—类似于OPath。在一个实施例中,eSQL对此目的使用“..”和“.?”运算符。再一次,模式进展(以及内部捕捉)是eSQL选择在类集导航和对象导航之间进行区分的主要原因。并且eSQL特意避免了对谓词使用“[]”来避免歧义问题。按……排序语义WinQL规定orderby(按……排序)子句在select子句之前处理。这不同于SQL,在SQL中,orderby子句在逻辑上是在select子句之后处理的。在一个实施例中,eSQL在这一点上可能更接近SQL,而WinQL采用更类似XQuery的方法。任一方法都是合理的,并且在某些方面,WinQL模型更好;然而,WinQL方法可能不足以更好地证明对SQL用户的行为中的变化。SQL-92实际上将orderby子句约束为仅包含对select子句中的项的引用。大多数实现允许orderby子句引用当前范围中的其它项。在一个实施例中,eSQL可以遵循后一样式。除了此处明确阐述的具体实现之外,考虑此处所公开的说明书,其他方面和实现将对本领域的技术人员是显而易见的。本说明书和所示的实现旨在被认为仅仅是具有所附权利要求书的真正范围和精神的示例。附录A用于示例性查询语言的详细规范以下是用于此处所构想的示例性查询语言的示例性但非限制性规范。以下规范之后的是用于对这一查询语言的扩展的示例性规范。1功能摘要1.1问题空间描述CDP提供了可由应用程序和框架消费的对象关系型映射基础结构。WinFS提供了用于各种各样的桌面应用程序的集成存储体验。WinFS和CDP都是构建在实体数据模型(EDM)之上的。EDM是支持基本关系概念、具有继承的丰富类型和关系的扩展的关系型数据模型。用户需要发出对其按照EDM表达的数据的丰富查询的能力。尽管SQL历史上已经是用于数据库访问的所选择的查询语言,但是它具有许多缺点。缺少对丰富类型的支持、非正交构造等仅仅是这些缺点中的几个。eSQL试图解决SQL的这些问题中的许多问题。eSQL被设计为类似于SQL,并且提供了某些附加的基础结构和对SQL的改进。WinFS和CAP都利用了现有的.NET数据访问技术,即ADO.NET来访问和操纵数据。.NET数据提供者提供了一种经由连接(Connection)、命令(Command)、数据读取器(DataReader)和其它此类对象从任何数据源访问数据的略微统一的方式。对.NET数据提供者的命令被表达为串,并且必须采用提供者(更具体而言,提供者所面对的数据源)的本机方言。作为CDP/WinFS工作的一部分,将提供三个新的提供者—对象提供者、EDM提供者和WinFS提供者,并且所有这些都将使用eSQL作为本机方言。2概览和设计原理2.1设计原理对类集和对象的一级支持eSQL必须使在其对象和类集上无缝地查询对用户而言是容易的。正交性eSQL中的构造必须是正交的。对给定构造的使用应当没有上下文解释。可组成性eSQL中的构造必须可在其它构造内组成。与SQL的相似性eSQL应当尽可能地保持为类似SQL的语言。除非偏离SQL是必要的(例如,当以上原因要求时),否则eSQL必须保持与SQL构造的逼真度。3语言概览eSQL包括表达式的概念。如在其它编程语言中一样,表达式是可被求值以产生值的构造。eSQL查询是任何合法的表达式。eSQL提供了以下各种表达式。3.1文字如在大多数编程语言中一样,文字仅仅是一常量值。eSQL提供了对串和数字文字以及特殊的空值文字(具有自由浮动数据类型)的本机支持。3.2参数eSQL中的参数允许表达式(和查询)引用在该查询外部定义的值。3.3变量引用eSQL中的变量引用允许表达式引用早先已经在同一范围中定义(且命名)的其它表达式。3.4内置运算符eSQL提供了一小组内置运算符。这包括如+、-、*、/的算术运算符、比较运算符(<、>、……)、逻辑运算符(and(和)、or(或)、not(非)等)以及几个杂项运算符(case(情况)、between(之间)等)。3.5函数eSQL并未定义其自己的任何内置函数。eSQL函数表达式允许使用任何用户定义的函数(对eSQL可见的)。3.6对象导航、方法这些eSQL表达式允许自然地引用和操纵对象特性和方法。3.7聚合函数与其SQL对应物一样,eSQL中的聚合函数允许类集上的folding(折叠)运算。eSQL支持nest聚合,以及任何用户定义的聚合。eSQL聚合函数是基于类集的,因为它们可以在表达式中的任何地方使用。eSQL还支持类似SQL的基于组的聚合,它们仅可在查询表达式中使用。3.8构造函数eSQL提供了三种构造函数。Row构造函数可用于创建匿名的、结构上类型化的(记录)值。Object构造函数(C#等中的构造函数)可用于构造(命名的)用户定义的对象。Multiset构造函数可用于构造其它值的多重集。3.9类型操作eSQL提供了允许查询并操纵表达式(值)的类型的操作。这包括如isof(类似于C#中的is表达式)等询问操作、如treat(C#中的as表达式)和oftype(C#序列中的OfType方法)的子类型-超类型强制转换、以及如cast的转换运算符。3.10集合运算符eSQL提供了一组丰富的集合运算。这包括如union、intersect、except和exists的类似SQL的集合运算符。另外,eSQL支持用于重复消除(distinct)、成员资格测试(in)、联接(join)的运算符。3.11查询表达式查询表达式表示经典的SQLselect-from-where-...查询。4语言细节—eSQL表达式4.1文字Null(空值)null文字用于表示任何类型的空值。null文字被认为是自由浮动的,即,它可与任何类型兼容。类型化的空值可通过null文字经由Cast运算符来创建selectFoo(null,v.a+v.b)fromvaluesasv—常规的空值文字selectcast(nullasIntl6)fromvaluesasv—类型化的空值参见9.6节以得到关于可以在何处使用自由浮动null文字的规则。Boolean(布尔)boolean文字由关键词true(真)和false(假)来表示。整型(Int32、Int64)整型文字的类型可以是Int32或Int64。Int32文字是一系列数字字符。Int64文字是一系列数字字符后跟大写的L。Decimal(十进制)定点数(decimal)是一系列数字字符、小数点“.”以及另一系列数字字符后跟大写的M。Float(浮点)、Double(双精度)双精度浮点数是一系列数字字符、小数点“.”以及另一系列数字字符可能后跟指数。单精度浮点数(或float)是双精度浮点数句法后跟小写的f。String(串)串是包围在引号中的一系列字符。引号可以是单引号(‘)或双引号(“)。引用的标识符可以用[]或““来表示。默认地,仅[]样式是允许的,并且““构造作为串文字来对待。eSQL解析器可允许““样式的引用标识符(这将由解析器选项来控制—这是在本文的范围之外的);在这一情况下,串文字只能使用单引号。参见9.1.2节以得到关于此的更多信息′hello′″x″″Thisisastring!″′soisTHIS′其它文字eSQL本机不支持其它数据类型的文字(DateTime(日期时间)、Guid等)。4.2参数参数是通常经由主机语言使用的绑定API在查询语言外部定义的变量。每一参数具有名称和类型。参数名用“@”符号作为名称前的前缀来描述,以将其与特性名称或查询内定义的其它名称区分。主机语言绑定API提供了用于绑定参数的API。以下示例示出了在表达式中对参数的使用selectcfromcustomersascwherec.Name=@name4.3变量变量表达式是对早先在当前范围中定义的(命名)表达式的引用。变量引用必须是如在6中定义的有效标识符。以下示例示出了在表达式中对变量的使用selectcfromcustomersasc4.4内置运算符eSQL提供了多个内置运算符,如下。4.4.1算术算术运算符是对诸如Int32、Int16、Int64、Double、Single以及Decimal等数字原语来定义的。对于二进制运算符,在应用该运算符之前对操作数进行隐式类型提升。所有算术运算符的结果类型与提升的操作数类型相同。也对串定义了加法并且加法担当串接。4.4.2比较比较运算符是对诸如Byte、Int32、Int16、Int64、Double、Single以及Decimal、String和DateTime等数字原语来定义的。在应用比较运算符之前对操作数进行隐式类型提升。比较运算符总是产生布尔。相等和不等是对布尔类型以及具有等式的任何对象类型定义的。具有等式的非原语对象在其共享同一等式时被认为是相等的。4.4.3逻辑逻辑运算符是仅对Boolean类型定义的—并且总是返回Boolean类型。4.4.4Case(情况)case表达式具有与TSQL的case表达式相似的语义。case表达式用于做出一系列条件测试以确定哪一表达式将产生适当的结果。如果b1,b2...,bn-1是类型为Boolean的表达式,而e1,e2,...,en是某一类型S的表达式,则以下表达式是产生类型S的单个值的有效case表达式。casewhenb1thene1whenb2thene2…[elsevn]end这一形式的case表达式应用了一系列一个或多个Boolean表达式来确定正确的所得表达式。求值为值true的第一个when表达式产生相应的then表达式。其余表达式不被求值。如果没有一个when条件得到满足,则结果是求值else表达式(如果存在该表达式)的结果或null(如果没有else表达式)。注意,在不同结果表达式(e1,e2,...en,vn)之间可发生隐式类型提升以确定case表达式的实际结果类型。4.4.5Between(之间)between表达式具有与SQL的between表达式相同的语义。它确定一给定表达式是否得到下限和上限之间的值。e1betweene2ande3如果e1、e2和e3都是某一类型T的表达式,则以上表达式是有效的between运算符表达式。between表达式是在同一值上写两个比较运算符的缩写。between句法在e1为复杂表达式时是便利的。e1>=e2ande1<=e3notbetween表达式可用于指示between表达式的逆运算e1notbetweene2ande3e1<e2ore1>e34.4.6like(像)like表达式具有与对TSQL描述的相同的语义。它确定一个串是否匹配一模式,如果该串匹配则解析为true,否则解析为false。如果match、pattern和escape都是具有类型String的表达式,则以下是产生Boolean值的有效like表达式matchlikepatternmatchnotlikepatternmatchlikepatternescapeescapematchnotlikepatternescapeescapepattern(模式)串句法与TSQL的pattern串句法相同。4.4.7空值测试(isnull)isnull运算符用于确定一表达式是否得到值null。如果该表达式得到值null,则该表达式得到值true。否则,该表达式得到值false。如果e是一表达式,则以下表达式是产生Boolean的有效表达式eisnulleisnotnull使用该isnull运算符来确定一外联接的元素是否不为空selectcfromcsascleftouterjoinasdsdwheredisnotnullandd.x=@x使用该isnull运算符来确定一成员是否具有实际值selectcfromcsascwherec.xisnotnull4.5成员访问成员访问通常也被称为点运算符。使用成员访问运算符来产生对象实例的特性或字段的值。如果m是具有类型M的类型T的成员,而t是类型T的实例,则t.m是产生类型M的值的合法成员访问表达式。selectp.Name.FirstNamefromPersonasp;4.6方法启用方法启用是对一方法的显式调用。它产生方法调用的结果。方法可以是类型的实例方法、类型的静态方法或全局静态方法,即独立函数。如果m是返回类型M的类型T的方法,且具有类型(P1,P2...,Pn)的参数,而t是类型T的实例且e1是类型P1的表达式,依此类推,则t.m(e1,e2....,en)是产生类型M的值的有效方法启用。如果m是返回类型M的类型T的方法,且具有类型(P1,P2...,Pn)的参数,而qn是引用类型T的限定名且e1是类型P1的表达式,依此类推,则qn.m(e1,e2,...,en)是产生类型M的值的有效静态方法启用。如果m是返回类型M的类型T的方法,且具有类型(P1,P2,...,Pn)的参数,而qn是引用该方法的名字空间前缀的限定名且e1是类型P1的表达式,依此类推,则qn.m(e1,e2...,en)是产生类型M的值的有效静态方法启用。可在eSQL查询中出现的方法启用的一个示例可能是使用内置到原语数据类型的方法selecttsql.substring(c.Name,0,2)fromcustomersc4.6.1重载解析一个以上方法可以用相同的名字来定义。为了确定给定表达式引用了哪一方法,遵循这些规则。逐个应用的仅产生单个方法的第一条规则是所解析的方法。1.存在相同数量的参数的方法。2.其中每一自变量类型与参数类型完全匹配或为null文字的方法。3.其中每一自变量类型与参数类型完全匹配或是参数类型的子类型或自变量是null文字的方法。具有最少数量的子类型转换的方法获胜。4.其中每一自变量类型与参数类型完全匹配或是参数类型的子类型或可以被提升到参数类型,或者自变量是null文字的方法。具有最少数量的子类型转换和提升的方法获胜。5.方法启用表达式是有歧义的。注意,即使单个方法可以使用这些规则来提取,自变量也仍有可能不匹配参数。如果情况如此,则自变量出错。4.7枚举枚举是按照名字来引用的强类型化的整型数字值。枚举使用引用枚举的类型的限定名后跟点运算符和枚举的名字来指定。如果qn是对某一枚举E的限定名,而en是该枚举的值的名字,则qn.en是有效枚举引用Color.Red4.8类型运算eSQL提供了操作表达式值的类型的多种运算。4.8.1IsOfIsof表达式检查一表达式是否为一指定类型的实例。eisof(T)eisnotof(T)eisof(onlyT)eisnotof(onlyT)如果e是某一编译时类型s,而S是T的子类型/超类型,则以上表达式是有效表达式,并返回类型Boolean的结果。如果e的类型在运行时被确定为T(或T的某一子类型),则eisofT返回True。如果e在运行时为null,则该表达式返回null;否则该表达式的结果为False。如果指定了only修饰符,则如果e完全是类型T而不是其任何子类型,则该表达式返回true。表达式eisnotof(T)和eisnotof(onlyT)在句法上分别等价于not(eisof(T))和not(eisof(onlyT))。如果S既不是T的子类型也不是其超类型,则该表达式引发编译时出错。4.8.1.1Treattreat表达式试图按照子类型/超类型来对一给定表达式重新定类型。treat(easT)如果e具有类型S而T是S的子类型或者S是T的子类型,则以上表达式是有效的treat表达式并产生类型T的值。如果S和T都不是彼此的子类型,则该表达式导致编译时出错。如果e具有类型Employee且Manager是Employee的子类型,则以下表达式产生相同的e值,但是被类型化为Manager而非Employeetreat(easManager)如果e的值实际上不是类型Manager,则该表达式产生值null。4.8.1.2Castcast(强制类型转换)表达式具有与TSQL的convert表达式相似的语义。cast表达式用于将一种类型的值转换成另一种类型的值。该语言仅支持某些特定类型的转换cast(easT)如果e是某一类型S而S可被转换为T,则以上表达式是有效的cast表达式。T必须是原语(标量类型)。对cast表达式的使用被认为是显式转换。显式转换可以截断数据或丢失精度。eSQL支持的有效强制转换的列表在10.5节中描述。4.8.1.3OfTypeOfType表达式指定了被发出以执行针对类集的每一元素的类型测试的类型表达式。OfType表达式产生仅包含要么等价于指定类型要么是该指定类型的子类型的元素的指定类型的新类集。如果ts是类型collection<S>的类集,而T是S的子类型,则OfType(ts,T是类型collection<T>的表达式,其产生ts中e的个别类型Te是T的子类型的所有元素e的类集。OfType表达式是以下查询表达式的缩写selectvaluetreat(tasT)fromtsastwheretisofT假定Manager是Employee的子类型,则以下表达式产生仅来自雇员(employee)类集的经理(manager)的类集。OfType(employees,Manager)还有可能使用类型过滤器来向上强制转换类集。OfType(executives,Manager)由于所有执行官都是经理,因此所得的类集仍包含所有原始执行官,尽管该类集现在被类型化为经理的类集。不可能做出已知的非法类型强制转换。以下表达式将引发编译时出错。(当然,假定Address不是Employee的子类型/超类型)OfType(employees,Address)4.9引用eSQL支持以下用于引用的运算符。4.9.1RefRef(引用)运算符在被应用于用于实体集(entityset)的相关变量(correlationvariable)时产生对来自该类集的实体的引用。例如,以下查询返回对每一Order的引用selectref(o)fromOrdersaso以下是ref运算符的非法使用—相关变量必须解析为当前范围中的实体集selectref(o)from(selectvalueofromOrdersaso)aso4.9.2CreateRefCreateRef(创建引用)运算符可用于制造对实体集中一实体的引用。该运算符的第一个自变量是实体集标识符(不是串文字),而第二个自变量是对应于实体类型的关键字特性的记录类型的表达式selectref(BadOrders,row(o.Id))fromOrdersaso记录类型的表达式必须在结构上等价于用于实体的关键字类型,即它必须具有与实体关键字的相同次序的相同数量和类型的字段—字段的名字无关紧要。4.9.3KeyKey(关键字)运算符是CreateRef运算符的逆运算,并且可以用于提取具有类型ref的表达式的关键字部分。Key运算符的返回类型是记录类型—其一个字段用于该实体的每一关键字,且以相同的次序selectkey(ref(BadOrders,row(o.Id)))fromOrdersaso4.9.4DerefDeref(解除引用)运算符解除引用值的引用,并产生该解除引用的结果selectderef(o.CustRef)fromOrdersaso如果r是类型ref<T>的引用,则Deref(r)是产生由r引用的实体的类型T的表达式。如果引用值为null,或者是悬空的,即引用的目标不存在,则Deref运算符的结果为null。4.9.5属性解除引用可以经由“.”运算符来导航通过引用。以下摘录通过导航通过CustRef特性来提取(Customer的)Id特性。selecto.CustRef.IdfromOrdersaso如果引用值为null,或者是悬空的,即引用的目标不存在,则结果为null。4.10构造函数eSQL提供了3种构造函数—行构造函数、对象构造函数和多重集构造函数。4.10.1行构造函数行构造函数(row)可用于从一个或多个值构造匿名的、结构上类型化的记录。行构造函数的结果类型是行类型—其字段类型对应于用于构造该行的值的类型。例如,以下表达式row(1asa,″abc″asb,a+34asc)产生类型Record(aint,bstring,cint)的值。行构造函数中的所有表达式都必须有别名—如果没有提供别名,则eSQL试图经由在0中指定的别名规则来生成别名。行构造函数中的表达式不能引用早先(在左边)在同一构造函数中定义的别名。同一行构造函数中的两个表达式具有相同的别名是非法的。可使用点(.)运算符来从记录中提取字段(类似于提取对象的特性)。4.10.2命名类型构造函数(命名类型初始化器)eSQL允许使用命名类型构造函数(初始化器)来创建命名复杂类型和/或实体类型的实例。以下表达式创建了Person类型的实例(假定Person类型具有这两个属性)person(″abc″,12)该构造函数的自变量假定为与类型的属性的声明是相同的次序。4.10.3类集构造函数多重集构造函数(multiset)从值列表中创建多重集的实例。该构造函数中的所有值必须是互相兼容的类型T,并且该构造函数产生类型Multiset<T>的类集。以下表达式创建整型多重集multiset(1,2,3){1,2,3}4.11集合运算本节包含各种集合运算。4.11.1Distinctdistinct(区分)表达式用于通过产生移除了所有重复引用的新类集来将对象类集转换成集合。如果c是类型collection<T>的类集,则distinct(c)是产生没有重复的类型collection<T>的类集的有效distinct表达式。类型T必须是可进行相等比较的。distinct表达式是select表达式的缩写distinct(c)selectvaluedistinctcfromc4.11.2Flattenflatten(展平)表达式用于将类集的类集转换成具有所有相同元素、仅仅没有嵌套结构的展平类集。如果c是类型collection<collection<T>>的类集,则flatten(c)是产生类型collection<T>的新类集的有效flatten表达式。flatten表达式是使用select表达式的缩写flatten(c)selectvaluec2fromcasc1,c1.itasc24.11.3Existsexists(存在)表达式具有与TSQL的exists表达式相同的语义。它确定一类集是否为空。如果该类集不为空,则exists表达式返回true,否则返回false。如果c是类型collection<T>的类集,则exists(c)是产生类型Boolean的单个值的有效exits表达式。4.11.4Inin(在内)运算符用于测试一值在类集中的成员资格。在右边的由该表达式所表示的类集中搜索该表达式中在“in”关键词左边的值。如果e是具有类型T的表达式而ts是具有类型Collection<S>的类集,并且S和T具有超类型/子类型关系,则以下表达式是有效的in运算符表达式eintsenotints在以下示例中,在顾客名字的集合中搜索名字“Bob”′Bob′incustomerNames如果一值被发现为在类集中,则in运算符产生值true。如果值为null或类集为null,则in运算符产生值null。否则,该表达式产生值false。该运算符的not(否)形式产生相反的结果。4.11.5Union,UnionAlleSQL中的union(并)和unionall(并全部)表达式具有与其TSQL等价表达式相同的语义。UnionAll产生包含两个类集的组合内容(具有重复)的类集。union产生移除了重复的类集。如果c1是类型Collection<T>的类集,而c2是类型Collection<S>的类集,其中M是T和S的公共超类型,则c1unionc2和c1unionallc2是产生类型Collection<M>的类集的有效union表达式。以下示例标识了或者生活在华盛顿,或者是好顾客的所有顾客的集合。第一个表达式消除了任何重复(例如,也生活在华盛顿的好顾客),而第二个表达式保留所有重复goodCustomersunionWashingtonCustomersgoodCustomersunionallWashingtonCustomers4.11.6IntersectIntersect(交)表达式具有与TSQL的intersect表达式相同的语义。它确定两个类集的交。如果c1是类型Collection<T>的类集,而c2是类型Collection<S>的类集,其中M是T和S的公共超类型,则c1intersectc2是产生类型Collection<M>的类集的有效intersect表达式。以下示例标识了生活在华盛顿且是好顾客的所有顾客的集合。goodCustomersintersectWashingtonCustomers4.11.7ExceptExcept(除外)表达式具有与TSQL的except表达式相同的语义。它确定两个类集的单向差别。如果c1是类型Collection<T>的类集,而c2是类型Collection<S>的类集,其中M是T和S的公共超类型,则c1exceptc2是产生类型Collection<T>的类集的有效except表达式。在以下示例中,标识不是坏顾客的所有顾客的集合allCustomersexceptbadCustomers4.11.8Overlapsoverlaps(重叠)表达式确定两个类集是否具有公共成员。如果c1是类型Collection<T>的类集,而c2是类型Collection<S>的类集,其中M是T和S的公共超类型,则c1overlapsc2是产生类型Boolean的结果的有效overlaps表达式。以下示例检查任何华盛顿顾客是否是好顾客WashingtonCustomersoverlapsgoodCustomers并且是以下表达式的句法快捷方式exists(WashingtonCustomersintersectgoodCustomers)4.11.9Elementelement(元素)表达式从单元素类集中提取元素。如果c是类型collection<T>的类集,则element(c)是产生类型T的实例的有效element表达式。以下示例试图从坏顾客集合中提取单个元素element(badCustomers)如果类集为空,或具有多于一个元素,则element表达式返回null。4.12类集导航eSQL提供了允许更容易地处理类集的多个句法构造。4.12.1投影..(集合投影)运算符用于将元素通过类集投影出来。如果c是类型Collection<T>的类集,而p是具有类型Q的T的特性,则c..p产生类型Collection<Q>的类集。更一般地,如果c是类型Collection<T>的类集,而e是类型Q的某一表达式,则c..e产生包含对c的每一元素求值e的结果的类型Collection<Q>的类集。以下示例得到所有部门的大写名字Departments..(upper(name))并且是对以下查询的快捷方式selectvalueupper(d.name)fromDepartmentsasd4.12.2过滤.?(集合过滤器)运算符用于将元素从类集中过滤掉。如果c是类型Collection<T>的类集,而e是具有类型Q的布尔表达式,则c.?e产生仅包含c中满足谓词e的那些元素的类型Collection<T>的类集。以下示例得到以西雅图为基础的所有部门的集合Departments.?(location=′Seattle′)并且是对以下查询的快捷方式selectvaluedfromDepartmentsasdwhered.location=′Seattle′4.12.3展平投影...(展平的集合投影)运算符用于将元素通过嵌套类集投影出来。更一般地,如果c是类型Collection<Collection<T>>的类集,而e是类型Q的某一表达式,则c...e产生包含首先展平c,然后对c的每一元素求值e的结果的类型Collection<Q>的类集。以下示例获得所有雇员的大写名字,假定Employees是Department的有集合值的特性Departments.Employees...(upper(Name))并且是对以下查询的快捷方式selectvalueupper(e.name)fromDepartmentsasd,d.Employeesase4.12.4展平的过滤..?(展平的集合过滤器)运算符用于将元素从嵌套类集中过滤掉。更一般地,如果c是类型Collection<Collection<T>>的类集,而e是类型Q的某一表达式,则c..?e产生包含首先展平c,然后过滤调结果中不满足e的那些元素的结果的类型Collection<T>的类集。以下示例获得其薪水大于10000的所有雇员的集合,假定Employee是Department的有集合值的特性Departments.Employees..?(salary>10000)并且是对以下查询的快捷方式selectvalueefromDepartmentsasd,d.Employeesasewheree.salary>10000定范围规则在以上所有情况中,当导航通过类型Collection<T>的类集时,创建一新的范围,并且将当前类集元素绑定到变量it。为了句法上的方便起见,T的特性(和方法)也被假设为在范围中,但是它们被认为是对通过it变量的成员访问的快捷方式。例如Departments..NameDepartments..(it.Name)4.13聚合函数聚合是产生一系列输入值(通常但不必定变为单个值)的表达式。它们一般结合select表达式的groupby子句使用,并且具有对它们实际可在何处使用的约束。每一聚合运算是对一个或多个类型定义的。eSQL没有定义聚合的集合。它仅仅依赖于周围的元数据空间来处理聚合函数。基于类集的聚合基于类集的聚合是在一组特定值上计算的聚合。例如,假定orders是所有定单的类集,则可以用以下表达式来计算最早发货日期min(orders..ShipDate)基于类集的聚合内的表达式使用当前的环境名字解析范围来求值。基于组的聚合基于组的聚合是在由groupby子句定义的组上计算的。对于结果中的每一组,使用每一组中的元素作为聚合计算的输入来计算单独的聚合。当在select表达式中使用groupby子句时,仅分组表达式名、聚合或常数表达式可以存在于projection或orderby子句中。以下示例计算对每一产品排序的平均数量selectp,avg(ol.Quantity)fromorderLinesasolgroupbyol.Productasp基于组的聚合仅仅是对于基于类集的表达式的快捷方式(以保留与SQL的兼容性)。以上示例实际上转换为selectp,avg(group..(ol.Quantity))fromorderLinesasolgroupbyol.Productasp其中group表达式引用了由groupby子句隐式地产生的嵌套聚合。有可能具有基于组的聚合而在select表达式中没有显式的groupby子句。所有元素将作为单个组来对待,这等价于基于常数来指定分组的情况。selectavg(ol.Quantity)fromorderLinesasolselectavg(group..(ol.Quantity))fromorderLinesasolgroupby1基于组的聚合内的表达式使用将对where子句表达式可见的名字解析范围来求值。为保留SQL外观和感觉,基于组的聚合还可在其输入上指定all或distinct修饰符。如果指定了distinct修饰符,则在计算聚合之前从聚合输入类集中消除重复。如果指定了all修饰符(或没有指定修饰符),则不执行重复消除。distinct修饰符实际上是对distinct运算符的句法缩写avg(distinctol.Quantity)avg(distinct(ol.Quantity))在基于类集的和基于组的聚合之间区分基于类集的聚合是eSQL中指定聚合的优选模式。然而,还支持基于组的聚合以对SQL用户减轻转换努力。类似地,对于类似SQL的行为支持对聚合输入指定distinct(或all)作为修饰符,但是优选机制是改为使用distinct()运算符。基于组的和基于类集的聚合之间的名字解析策略中的区别可能要求实现在两种范围下做出求值假设。策略是首先偏爱解释为基于类集的聚合,然后是基于组的聚合。4.14不支持的表达式4.14.1限定谓词SQL允许以下形式的构造sal>all(selectsalaryfromemployees)sal>any(selectsalaryfromemployees)eSQL不支持这些构造。以上表达式在eSQL中可被表达为notexists(employees.?(sal>it.salary))exists(employees.?(sal>it.salary))或notexists(select0fromemployeesasewheresal>e.salary)exists(select0fromemployeesasewheresal>e.salary)eSQL可以用限定表达式来扩展。4.14.2*SQL支持将“*”用作select子句中的句法快捷方式来指示所有列需要被投影出来。5语言细节—查询表达式查询表达式是最通用的eSQL表达式,并且对熟悉SQL的程序员是最常见的。它将许多不同的查询运算符组合在一起成为单个句法。这些运算符中的许多可以被单独指定;然而,没有一个能像在被组合成查询表达式时那样表达。除非另外指定,否则select表达式类似于TSQL的select语句来表现。查询表达式由向对象类集接连地应用运算的一系列子句组成。它们基于在标准SQLselect语句中找到的相同子句;select、from、where、groupby、having和orderby。在其最简单的形式中,查询表达式由select关键词后跟投影列表、from关键词、源表达式、where关键词以及最后的过滤条件指定。selectmfromcwheree在此示例中,c是某一类型T的对象的类集,m是具有类型M的T的成员,而e是可以引用T的一个或多个成员的Boolean表达式。该查询产生具有类型M的新对象类集。5.1from子句对于大部分,from子句具有与对TSQL描述的语义相同的语义。然而,它在句法上的限制要少得多,从而允许产生类集的任何语言表达式为合法源。from子句是一个或多个from子句项的逗号分隔的列表。from子句可用于为select表达式指定一个或多个源。from子句的最简单形式是标识类集和别名的单个表达式fromCasc一般而言,select表达式对源类集中的每一元素按序操作。这被称为迭代。选择列表或where子句中的表达式可以使用类集的别名作为引用元素的变量来引用当前元素的特性。5.1.1from子句项每一from子句项引用查询中的一个源类集。eSQL支持以下各类from子句项。5.1.1.1简单from子句项最简单的from子句项是标识类集和别名的单个表达式Casc别名指定是可任选的—以上from子句项的一种替换指定可以是C如果没有指定别名,则eSQL试图基于类集表达式来生成别名。参见稍后描述的0。5.1.1.2联接from子句项联接from子句项表示两个from子句项之间的联接。eSQL支持交叉联接、内联接、左和右外联接以及全外联接—所有这些都与TSQL相似。如同在T-SQL中一样,联接中所涉及的两个from子句项必须是独立的—它们不能相关。可对这些情况使用交叉应用(CrossApply)/外应用(OuterApply)。交叉联接crossjoin(交叉联接)表达式产生两个类集的笛卡尔积CasccrossjoinDasd内联接innerjoin(内联接)产生两个类集的约束的笛卡尔积Casc[inner]joinDasdone该表达式处理与其中on条件为true的右边类集的每一元素配对的左边类集的每一元素的组合。on条件必须始终被指定。左外联接、右外联接outerjoin(外联接)表达式产生两个类集的约束的笛卡尔积CascleftouterjoinDasdone该表达式处理与其中on条件为true的右边类集的每一元素配对的左边类集的每一元素的组合。如果on条件为false,则该表达式仍处理与具有值null的右边元素配对的左边元素的单个实例。右外联接可以用类似的方式来表达。全外联接显式fullouterjoin(全外联接)产生两个类集的约束的笛卡尔积cascfullouterjoinDasdone该表达式处理与其中on条件为true的右边类集的每一元素配对的左边类集的每一元素的组合。如果on条件为false,则该表达式仍处理与具有值null的右边元素配对的左边元素的一个实例,以及与具有值null的左边元素配对的右边元素的一个实例。备注·为保留与Sql-92、Tsql的兼容性,outer关键词是可任选的。因此,“leftjoin”、“rightjoin”和“fulljoin”是“leftouterjoin”、“rightouterjoin”和“fullouterjoin”的同义词。·ON子句必须用于内和外联接,它对于交叉联接是非法的。5.1.1.3应用from子句项eSQL支持两种应用—交叉应用(CrossApply)和外应用(OuterApply)。crossapply产生左边类集的每一元素与通过对右边表达式求值产生的类集的元素的唯一配对。采用crossapply,右边表达式是左边元素的函数selectc,ffromCasccrossapplyfn(c)asfcrossapply的行为与对联接列表所描述的相似。如果右边表达式求值为空类集,则crossapply不对左边元素的该实例产生任何配对。outerapply类似于crossapply,不同之处在于即使右边表达式求值为空类集仍产生配对selectc,ffromCascouterapplyfn(c)asf注意不同于TSql,不需要显式去嵌套步骤。5.1.2from子句中的多个类集from子句可以包含通过逗号分隔的一个以上类集指定。在这一情况下,类集被假定为联接在一起。认为这是n向交叉联接。5.1.3左相关from子句中的项可以引用早先指定的项。在以下示例中,C和D是独立的类集,而c.Names依赖于CfromCasc,Dasd,c.Namesase这在逻辑上等价于from(CascjoinDasd)crossapplyc.Namesase5.1.4语义逻辑上,from子句中的类集被假定为是n向交叉联接的一部分—退化的情况是1向交叉联接。from子句中的别名从左到右处理,并且被添加到当前范围以供稍后引用。from子句被假定为产生行的多重集—对from子句中的每一项有一个字段,并且表示来自该类集项的单个元素。在以上示例中,from子句逻辑上产生类型Row(c,d,e)的行的多重集,其中字段c、d和e被假定为是C、D和c.Names的元素类型。eSQL对范围中的每一简单from子句项引入一别名。例如,在以下查询中from(CascjoinDasd)crossapplyc.Namesase被引入到范围中的名字是c、d和e。与SQL中不同,from子句仅将别名引入到范围中。对这些类集的列(特性)的任何引用必须用别名来限定。5.2where子句where子句具有与对TSQL描述的相同的语义。它通过将源类集的元素限于通过条件的那些元素来限制由查询表达式产生的对象。selectcfromcsascwheree表达式e必须具有类型Boolean。where子句直接在from子句之后,在分组、排序或投影之后应用。from子句中定义的所有元素名都对where子句的表达式可见。5.3groupby子句groupby子句具有与对TSQL描述的相似的语义。可以指定其值用于出于对聚合求值的目的而将源元素分组在一起的一个或多个表达式selecte1,count(c.d1)fromcgroupbye1,e2,...,engroupby子句中的每一表达式必须求值为可进行相等性比较的某一类型。这些类型一般是诸如数字、串和日期等标量原语。有可能使用复杂类型作为groupby条件,只要该类型定义了相等性的概念。可能不能按照类集来分组。一旦显式或隐式地(通过在查询中的having子句)指定了group子句,当前范围就被隐藏,并且引入一新范围。select子句、having子句和orderby子句将不再能够引用from子句中指定的元素名。可以仅引用分组表达式本身。为此,可以向每一分组表达式分配新名字(别名)。如果没有为分组表达式指定别名,则eSQL试图经由0中的别名生成规则来生成别名selectg1,g2,...,gnfromcasc1groupbye1asg1,e2asg2,...,enasgngroupby子句中的表达式可能无法引用早先在同一groupby子句中定义的名字。除了分组名之外,还可在select子句、having子句和orderby子句中指定聚合。聚合包含对组的每一元素求值的表达式。聚合运算符减少所有这些表达式的值(通常但不总是减少为单个值)。聚合表达式可以引用父范围中可见的原始元素名,或引用由groupby子句本身引入的新名字中的任一个。尽管聚合出现在select子句、having子句和orderby子句中,但是它们实际上是在与分组表达式相同的范围下求值的selectname,sum(o.Price*o.Quantity)astotalfromorderLinesasogroupbyo.Productasname该查询使用了groupby子句来产生按产品分解的、所有排序的产品的成本的报告。它将名字“name”给予产品作为分组表达式的一部分,然后在选择列表中引用该名字。它还在选择列表中指定了聚合“sum”,该聚合内部地引用定单行的价格和数量。命名组组本身可具有名字。当一个组被命名时,它将引用形成组实例的对象类集的新名字引用到范围中。组本身是简单地将匹配关于组的准则的所有项聚合为一多重集的聚合,即嵌套聚合。逻辑上,groupby子句基于键以及包含该组的非键列的类集的隐式嵌套聚合(对每一组一个)来执行分组。对聚合,即基于组的聚合的后续引用将被转换成基于类集的聚合,其中所讨论的类集是由groupby子句产生的嵌套聚合selectname,sum(o.Price*o.Quantity)astotal,mygroupfromorderLinesogroupmygroupbyo.Productasname在此示例中,组mygroup在groupby子句中标识并在选择列表中引用。名字mygroup引用了形成单个组的OrderLine实例的类集。所得的类集的每一行将具有三个特性;name(名字)、total(总数)和mygroup(我的组)。不必总是对组定义自己的名字。甚至没有指定一个组也可以总是使用名字group来访问selectname,sum(o.Price*o.Quantity)astotal,groupfromorderLinesogroupbyo.Productasname5.4having子句having子句用于在分组结果上指定附加的过滤条件。如果查询中没有指定groupby子句,则假定隐式的“groupby1”—单集合组。having子句正如where子句一样工作,不同之处在于它是在groupby运算之后应用的。这意味着having子句只能引用分组别名和聚合selectname,sum(o.Price*o.Quantity)astotalfromorderLinesogroupbyo.Productasnamehavingsum(o.Quantity)>1该示例与groupby示例相同,除了having子句将组仅限于其中具有一个以上产品单元的那些组之外。5.5select子句select关键词之后的一个或多个表达式的列表被称为选择列表,或更正式地称为投影。投影的最一般形式是单个表达式。如果从类集c中选择某一成员m,则将产生对c的每一元素的所有m值的新类集selectc.mfromc例如,如果customers是具有类型为string的特性Name的类型customer的类集,则从customers中选择Name将产生串类集selectc.Namefromcustomersasc5.6行和值选择子句eSQL支持select子句的两种变体。第一种变体—行选择—由select关键词来标识,并且可用于指定应被投影出来的一个或多个项。隐式地,行包装在项周围添加,其结果是查询表达式的结果总是行的多重集一具有适当的字段。行选择中的每一表达式必须指定一别名。如果没有指定别名,则eSQL试图使用在0中描述的别名规则来生成别名。select子句的另一变体—值选择—由selectvalue关键词来标识,并且仅允许指定一个项,且不添加行包装。行选择总是可按照值选择来表达。例如select1asa,″abc″asb,a+34ascselectvaluerow(1asa,″abc″asb,a+34asc)all和distinct修饰符select的两种变体允许指定all或distinct修饰符。如果指定了distinct修饰符,则从由查询表达式产生的类集中消除重复(直到并包括select子句)。如果指定了all修饰符,则不执行重复消除。这些修饰符仅被保留为与SQL兼容。eSQL建议改为使用distinct运算符,但是支持这些变体,并且透明地转换表达式selectdistinctc.a1,c.a2fromTasadistinct(selectc.a1,c.a2fromTasa)语义select子句在from子句、groupby和having子句被求值之后才求值。select子句只能引用当前在范围中的项(经由from子句或从外范围)。如果已经指定了groupby子句,则select子句仅被允许按照键来引用组的别名。对from子句项的引用仅作为聚合函数的一部分而被准许。5.5.1与SQL的差别不支持*eSQL不支持使用*或限定*来指示整个from子句(或来自表的所有列)应被投影出去。即使是在SQL中,设计良好的查询也避免这些构造,因为它们在模式进展时可能会有意料不到的副作用。eSQL改为通过从from子句中引用类集别名来允许查询投影出整个记录。以下SQL查询select*fromT1,T2通过以下构造以eSQL更好地表达selectt1,t2fromT1ast1,T2ast25.6orderby子句orderby子句可以指定确定结果中的元素的排序的一个或多个表达式。orderby子句在逻辑上应用于select子句的结果。它可以经由其别名来引用选择列表中的项。另外,它还可引用当前在范围中的其它变量。然而,如果select子句已经用distinct修饰符来指定,则orderby子句只能引用来自select子句的别名selectcasc1fromcsascorderbyc1.e1,c.e2,...,enorderby子句中的每一表达式必须求值为可对有序不等性(小于或大于等)进行比较的某一类型。这些类型一般是诸如数字、串和日期等标量原语。orderby子句中的每一表达式可任选地指定排序准则。可指定ASC(或DESC)以指示特定表达式上需要升序(或降序)。另外,对于串类型表达式,可指定COLLATE子句以指示要使用串校对来进行排序。可使用位置指定,例如orderby3来指示选择列表上的对应(第三个)项不被支持。6语言细节—命令6.1命令eSQL命令是用于整个查询请求的术语。命令可以是诸如select表达式等查询表达式,或用于插入、删除或更新对象的语句。命令逻辑上由三部分组成。·可任选前序·可任选WITH子句·查询或DML语句。例如,想像有称为Customer的类型和名为customers的Customer实例的类集。以下都是有效命令customers.?(Name=′Bob′)selectc.Name,c.Phonefromcustomersc6.1.1命令前序(可任选)命令前序可以指定要使用的一组名字空间。这些将在稍后关于名字空间的节中描述。6.1.2WITH子句eSQL支持WITH子句作为对任何查询(或DML)语句的前缀。WITH子句的句法如下with子句::=WITH<公共表表达式>[,<公共表表达式>]*<公共表表达式>::=<简单标识符>AS<加括号的表达式>备注·<公共表表达式>定义可以引用早先在WITH子句中定义的公共表表达式。例如,以下定义查询是合法的WITHtab1as(...),tab2as(selecttfromtablast)6.2查询语句查询语句就是一表达式。6.3DML语句DML语句在下一节描述。7DMLeSQL支持插入、更新和删除DML语句。7.1.1插入eSQL中的INSERT(插入)命令与标准SQL中的INSERT语句非常相似[With子句]Insert[into]<容器>[<withparent>]FROM<表达式>[With子句]Insert[into]<容器>[<withparent>]<查询表达式>[With子句]Insert[into]<容器>[<withparent>]VALUES<表达式>语义·<容器>表示任何数据容器。实体集是最多的逻辑容器。·<表达式>/<查询表达式>是多重集(在前两种情况中),这产生要插入到容器中的值的集合。在第三种情况(VALUES子句)情况下,<表达式>是容器的元素类型。·with子句在后面的小节中定义。·withparent子句在后面的小节中描述。示例InsertintoNewCustomersselectofromOldCustomersasoInsertintoNewCustomersfromMultiset(Customer(...),Customer(...),...)--以下语句都是等价的InsertintoNewCustomersfromMultiset(Customer(...))InsertintoNewCustomersselectcfromMultiset(Customer(...))ascInsertintoNewCustomersfrom{Customer(...)}InsertintoNewCustomersvaluesCustomer(...)InsertintoNewCustomersvalues(Customer(...))备注·eSQL的INSERT..VALUES语句与T-SQL略微不同。对于一个,值周围的括号是可任选的,并且当存在时不被解释为行构造函数。·与T-SQL不同,eSQL不允许对insert语句指定列列表。7.1.2删除eSQL中的DELETE(删除)命令与标准SQL中的DELETE语句相似[With子句]Delete[from]<容器>[as<别名>][<withparent子句>][where<表达式>]语义·<容器>表示任何数据容器。实体集是最多的逻辑容器。·<表达式>是谓词,即类型为boolean。·<withparent子句>在以后定义。示例DeletefromNewCustomersasnwheren.id>10DeletefromNewCustomers备注·与T-SQL不同,eSQL中的DELETE语句不允许多个from子句。改为使用子查询。7.1.3更新eSQL中的更新语句概念上类似于标准SQL中的UPDATE语句更新语句::=[with子句]UPDATE<容器>[as<别名>][<withparent子句>][<set子句>][<apply子句>][where<表达式>]<set子句>::=SET<set子句>[,<set子句>]*<set子句>::=<字段表达式>EQUAL<表达式><字段表达式>::=<表达式>语义·<容器>表示任何数据容器。实体集是最多的逻辑容器。·<表达式>是谓词,即类型为boolean。·<字段表达式>是l值表达式,即,它可以是列引用、对列特性的引用、或涉及这一特性的TREAT表达式。·<withparent子句>在以后定义。示例UPDATENewCustomersasnSETn.name.firstName=UPPER(n.name.firstName),TREAT(n.addressasInternationalAddress).countrycode=′US′WHEREn.namelike′ABC%′备注·与T-SQL不同,eSQL中的UPDATE语句不允许多个from子句。改为使用子查询。·在UPDATE语句中属性可被修改至多一次。7.2用于EDM关系的DMLEDM引入了关系集(RelationshipSet)的概念作为关系实例的类集。关联和组成关系都经由这些关系集来建模。7.2.1关联一般的使用模式是首先创建实体的实例,然后通过插入到关系集中来创建实体之间的关系。以下示例展示了对这一关系集的DML。假定两个实体类型Order和Customer(具有实体集—Orders和customers)。还假定这两个实体之间的关系OrderCustomer,并假定OrderCustomerSet为关系集--在Order和Customer之间插入关系InsertintoOrderCustomerSetSelectOrderCustomer(ref(o),ref(c))FromCustomersasc,OrdersasoWhereo.Id=123andc.Name=′ABC′--删除Order和Customer之间的关系DeletefromOrderCustomerSetasocWhereoc.Order.Id=1237.2.2组成对应于组成的关系集也是可更新的。必须首先创建父实体实例。然而,与关联不同,没有用于创建子实例的单独步骤。对组成关系集的插入自动创建子实例。其示例包括-将新行插入到现有定单中InsertintoOrderLineSet.Linewithparent(element(selectref(o)fromOrdersasowhereo.Id=20))selectLine(...)from...--修改现有行项UpdateOrderLineSet.Lineaslwithparent(element(selectref(o)fromOrdersasowhereo.Id=20))Setl.description=...Where...--删除现有行项DeleteOrderLineSet.Lineaslwithparent(element(selectref(o)fromOrdersasowhereo.Id=20))wherel.description=...注意用于修改组成的特殊的“withparentas”子句。8eSQL杂项8.1递归查询eSQL通过允许表定义(加括号的表达式)引用自身来启用对递归查询的支持。例如WITHtab1as(multiset(1,2,3,4,5)),tab2as(multiset(1)unionallselectt2fromtab2ast2,tab1ast1wheret2=t1)selecttfromtab1asteSQL并不保证递归的终止。8.2对EDM关系的支持EDM引入了关系集的概念—这是SQL中的链接表的逻辑等价物。关系集可以恰如其它类集一样查询。另外,eSQL支持用于更容易地在关系上导航的导航助手。导航助手的一般形式如下Selecto.Id,o->OrderCustomer.CustomerFromOrdersaso其中OrderCustomer是关系的名字,而Customer是关系的顾客端的名字。8.3注释eSQL支持T-SQL样式的注释。eSQL注释可以是以下形式--这是注释--/*这是也注释*/注释可以在其中期望空白字符的任何位置处使用。·“--”(双连字号)样式的注释字符可在与要执行的代码同一行上使用,或者在单独的行上使用。从双连字号开始到行末的所有一切都是注释的一部分。·/*...*/(正斜杠星号字符对)。这些注释字符可在与要执行的代码同一行上使用,或者在单独的行上使用,或甚至在可执行代码内使用。从开始的注释对(/*)到结束的注释对(*/)的所有一切都被认为是注释的一部分。这些注释可以跨多行。9eSQL名字解析和其它规则9.1标识符eSQL中的标识符用于表示表达式别名、变量引用、对象特性、函数等。eSQL中的标识符可以有两种。9.1.1简单标识符简单标识符仅仅是字母数字(和下划线)字符的序列。标识符的第一个字符必须是字母,a-z或A-Z。9.1.2引证的标识符引证的标识符是被方括号或双引号包围的任何字符序列。默认引证是方括号。双引号只能在用于eSQL的解析器允许时才用作引证字符—控制这一允许确切的解析器的选项留给解析器判断。还要注意,当双引号可被用作引证字符时,它们不能用于串文字一串文字因此始终需要使用单引号。引证的标识符允许用一般在标识符中不合法的字符来指定标识符。方括号(或引号)之间的所有字符都被认为是标识符的一部分,包括所有的空格。引证的标识符不能包括以下字符·新行·回车·制表符·退格·[(仅在[]样式的引证的情况下)可以用一般非法的字符来发明特性名selectc.ContactNameas[ContactName]fromcustomersascselectc.ContactNameas″ContactName″fromcustomersasc可以使用引证的标识符来指定一般将被认为是语言的保留字的标识符。例如,如果类型Email(电子邮件)具有名为“from(发件人)”的特性,则可以使用方括号将其与保留字“from”区分开来selecte.[from]fromemailsase可在点运算符的右侧使用引证的标识符selecttfromtsastwheret.[aproperty]==2为使用同一字符作为标识符中的结束引证字符,两次使用它。例如selecttfromtsastwheret.[abc]]]==2标识符abc]被适当地换码。9.2大小写敏感性eSQL中的所有关键词都是大小写不敏感的(如在SQL中一样)。标识符取决于所指定的解析器选项可以是大小写敏感或大小写不敏感的。eSQL中的关键词总是大小写不敏感的。9.3别名规则eSQL建议只要需要就在查询中指定别名。对于以下构造需要别名·row构造函数的字段·查询表达式的from子句中的项·查询表达式的select子句中的项·查询表达式的groupby子句中的项·join表达式的自变量有效别名任何简单标识符或引证的标识符都是有效别名。别名生成如果没有指定别名,则eSQL试图基于几个简单规则来生成别名。·如果表达式(对其未指定别名)是简单或引证的标识符,则该标识符被用作别名。例如row(a,[b])row(aasa,[b]as[b])·如果该表达式是更复杂的表达式,但是该表达式的最后一个分量是简单标识符,则该标识符被用作别名。例如row(a.a1,b.[b1])row(a.a1asa1,b.[b1]as[b1])·否则引发编译时异常。9.4定范围规则定范围规则定义特定变量何时在查询语言中可见。某些表达式或语句引入新名字。定范围规则确定何处可使用这些名字,以及何时或何处具有与另一声明相同的名字的新声明可隐藏其前导声明。当定义名字时,它们被认为是在范围内定义的。范围覆盖了查询的区域。特定范围内的所有表达式或名字引用可以看见该范围内定义的名字。在一范围开始之前且在其结束之后,在该范围内定义的名字不能被引用。范围可被嵌套。部分语言引入了覆盖可包含同样引入范围的其它语言表达式的区域的新范围。当范围被嵌套时,可对在引用所处的最内部范围内定义的名字以及在任何外部范围内定义的任何名字做出引用。在同一范围内定义的任何两个范围被认为是兄弟范围。不能对在兄弟范围内定义的名字做出引用。如果一名字是在匹配在外部范围中声明的名字的内部范围内声明的,则在该范围内或在该范围内声明的范围内的引用仅引用新声明的名字。外部范围中的名字被隐藏。甚至在同一范围内,名字在被定义之前也可能不能被引用。全局名字可以作为执行环境的一部分存在。这可包括持久类集或环境变量的名字。包含这些名字的范围是最外面的范围。参数不在范围内。由于对参数的引用包括特殊句法,因此参数的名字不与查询中的其它名字抵触。9.4.1查询表达式查询表达式引入了一个新范围。在from子句中定义的名字被从左到右引入到from范围中以便出现。在联接列表中,表达式可以引用早先在该列表中定义的名字。在from子句中标识的元素的公有特性(字段等)不被添加到from范围中—它们必须总是经由别名—限定名来引用。通常,select表达式的所有部分都被认为是在from范围内。groupby子句也引入一个新的兄弟范围。每一组可具有引用该组中的元素的类集的组名。每一分组表达式也将新名字引入到group范围中。另外,嵌套聚合(或命名组)也被添加到范围中。分组表达式本身在from范围中。然而,当使用groupby子句时,选择列表(投影)、having子句和orderby子句被认为是在group范围而非from范围中。聚合获得特殊的对待,并且将在下文中描述。选择列表可将新名字按序引入到范围中。右边的投影表达式可以引用在左边投影的名字。orderby子句可以引用在选择列表中指定的名字(别名)。对select表达式中的子句求值的顺序确定了将名字引入到范围中的顺序。from子句被首先求值,之后是where子句、grouping子句、having子句、select子句,最后是orderby子句。聚合处理eSQL支持两种形式的聚合—基于类集的聚合和基于组的聚合。基于类集的聚合是eSQL中的优选构造,而基于组的聚合仅仅是对SQL兼容性支持的。当解析一聚合时,eSQL首先试图将其作为基于类集的聚合来对待。如果失败,则eSQL将该聚合输入变换为对嵌套聚合的引用,并试图解析该新聚合。例如avg(t.c)avg(group..(t.c))9.4.2类集过滤器/投影运算对于类集过滤器/投影运算符(..、...和..?),为投影/谓词创建一新范围,并且将名字it隐式地添加到范围中—以表示该类集的每一元素。另外,为了简明起见,类集元素的公有成员也被添加到范围中—再一次仅仅是对于投影/谓词的求值期间。例如Departments..NameDepartments..(it.Name)产生部门名的多重集—其中每一Department被假定为具有Name特性。9.5名字空间eSQL引入了名字空间以解决对于诸如类型名、实体集、函数等的全局标识符的名字冲突问题。eSQL中的名字空间支持非常类似于CLR模型。eSQL提供了可在命令前序中使用的using子句。提供了两种形式的using子句—限定名字空间(其中对名字空间提供较短的别名)和非限定名字空间usingSystem.Data.TSql;usingtsql=System.Data.TSql;名字解析规则如果一标识符不能在本地范围中解析,则eSQL试图在全局范围,即名字空间中定位该名字。eSQL首先试图将该标识符(前缀)与限定名字空间之一进行匹配。如果存在匹配,则eSQL试图解析指定名字空间中的其余标识符—如果没有找到匹配,则引发异常usingtsql=System.Data.TSql;usingwinfs=System.Storage.WinFS;selecttsql.substr(p.Name)fromPersonaspselecti.ItemIdfromItemsasiwhereiisofwinfs.ContacteSQL然后试图对该标识符注入所有非限定名字空间(在前序中指定)。如果该标识符可在仅一个名字空间中定位,则返回该位置。如果一个以上名字空间具有对该标识符的匹配,则引发异常。如果对该标识符不能标识到任何名字空间,则eSQL将该名字向外传递到下一范围(命令/连接对象)usingSystem.Data.TSql;selectsubstr(p.Name)fromPersonasp与CLR模型的区别与CLR模型的一个区别是价值呼出(worthcallingout)。在CRL中,可使用部分限定的名字空间—eSQL不允许。例如,在C#中,以下是合法的,而其等价物在eSQL中不是合法的usingSystem.Data.TSql;voidmain(){intx=Y.z.Foo();--对名字空间System.Data.TSql.Y中的类--z的方法Foo的引用}ADO.NET使用查询/DML语句经由ADO.NET命令来表达。命令可在Connection(连接)对象上构建。名字空间也可被指定为Command(命令)和Connection对象的一部分。如果eSQL不能解析查询本身内的标识符,则注入外部名字空间(经由类似的规则)。查询、Command和Connection对象形成了名字空间环—其中每一环被首先注入。9.6空值文字和类型推导如早先所描述的,空值文字与eSQL类型系统中的任何类型兼容。然而,为使空值文字的类型能被正确地推导,eSQL对可在何处使用空值文字施加了某些约束。以下规则适用。类型化的空值类型化的空值,即“cast(nullasInt16)”可在任何地方适用;对于类型推导没有进一步的需求,因为该类型此时是已知的。自由浮动空值文字自由浮动空值文字可在以下上下文中使用·作为对cast/treat表达式的自变量—这是建议的机制以产生类型化的空值表达式。·对方法或函数的自变量。(标准重载规则适用,并且如果不能选取一个重载,则引发编译出错)·作为对诸如+、-、*、/等算术表达式的自变量之一。其它自变量不能是空值文字;否则类型推导是不可行的。·作为对逻辑表达式(and/or/not)的自变量之一—所有自变量都已知是类型boolean。·作为对“isnull”或“isnotnull”表达式的自变量—表达式“nullisnull”和“nullisnotnull”是合法的(但是很愚蠢)/·作为对like表达式的一个或多个自变量—所有自变量都期望是串。·作为对命名类型构造函数的一个或多个自变量。·作为对多重集构造函数的一个或多个自变量。然而,对多重集构造函数的至少一个自变量必须是除空值文字之外的表达式。·作为case表达式中的一个或多个then/else表达式。case表达式中的至少一个then/else表达式必须是除空值文字之外的表达式。·在其它情形中不能使用自由浮动空值文字。这些情形中的某一些(非穷尽的,仅作为说明包括)包括·对行构造函数的自变量。备注isnull和isnotnull表达式被认为是特殊的。10eSQL类型系统eSQL对实体数据模型(EDM)操作,并产生其实例。本文将不深入EDM的细节;相反,调出几个特殊兴趣项。10.1行类型行(也称为元组)是按照成员资格来结构化的。行的结构取决于组成它的类型化且命名的成员的序列。行是EDM内联类型—它没有身份并且不能从中继承。同一行类型的实例在成员分别等价的情况下是等价的。行没有在其结构等价性之外的行为。行没有公共语言运行库中的等价物。查询可以得到包含行或行类集的结构。在eSQL查询和主机语言之间绑定的API将定义行如何在查询结果中实现。在ADO.NET中,行将被表现为数据记录(DataRecord)。10.2类集类集类型表示其它对象的零个或多个实例。EDM支持几种类集—包括多重集、列表等。本文仅处理多重集。10.3可空值性在EDM中,可空值性是对特性的约束,并且不是类型本身的一方面。EDM中的每一类型都是可为空值的。eSQL中的null文字被认为可与EDM中的每一其它类型兼容。10.3.1空值语义eSQL中的空值的行为与TSql中的空值非常相似。具体地,在空值上操作的多数表达式返回空值。这包括·算术表达式·逻辑表达式·类型表达式以下表达式在其一个或多个自变量为空值时不会返回空值·构造函数(类型、行、多重集)·函数/方法·isnull和isnotnull运算符。·其它10.4隐式类型提升eSQL定义了各种公共二元和一元运算符,诸如如整型、浮点和串等原语数据类型上的加、乘和求反。通常,诸如加等二元运算符在同一数据类型的两个实例上运算。然而,有时候请求运算符在不同类型的两个实例上工作是合法的;例如,将整型和浮点相加。为使其工作,两个操作数之一必须被提升到另一个的类型。一般而言,这是具有最高精度的类型,因此在转换期间没有数据损失。在某些情况下,两个操作数都被提升到第三类型以确保没有数据损失。下表列出了内置到该语言中的隐式类型提升。第一列列出了基础类型,而第二列列出了可对该类型做出的最直接的提升。例如,Int16可被提升到Int32。然而,它还可被提升到Int64,因为Int32可提升到Int64。未列出或列出为具有“无”的提升的类型不能被隐式提升。如果希望在试图将Int16加到float时知道正确的类型提升,可以查找该表以查看Int16实际将被提升到float,而float不会被提升到Int16。因此,Int16将被提升到float,使得两个float可被加到一起。一个略复杂的示例将使Int32加到float。注意,Int32不会提升到float,而float不会提升到Int32。然而,Int32和float都将提升到double。因此,两个操作数都被提升到double,并且将两个double加在一起。10.5合法类型转换以下是原语数据类型之间的合法转换的表。11主机语言绑定主机语言绑定是可用于与eSQL交互的API。eSQL被设计为以与当前TSQL被程序员使用微软的数据访问API来访问非常相似的方式经由来自主机编程语言的API访问的查询语言。概念上,被查询的数据驻留在主机语言的域外部的某一地方、在数据库或其它持久存储中、或在对正常的语言运算符不透明的数据结构中。用于eSQL的优选数据访问机制是ADO.NET。ADO.NET中的命令对象具有CommandText(命令文本)特性(可以被设为eSQL查询串)。Command对象上的Execute(执行)方法返回DataReader(支持IEnumerator接口)。DataReader在逻辑上是数据记录(DataRecord)的类集,其中数据记录表示结果的单个元素。12语法12.1命令命令::=[前序][with子句](查询|dml)前序::={名字空间声明;}*名字空间声明::=using(加别名的名字空间声明|无别名的名字空间声明)加别名的名字空间声明::=简单标识符=标识符无别名的名字空间声明::=标识符with子句::=withwith子句项列表with子句项列表::=with子句项|with子句项列表,with子句项with子句项::=简单标识符as加括号的表达式12.2DMLdml::=插入|更新|删除插入::=insert[into]表达式[withparent子句]from表达式|insert[into]表达式[withparent子句]查询表达式|insert[into]表达式[withparent子句]values表达式删除::=delete[from]加别名的表达式[withparent子句][where表达式]更新::=update加别名的表达式[withparent子句]set子句[where表达式]withparent子句::=withparent加括号的表达式set子句::=setset子句项列表set子句项列表::=set子句项|set子句项列表,set子句项set子句项::=简单set子句项简单set子句项::=表达式=表达式12.3查询表达式查询::=通用表达式通用表达式::=表达式|查询表达式表达式::=文字表达式|参数表达式|变量引用表达式|内置表达式|点表达式|构造函数表达式|方法表达式|聚合表达式|类集导航表达式|关系导航表达式|集合运算表达式|加括号的表达式表达式列表::=表达式[,表达]*加别名的表达式::=表达式as标识符|表达式加括号的表达式::=(通用表达式)文字表达式::=数字文字|串文字|布尔文字|空值文字参数表达式::=@简单标识符变量引用表达式::=标识符点表达式::=表达式.标识符方法表达式::=[表达式.]标识符([all|distinct][表达式列表])构造函数表达式::=行构造函数表达式|对象构造函数表达式|多重集构造函数表达式行构造函数表达式::=row(加别名的表达式[,加别名的表达式]*)多重集构造函数表达式::=multiset([表达式列表])|{[表达式列表]}对象构造函数表达式::=[标识符.]*标识符([表达式列表])聚合表达式::=[标识符]*标识符([all|distinct]表达式列表)内置表达式::=算术表达式|比较表达式|逻辑表达式|like表达式|isNull表达式|between表达式|case表达式|typeOp表达式|ref表达式|deref表达式|key表达式|createRef表达式算术表达式::=表达式+表达式|表达式-表达式|表达式*表达式|表达式/表达式|表达式%表达式|+表达式|-表达式比较表达式::=表达式<表达式|表达式>表达式|表达式=表达式|表达式<=表达式|表达式>=表达式|表达式<>表达式逻辑表达式::=表达式and表达式|表达式&&表达式|表达式or表达式|表达式‖表达式|not表达式|!表达式isNull表达式::=表达式is[not]nulllike表达式::=表达式[not]like表达式escape表达式between表达式::=表达式[not]between表达式and表达式deref表达式::=deref(表达式)ref表达式::=ref(标识符)createRef表达式::=createref(标识符,表达式)key表达式::key(表达式)case表达式::=casewhen列表[else表达式]endwhen列表::=when表达式{when表达式}when表达式::=when表达式then表达式else表达式::=else表达式typeOp表达式::=treat(表达式as表达式)|cast(表达式as表达式)|表达式is[not]of([only]表达式)|oftype(表达式,表达式)关系导航表达式::=表达式->标识符.标识符类集导航表达式::=表达式..表达式|表达式.?表达式|表达式...表达式|表达式..?表达式集合运算表达式::=表达式union表达式|表达式unionall表达式|表达式intersect表达式|表达式except表达式|表达式overlaps表达式|表达式in表达式|exists(通用表达式)|element(通用表达式)|flatten(通用表达式)查询表达式::=select子句from子句[where子句][groupBy子句][having子句][orderBy子句]select子句::=行选择子句|值选择子句rowSelect子句::=select[all|distinct]加别名的表达式{,加别名的表达式}值选择子句::=selectvalue[all|distinct]表达式from子句::=fromfrom子句项{,from子句项}from子句项::=简单from子句项|联接from子句项|(联接from子句项)|应用from子句项|(应用from子句项)简单from子句项::=表达式|表达式as简单标识符联接类型::=left[outer]join|right[outer]join|[inner]join|full[outer]join联接from子句项::=from子句项crossjoinfrom子句项|from子句项联接类型from子句项on表达式应用类型::=crossapply|outerapply应用from子句项::=from子句项应用类型from子句项where子句::=where表达式groupBy子句::=group[标识符]by加别名的表达式{,加别名的表达式}having子句::=having表达式orderBy子句::=orderbyorderBy子句项{,orderBy子句项}orderBy子句项::=表达式[collate校对][asc|desc]校对::=简单标识符标识符::=引证的标识符|简单标识符引证的标识符::=[unicode字符]|“unicodeCharacters“unicode字符::=unicode字符{unicode字符}简单标识符::=字母{字母|数字|_}数字文字::=int32文字|int64文字|double文字|float文字|decimal文字空值文字::=null布尔文字::=true|falseint32文字::=数字{数字}int64文字::=整型文字Ldouble文字::=数字{数字}.数字{数字}[(E|e)[+|-]数字{数字}]float文字::=double文字fdecimal文字::=数字{数字}.数字{数字}M串文字::=‘{unicode字符}‘|“{unicode字符}“附录B对示例性查询语言的扩展的详细规范1概览1.1目的本文详细描述了对用于WinFS/CDP的eSQL查询语言的扩展(函数)。eSQL提供了对启用函数的支持,但是本身不提供任何内置函数。(如+、-等运算符被不同地处理,并且被假定备份到语言本身中)。如T-SQL和SQL的其它方言等语言的用户习惯于丰富的内置函数集。本文的目的是描述eSQL可如何被扩展以解决这些需求。基本上,eSQL中的所有函数都相同地对待作为用户定义的函数。eSQL使用名字空间来以与对用于包含类型的名字空间的使用相似的方式来定义不同的函数容器。提供者可定义具有相关联的函数集的此类名字空间,并且使得它们经由元数据服务对eSQL的用户可用。本文集中于可从SQLServer和WinFS获得的特定内置函数集以及将其变得可用的名字空间。本文并未解决提供者如何提供该函数列表以及系统的其余部分如何加载它们的问题。这些扩展按照支持它们的存储提供者来分组。提供者列表如下1.SQLServer2000.2.SQLServer2005.该提供者支持所有SQL2000函数。3.WinFS.该提供者支持所有SQL2005和SQL2000函数。下表概述了功能集请参见本文的后几节以获得作为上表中的部分支持所列出的情况。1.2扩展机制关于启用扩展功能集的eSQL扩展机制的细节可以在eSQL语言规范的第9.4节(名字空间)中找到。注意,不支持部分名字空间,参见eSQL语言规范以获得更多细节。名字空间可使用eSQL的using子句导入到查询中。或者,一个或多个名字空间可在查询边界外指定,例如,被指定为Command/Connection对象的一部分。1.3扩展执行查询执行(包括扩展函数)总是在存储中发生。另外,除非另外注明,否则扩展函数的语义期望与服务器相同。eSQL解析器确认函数启用的签名;然而,任何语义确认在底层存储中执行。2详细设计2.1聚合聚合函数对值的多重集执行计算并返回单个值。对于COUNT的异常,聚合函数忽略所有空值。支持以下可用SQL函数聚合函数在计算值时忽略空值(除了count之外)。还要注意,不支持count(*)。聚合函数可在谓词和投影定义中使用。另外,聚合支持在支持相等性的数据类型上的“区分”模式(类似于SQL支持)。这些函数的返回类型基于针对其执行该函数的特性的类型。因此,例如,avg(int)将返回int。这可能不被期望为2,3的平均将返回2而非2.5。为解决这一问题,用户需要显式地将特性强制转换成具有正确精度的类型,例如avg(cast(intPropertyasdouble))。2.1.1名字空间这些函数在System.Data.Tsql.Aggregates名字空间中。2.1.2示例usingSystem.Data.TSql.Aggreqates;selectcount(i.Children)asCountfromItemsasi;selectmax(i,DisplayName)fromItemsasigroupbyi.Gender;2.1.3支持的平台支持SQL2000、SQL2005和WinFS。2.2串函数这些标量函数对串输入值执行运算并返回串或数字值。请注意,所有串函数都具有基于1的索引。2.2.1名字空间这些函数在System.Data.Tsql.String名字空间中。2.2.2示例usingSystem.Data.String;selectsubstring(reverse(i.DisplayName),1,5)fromItemsasi2.2.3支持的平台支持SQL2000、SQL2005和WinFS。2.3数学函数这些标量函数执行一组常见的数学运算。2.3.1名字空间这些函数在System.Data.TSql.Math名字空间中。2.3.2示例usingSystem.Data.TSql.Math;selectPI()fromItemsasi2.3.3支持的平台支持SQL2000、SQL2005和WinFS。2.4日期函数这些标量函数对日期和时间输入值执行运算并返回串、数字或日期和时间值2.4.1名字空间这些函数在System.Data.TSql.Date名字空间中。2.4.2示例日期部分需要被指定为串文字usingSystem.Data.TSql.Date;selectifromItemsasiwherei.ModifiedTime>dateadd(′day′,-10,i.ModifiedTime)2.4.3支持的平台支持SQL2000、SQL2005和WinFS。2.5系统函数存在生存在System.Data.TSql.System名字空间中的按照平台变化的一组系统函数。下表概述了这些函数以下各节提供了关于特定函数的更多细节。2.5.1Soundex和DifferenceSoundex函数映射到T-SQL的Soundex函数,并用于将字母串转换成4字符的代码以找到发音类似的单词或名字。该代码的第一个字符是字符表达式的第一个字符,而该代码的第二到第四个字符是数字。字符表达式中的元音被忽略,除非它们是串的第一个字母。串函数可被嵌套。其句法如下stringsoundex(string字符表达式)Difference返回两个字符表达式的SOUNDEX值之差作为整数。该函数可用于例如按发音对名字排序。其句法如下intdifference(string字符表达式,string字符表达式)以下是在查询中使用soundex和difference的示例。usingSystem.Data.TSql.System;//对所有项获得DisplayName的数据长度selectp.DisplayName,p.FullName.GivenName,p.FullName.SurnamefromOfType(Items,Person)asporderbysoundex(p.FullName.Surname)desc2.5.1.1支持的平台支持SQL2000、SQL2005和WinFS。2.5.2DatalengthDatalength函数用于返回特性的大小(作为int)。它映射到SQL的datalength函数。其句法如下intdatalength(特性)在特性没有值(即为空值)的情况下,返回值为0而非空值。这与SQL的datalength函数工作的方式形成对比。以下是在查询中使用datalength的示例。usingSystem.Data.TSql.System;//对所有项获得DisplayName的数据长度selectWinFS.Datalength(i.DisplayName)fromItemsasi//获得消息正文的数据长度selectsum(datalength(m.Body..Content)),m.SubjectfromOfType(Items,Message)asm2.5.2.1支持的平台支持SQL2000、SQL2005和WinFS。2.6XML函数对XML查询的支持经由扩展函数来启用。提供以下扩展函数XMLquery(XMLxmldoc,stringquery);booleanexist(XMLxmldoc,stringquery);stringvalue(XMLxmldoc,stringquery);2.6.1名字空间这些函数在System.Data.TSql.XML名字空间中。2.6.2示例usingSystem.Data.TSql.XML;selectifromItemsasiwhereExist(i.XMLProperty,′//text()′)2.6.3支持的平台支持SQL2005和WinFS。2.7WinFS函数2.7.1全文查询(仅WinFS)WinFS应支持用于全文搜索的两种TVF—包含和自由文本(各自有两个重载)。这些函数被映射到MSSearch中的功能typeFTFElementTypeasrow(ItemIdGuid,RankInt);typeFTFTypeasMultiset<FTFElementType>;FTFTypeContains(stringcontainsQuery);FTFTypeContains(stringpropName,stringcontainsQuery);FTFTypeFreeText(stringfreeTextQuery);FTFTypeFreeText(stringpropName,stringfreeTextQuery);所有这些函数返回行的多重集—每一行有两个字段。ItemId字段是Guid,并且是匹配全文谓词的项的id。Rank字段是该项的排名。结果不按照排名来排序—必须在查询中使用显式顺序来实现排序。包含包含对MSSearch的Contains(包含)谓词建模。containsQuery自变量必须符合由MSSearch的Contains谓词指定的句法。propName自变量必须符合用于在WinFS模式安装期间指定搜索特性的句法。自由文本自由文本对MSSearch的FreeText(自由文本)谓词建模。freeTextQuery自变量必须符合由MSSearch的FreeText谓词指定的句法。propName自变量必须符合用于在WinFS模式安装期间指定搜索特性的句法。备注·eSQL不解释任何自变量。它仅关心它们必须是串。2.7.1.1技术模式安装当安装WinFS模式时,标识任何搜索特性并将其储存在客户端程序集中。用于特性指定的句法如下查询处理如先前所提到的,eSQL并不解释这些函数的自变量。更具体而言,解析查询的WinFS运行库不执行除了确保参数为串之外的任何检查。WinFS提供者负责解释propertyName(特性名)自变量—可能是SqlGen的一部分、在客户端程序集中查找适当的特性id、然后将其转换成存储中等价的TVF调用。存储TVF将具有看上去如下的签名createfunctionFullTextSearch(@sqlStrnvarchar(max))returns@rTabletable(ItemId[System.Storage.Store,0.1,*,f45b95b0f79f4fbe].ItemId,Rankint)createfunctionFullTextSearch(@propIduniqueidentifier,@sqlStrnvarchar(max))returns@rTabletable(ItemId[System.Storage.Store,0.1,*,f45b95b0f79f4fbe].ItemId,Rankint)2.7.1.2名字空间这些函数在System.Data.TSql.WinFS名字空间中。2.7.1.3示例以下示例示出了对这些函数的使用usingSystem.Data.TSql.WinFS;selecti.DisplayName,c.RankfromItemsasi,Contains(′fooandbar′)ascwherei.ItemId=c.ItemId;selecti.DisplayName,c.RankfromItemsasi,Contains(′Contact.DisplayName′,′fooandbar′)ascwherei.ItemId=c.ItemId;selecti.DisplayName,c.RankfromItemsasi,FreeText(′Treat(Contact.EAddressasUsAddress).Zip′,′12345′)ascwherei.ItemId=c.ItemIdorderbyc.Rankdesc;2.7.1.4支持的平台支持WinFS。2.7.2SumString(仅WinFS)SumString(串求和)聚合(映射到称为SumString的存储UDF)提供了将串的类集的内容串接成具有定界符的单个串的能力。这可用于多个应用程序情形,包括在单个文本字段中显示消息参与者的“收件人”列表。该eSQL扩展是类似于count、sum、max等的聚合函数,并且可被应用于InlineObjectCollections、ItemFragmentCollections、ExtensionCollections上的串特性以及任何串特性作为Group运算的结果。用于该聚合的句法如下stringSumstring(Multiset<string>coll)其中类集必须是串的类集。2.7.2.1名字空间这些函数在System.Data.TSql.WinFS名字空间中。2.7.2.2示例以下是在查询中使用SumString的示例。usingSystem.Data.TSql.WinFS;//将ParticipantEAddress的列表串接在一起selectSumString(m.Participants.?(ParticipantType=Sender)..ParticipantEAddress.Address),m.SubjectfromOfType(Items,Message)asm2.7.2.3支持的平台支持WinFS。2.7.3NormalizeDigits(仅WinFS)NormalizeDigits(归一化数字)聚合(映射到称为normalizeddigits的存储UDF)提供了以类似于windows外壳排序的格式来对串排序的能力。用于该函数的句法如下stringNormalizeDigits(stringproperty,intmaxLength)2.7.3.1名字空间这些函数在System.Data.TSql.WinFS名字空间中。2.7.3.2示例以下是在查询中使用SumString的示例。usingSystem.Data.TSql.WinFS;//使用该函数来对项排序selecti.ItemId,i.NamespaceNamefromItemsasiorderbyNormalizeDigits(i.NamespaceName,4000)2.7.3.3支持的平台支持WinFS。3名字空间加载本文中描述的所有名字空间应在连接到SqlClient提供者时被自动加载。应当无需使用显式的“using”子句。4计算的方法和特性eSQL查询语言支持可用于在查询中引用被添加到O空间部分类的特性(即,非映射特性)的扩展机制。这些特性需要将eSQL表达式通过属性与其相关联。该表达式然后在查询中引用特性的任何地方被替换。所提供的eSQL表达式可以引用其它计算的特性和方法、其它映射的特性以及在Provider(提供者)清单中指定的其它函数。4.1计算的特性System.Data.Obiects.CalculatedAttribute可被添加到仅在客户端类中定义的特性(非映射特性)以允许在查询中引用该特性。CLR特性的类型必须匹配由eSQL表达式返回的类型。如果表达式的返回类型是非空值类型,则特性必须被声明为非空值类型。该约束由基础结构来强制实施,并且将抛出异常。如果该表达式的返回类型是类集,则特性必须为类型Query<T>。项上的ItemSize(项大小)特性是现今的模式中存在的计算的特性的一个实际示例。以下是使用该特性的一个示例//以下检索特定文件夹中大于10k(~10000bytes)的所有文档Query<Item>searcher=wd.Items.Source.Where(″it.ItemSize>100000″);该特性将用以下属性来标记[Calculated(Expression=″System.storage.Item.GetItemSize(ItemId)″)]publicintItemSize{get;}以上表达式引用映射到存储侧函数的提供者清单中的函数。PrimaryEmailAddress(主要电子邮件地址)是可从模式中计算的特性的示例,并且现在通过该函数可以被预测并通过eSQL来投影。该特性可返回用Primary关键词标记的人的电子邮件地址。以下代码示出了如何定义该PrimaryEmailAddress特性以允许通过eSQL来查询它publicpartialclassPersonItem...{…//对PrimaryEmailAddress特性加属性//@关键词引用计算的参数[Calculated(Expression=@″usingSystem.Storage.Contacts;element(selectvalueefromthis.EAddressesasewhereeisofSmtpEmailAddressandexists(select1frome.KeywordswhereValue=@keyword)))″)][CalculatedParameter(Name=″keyword″,Value=″System.Storage.Contacts.Primary″)]publicSmtpEmailAddressPrimaryEmailAddress{get{foreach(SmtpEmailAddresssmtpEAinEAddresses.OfType<SmtpEmailAddress>()){foreach(KeywordkinsmtpEA.Keywords){if(k.Value==″System.Storage.Contacts.Primary″)returnsmtpEA;}}}}}该特性然后可在查询中如下使用Query<Person>searcher=wd.Items.Source.OfType<Person>().Where(″PrimaryEmailAddress.Address=′rameshn@microsoft.com′″);Personperson=searcher.GetSingle();//注意GetSingle作为DCR来添加//该特性可如下投影Query<DataRecord>projection=wd.Items.Source.OfType<Person>().Select(″it.DisplayName,it.PrimaryEmailAddress″);//枚举结果foreach(DataRecordrecordsinprojection){stringdisplayName=(string)record[″DisplayName″];smtpEmailAddressprimaryEmailAddress=(smtpEmailAddress)record[″PrimaryEmailAddress″];}关于eSQL表达式的备注·“this”前缀必须用于引用实例上的其它特性/方法(映射的或计算的)·该表达式仅在运行时确认。·可提供任何有效的eSQL表达式,包括using子句。·计算的参数需要以@为前缀以在表达式中引用它们。4.2计算的方法类似于计算的特性,在部分类中定义的方法也可被启用以下eSQL中使用。System.Data.Objects.CaclulatedAttribute可用类似于添加到特性的方式来添加到方法。以下语义和限制应用于计算的方法·当前在eSQL中不支持泛型(例如,X<T>)方法,即,泛型类型参数将不在eSQL表达式中使用。·在计算的方法中不支持允许对一方法的可变数量的参数的paramsC#关键词。·当前不支持计算的方法的多态性。·当前没有对外部定义的私有或内部计算的方法的支持,即,API必须能够访问特性/方法。·一般而言,不支持计算的方法中的递归调用。·当前不支持通过参数值(类型)对计算的方法的重载。·方法的返回类型必须匹配由eSQL表达式返回的类型。о如果表达式的返回类型是非空值类型,则方法必须被声明为返回非空值类型。该约束由基础结构来强制实施,并且将抛出异常。о如果该表达式的返回类型是类集,则方法的返回类型必须为类型Query<T>。·不支持通过指定新运算符来隐藏方法。·不支持虚方法。·不支持返回void的方法·方法参数不需要前缀以在表达式中引用它们以下是使用计算的方法的一个示例此处的情形是要生成类似于图3所示的按照日期视图Outlook组的友好分组视图。以下代码将其封装到方法中以使其能在别处重用publicpartialclassMessage...{//该方法返回一组织的雇员//表达式中的empolyer引用方法参数employer[Calculated(Expression=@″usingSystem.Data.TSql.Date;casewhendatediff(′hour′,this.ReceivedTime,date)<0then′Future′whendatediff(′hour′,this.ReceivedTime,date)<24then′Today′whendatediff(′hour′,this.ReceivedTime,date)between24and48then′Yesterday′whendatediff(′hour′,this.ReceivedTime,date)between48and(24*(datepart(′weekday′,date)))thendatename(weekday,this.ReceivedTime)whendatediff(′hour′,this.ReceivedTime,date)between(24*(datepart(′weekday′,date)))and(24*(datepart(′weekday′,date)))+(7*24)then′LastWeek′whendatediff(′hour′,this.ReceivedTime,date)between(24*(datepart(′weekday′,date)))+(7*24)and(24*(datepart(′weekday′,date)))+(14*24)then′TwoWeeksAgo′whendatediff(′hour′,this.ReceivedTime,date)between(24*(datepart(′weekday′,date)))+(14*24)and(24*(datepart(′weekday′,date)))+(21*24)then′ThreeWeeksAgo′whendatediff(′hour′,this.ReceivedTime,date)between(24*(datepart(′weekday′,date)))+(21*24)and(24*(datepart(′weekday′,date)))+(84*24)anddatediff(′month′,this.ReceivedTime,date)<2then′LastMonth′else′Older′endasRelatiyeDate″)]publicstringGetReceivedTimeName(DateTimedate){//该方法的实现需要进行与表达式类似的逻辑以实现一致性…}}这然后可用于如下对结果分组//创建对消息的查询以按照ReceivedTimeName来分组Query<DataRecord>projection=context.Items.Source.OfType<Message>().Select(″it.Subject,it.GetReceivedTimeName(@date)asRelatiyeDate″,newQueryParameter(″date″,DateTime.Now)).GroupBy(″RelativeDate″);//枚举结果foreach(DataRecordrecordinprojection){…}4.3计算的和计算的参数属性System.Data.Objects.CalculatedAttribute定义如下namespacesystem.Data.Objects{[System.Attributeusage(AttributeTargets.Property|AttributeTargets.Method)]publicclassCalculatedAttributeAttribute{publicstringExpression{get;set;}publiccalculatedAttribute(stringexpression);}}该属性定义以下特性·Expression(表达式)。Expression特性包含投影值的有效eSQL表达式。该值然后可在另一表达式中使用。参见下文以获得关于如何在表达式中指定命名参数的细节。System.Data.Objects.CalculatedParameterAttribute定义如下namespacesystem.Data.Objects{[AttributeUsage(AttributeTargets.Property|AttributeTargets.Method,AllowMultiple=true)]publicclassCalculatedParameterAttributeAttribute{publicstringName{get;set;}publicobjectValue{get;set;}publicCalculatedParameterAttribute();}}该属性定义以下特性·Name(名字)。在计算的属性的Expression特性中引用的参数的名字。·Value(值)。由Name特性定义的参数的值。·在CalculatedAttribute中定义的命名参数遵循以下语义·对计算的参数的引用必须具有@前缀·定义CalculatedParameterAttribute和CalculatedAttribute的次序无关紧要。·参数名必须跨方法参数名以及由方法上的CalculatedParameterAttribute定义的其它命名参数是唯一的。·对于计算的特性,表达式中的任何命名参数必须由CalculatedParameterAttribute来定义。·以下示例示出了对以上属性类的使用publicpartialclassPerson...{//表达式中的@year引用由CalculatedParameterAttribute提供的命名参数year[Calculated(Expression=@″usingSystem.Data.TSql.Date;(@year-year(this.BirthDate)″)][CalculatedParameter(Name=″year″,Value=System.DateTime.Year)]publicintGetRoughAge(){...}}权利要求1.一种用于处理对数据的应用程序请求的系统,包括用于接受请求的对象服务层210,其中所述请求包括from子句形式的类集表达式;提供用于将所述请求变换成数据库查询的多个映射变换的映射提供者层220;用于将所述数据库查询转换成经修改的数据库查询的桥层230,所述经修改的数据库查询适用于第一数据库240的至少一个要求;所述第一数据库240包括用于处理所述经修改的数据库查询的查询处理引擎,其中所述第一数据库被配置成向所述桥层返回结果。2.如权利要求1所述的系统,其特征在于,所述请求还包括从包括并、交以及除外的组中选出的集合运算,其中所述集合运算对类集操作。3.如权利要求1所述的系统,其特征在于,所述请求还包括对类集操作的联接运算。4.如权利要求1所述的系统,其特征在于,所述桥层230被配置成将所述数据库查询转换成适用于多个数据库240中的任一个的要求的多个经修改的数据库查询中的任一个。5.一种用于处理对数据的应用程序请求的系统,包括用于接受请求的对象服务层210,其中所述请求包括从包括并、交和除外的组中选出的集合运算,其中所述集合运算对类集操作;提供用于将所述请求变换成数据库查询的多个映射变换的映射提供者层220;用于将所述数据库查询转换成经修改的数据库查询的桥层230,所述经修改的数据库查询适用于第一数据库240的至少一个要求;所述第一数据库240包括用于处理所述经修改的数据库查询的查询处理引擎,其中所述第一数据库被配置成向所述桥层返回结果。6.如权利要求5所述的系统,其特征在于,所述请求还包括对类集操作的联接运算。7.如权利要求5所述的系统,其特征在于,所述桥层230被配置成将所述数据库查询转换成适用于多个数据库240中的任一个的要求的多个经修改的数据库查询中的任一个。8.一种用于处理对数据的应用程序请求的系统,包括用于接受对类集操作的联接运算的对象服务层210;提供用于将所述请求变换成数据库查询的多个映射变换的映射提供者层220;用于将所述数据库查询转换成经修改的数据库查询的桥层230,所述经修改的数据库查询适用于第一数据库240的至少一个要求;所述第一数据库240包括用于处理所述经修改的数据库查询的查询处理引擎,其中所述第一数据库被配置成向所述桥层230返回结果。9.如权利要求8所述的系统,其特征在于,所述桥层被配置成将所述数据库查询转换成适用于多个数据库中的任一个的要求的多个经修改的数据库查询中的任一个。10.一种包括被配置成向数据库240请求数据的应用程序200的计算机系统,其中所述应用程序根据一查询语言生成请求,所述查询语言至少包括以下特性所述查询语言准许使用from子句形式的类集表达式;所述查询语言准许使用从包括并、交和除外的组中选出的集合运算,其中所述集合运算对类集操作;所述查询语言准许使用对类集操作的联接运算。11.如权利要求10所述的计算机系统,其特征在于,所述查询语言还要求每一查询包括至少一个表达式。12.如权利要求11所述的计算机系统,其特征在于,每一表达式具有不依赖于使用所述表达式的上下文的统一解释。13.如权利要求10所述的计算机系统,其特征在于,所述查询语言还准许使用以下表达式1+2*3"abc″row(1asa,2asb){1,3,5}e1unionalle2distinct(e1)14.如权利要求10所述的计算机系统,其特征在于,所述查询语言将所有子查询认为是多重集子查询。15.如权利要求10所述的计算机系统,其特征在于,如果期望来自子查询的标量值,则所述查询语言要求所述应用程序提供对类集操作的元素运算符。16.如权利要求10所述的计算机系统,其特征在于,所述查询语言还准许使用元素运算符来从类集中提取单元素值,并且准许使用selectvalue子句来避免在查询表达式期间创建行包装。17.如权利要求10所述的计算机系统,其特征在于,所述查询语言还准许使用selectvalue子句来跳过隐式行构造。18.如权利要求10所述的计算机系统,其特征在于,所述查询语言还准许使用行构造函数来构造任意行,使得selectvaluerow(e1,e2,...)语句显式地等价于语句selecte1,e2,...。19.如权利要求10所述的计算机系统,其特征在于,所述查询语言还准许使用广义化from子句中的左相关并统一地处理它们的查询表达式。20.如权利要求10所述的计算机系统,其特征在于,所述查询语言要求所有列引用以表别名来限定。21.如权利要求10所述的计算机系统,其特征在于,所述查询语言还包括准许使用句法快捷方式来处理通过类集的导航,其中所述快捷方式使用..操作符以允许从类集投影表达式,使得“a...b”等价于“selectvaluet.bfromaast”。22.如权利要求10所述的计算机系统,其特征在于,所述查询语言还包括准许使用groupby键的别名,以及这些键之中的左相关。23.如权利要求10所述的计算机系统,其特征在于,所述查询语言还准许使用两种聚合,即对类集操作并产生聚合的结果的基于类集的聚合,以及结构化查询语言(SQL)样式聚合,其中所述机制隐式地将SQL样式聚合转换成基于类集的聚合。24.如权利要求10所述的计算机系统,其特征在于,所述查询语言还准许使用INSERT..VALUES语句,并且所述语句不允许在所述INSERT..VALUES语句中指定列列表。全文摘要提供了一种具有对丰富数据类型的支持的查询语言。一种系统可包括被配置成根据查询语言来生成查询的应用程序。还提供了一种用于处理根据所提供的查询语言生成的查询的平台。该平台一般包括对象服务层、映射提供者层、以及最终可以与各种市场上可购买的数据库接口的桥层。文档编号G06F17/30GK101427249SQ200780010055公开日2009年5月6日申请日期2007年1月16日优先权日2006年3月20日发明者S·穆拉利达,S·F·施塔克,S·B·豪斯,S·卡瓦诺,F·M·F·巴尔武埃纳,R·纳加拉贾申请人:微软公司
网友询问留言 已有0条留言
  • 还没有人留言评论。精彩留言会获得点赞!
1