关系数据库快速入门

介绍

本教程的目的

本教程解释了如何使用实体框架构造和执行关系数据库的查询。
它旨在作为一个基于示例的快速入门指南,而不是一个完整的参考。它涵盖了最常见的用例,并用具体示例说明了这些用例。
在阅读完本教程之后,人们可以预期已经对如何构造实体框架查询并执行它们有了核心的实际理解。一些更高级的材料、微妙之处和角落案例被有意省略,以保持教程的规模和入门级。然而,大多数核心查询构建机制都有介绍,并用示例进行了说明,这应该足以开始使用。

关系数据库连接的作用

关系数据库是一种重要且广泛使用的信息存储、处理和查找手段。在过去的几十年中,相关技术已经非常成熟,现代关系数据库后端具有存储和处理结构化数据(从小数据量到超大数据量)的卓越功能。因此,能够将关系数据库无缝集成到工作流程中,对于在从数据科学到web应用程序的多个领域中进行工作,这一点很重要。
这方面的一个重要方面是能够在数据库端处理甚至非常大的数据集,“超出核心”。大多数常见的关系数据库后端都经过了高度优化,并应用了前沿技术,能够非常高效地处理如此大的数据集。
在Wolfram语言的上下文中,将大型数据集上的计算卸载到数据库引擎的能力尤为重要,因为许多此类计算不需要Wolfram Language的全部计算能力,而生成的数据集的大小可以大大减小,以便可以在Wolfram语言中轻松地进行进一步处理。

Wolfram语言中的关系数据库连通性

一般注意事项

将关系数据库直接集成到个人工作中可能会变得复杂,原因有很多。首先,许多领域专家不具备直接使用关系数据库所需的SQL知识。其次,SQL方言和常用的不同数据库后端支持的功能集之间存在(通常是细微的)差异。第三,必须手动构建数据管道,将数据库连接到编程环境。由于这些原因,将关系数据库直接紧密集成到语言中是非常有益的。它允许用户专注于解决他们的问题,而不是提供技术基础设施。
Wolfram语言的高级符号特性为将关系数据库集成到该语言中的框架设定了严格的要求。它必须是高级的、符号化的和惯用的,并且要公开足够的结构,以避免对原始SQL的查询构建和其他功能进行过多的限制。特别是,惯用的Wolfram语言编程倾向于函数式编程风格、函数组合和不变性因此,连接框架需要包含和支持的功能。

实体框架的作用

事实证明,实体框架在很大程度上满足了概述的要求。虽然Wolfram语言中没有OOP意义上的对象,但在关系数据库的上下文中,Wolfram Language的实体框架在许多方面与其他语言的对象关系映射器(ORM)类似。有些过于简化了,实体映射到数据库表的单行,实体类映射到数据库表格(现有或虚拟),实体属性映射到数据库列。
因此,在大量需要查询数据库的情况下,可以使用Wolfram语言,并通过使用实体框架功能查询语言构建的高级实体框架查询与数据库通信。然后,该框架负责将查询转换为适当的SQL方言,在数据库端执行查询,并以适当的格式将结果返回给用户。

实体框架和DatabaseLink

长期以来,用Wolfram语言与关系数据库交互的主要工具数据库链接它建立在J/Link和JDBC之上,提供了一个功能齐全的工具箱来与关系数据库交互。
重要的是要了解数据库链接工具箱和本教程中讨论的技术。最重要的区别在于这些工具提供的抽象级别,以及支持与数据库交互的功能。
工具箱由提供数据库链接是相当低级的:虽然它有一些特性,允许在简单的情况下以符号方式构造数据库查询,但在与一起使用时,大多数实际查询都必须编写为SQL字符串数据库链接这对用户有很多影响,从需要熟悉SQL(以及与使用的特定数据库后端相对应的特定SQL方言)到无法使用Wolfram语言在符号功能和高级抽象方面的优点。所有这些都导致数据库不完整、低级地集成到Wolfram语言中。
基于实体框架的技术从头开始设计,以提供关系数据库到Wolfram语言的高级无缝集成,并包含功能(如符号查询语言、关系、自动SQL生成和后端专门化、内置类型检查等)这使得在Wolfram语言中涉及关系数据库的更强大和更高级的工作流成为可能。
另一方面,数据库链接支持涉及数据库的常见工作流所需的许多核心功能,例如写操作(SQL插入/更新/删除)、事务支持、超时控制、连接池等。为了提高效率,它还提供对数据库结果集的低级访问。所有这些基于实体框架的技术目前都缺乏,因此可以说数据库链接目前功能更加完善。
除了两种技术都集成到Wolfram语言中之外,这两种技术之间目前没有深入的互操作性。在未来版本的Wolfram语言中,互操作性可能会变得更好。

教程的组织

材料的简要分类如下。
这个第一节解释了准备使用关系数据库的标准工作流:建立连接、创建和注册数据库备份实体存储对象。
这个第二部分回顾并说明了关系数据库上下文中Entity框架的关键特性。
这个第三部分通过Entity框架提供的核心查询构建块来构建更复杂的查询。
这个第四部分包含一些额外有用的构造和工具的简要介绍,这些构造和工具可能被认为更高级,但在实践中非常有用。
这个第五部分说明了实体框架支持的各种查询构建原语通常可以生成何种SQL。
这个第六段描述了如何以编程方式生成查询,并举例说明了为什么这可能是有益的。
这个第七节描述了一些查询构造的实用技术。
这个第八节包含错误处理的简要介绍,以及构建和运行查询时可能遇到的典型错误。
这个最后一段包含多个更复杂查询的示例,说明如何将不同的查询构建块结合在一起来构造更复杂的查询。

示例数据库

本教程中的示例基于示例公共域数据库经典车型(尤其是其SQLite版本),这是一家经典汽车数据库缩放模型零售商。
数据库由八个表组成:
它具有以下实体关系图(请注意,图上的数据类型对应于数据库的MySQL版本):
如下图所示,表存在以下关系:
连接到关系数据库

连接到数据库

要开始使用关系数据库,首先必须经历以下步骤:
以下是如何对本教程中使用的示例数据库执行此操作的:
这将创建与数据库的连接:
这使用连接来构造关系数据库对象:
这将创建一个数据库备份实体存储:
这将注册实体存储:
现在可以使用了。
下面是一个快速检查:显示有关“办公室”表:
可以简化前面的步骤,将其浓缩为更少的步骤。
首先,注销实体存储:
以下是浓缩步骤:
构建数据库连接的特定语法可能因不同的数据库后端而异。

使用RelationalDatabase对象

如前一节所述,创建数据库备份所需的步骤之一实体存储包括创建关系数据库对象。然而,这个对象本身也很有用。它包含有关数据库模式(表、列、约束等)的信息,可以用于可视化检查和以编程方式提取感兴趣的部分信息。
这将创建一个关系数据库对象:
的格式关系数据库对象可以使用分层组打开器轻松直观地检查数据库结构,可以展开这些打开器来检查给定表或列的更多详细信息:
还可以通过编程提取此信息。
下面列出了给定关系数据库对象:
以下测试是否存在具有给定名称的表:
以下列出了给定表的所有列名:
以下测试是否存在具有给定名称的列:
以下列出了给定表列的所有属性关系数据库对象知道:
可以按如下方式获取其值:
有关各种属性和方法的更多详细信息,请参阅关系数据库对象。
实体框架和关系数据库

介绍

本节介绍了实体框架支持的重要核心操作,以及它们在关系数据库上下文中的使用方式。它并不是主要关注查询构造,它有一个单独部分相反,它奠定了基础并讨论了其他重要方面,其中许多方面是通过Entity框架有效处理关系数据库的先决条件。
为了在处理关系数据库的工作中有效地使用实体框架,必须至少基本了解实体框架的概念和结构如何与关系数据库的核心概念和结构相对应。首先讨论这个主题。
接下来将讨论查询执行和所谓的“解析器”(通过启动查询编译和执行过程并返回结果,将符号表示的查询实际转换为特定结果的函数)。
接下来,计算属性和实体函数已覆盖。这些是非常重要的构建块,允许用户动态定义和计算新属性,而这些新属性可能需要在数据库端执行复杂的计算。
在许多情况下,能够使用单个实体非常重要。这对于各种用途都很有用,从更好地可视化和理解结果到查询构造(对于调试和原型设计,能够在单个实体上执行部分查询非常有价值)。下面简要介绍这个主题。
对于关系数据库,数据库表的主键是一个中心概念。实体框架对应于规范名称单个实体的属性。与规范名称一起定义单个实体的另一部分是该实体的类型。这些主题被认为足够重要,值得单独一节。
虽然涉及关系数据库的大多数现代工作流都处理具有主键的数据库表,但当表没有主键,或者表没有主钥但在数据库模式级别上没有强制执行时,这种情况并非闻所未闻。这些在实体框架的上下文中更为重要,因为对于此类表/实体类型,只有实体框架的一部分功能有效。接下来将讨论这个问题。
虽然本教程并不关注与关系数据库交互中隐含的类型和类型系统,但有一个类型区别对于有效工作来说非常重要。即,实体类型可能包含实体值和实体类值的属性。在本教程中,这些属性称为关系,并由框架添加到每个实体类型的核心属性集(直接对应于数据库表列的属性)。
本节讨论的最后一个主题涉及在关系数据库上下文中缺少的值以及在结果中可能遇到这些值的方式。

实体框架和SQL之间的近似映射

由于实体框架在Wolfram语言中使用尤其是表示关系数据库显然,实体框架和关系数据库/SQL之间必须有核心概念和构造的映射。
对于Entity框架的许多功能,这样的映射相当直接。然而,在某些方面,实体框架代表了更丰富的数据模型。例如,对于实体框架的内存使用,实体属性可以将任意Wolfram语言表达式作为其值如果直接使用,甚至不符合关系数据库的第一种范式(例如,当实体属性为列表值)。另一个例子是,实体框架能够用无限多的实体来表示类型,这是关系数据库不容易做到的。此外,对于使用Wolfram语言的关系数据库,容易实现的计算集更加有限,后者自然具有“开箱即用”的更丰富的计算功能。
关系模型的限制也有其优点,例如能够为符合ACID的数据库的数据一致性、事务和其他相关有用功能提供强有力的保证。这些限制还意味着对数据库支持实体存储的实体框架施加类似的限制。
也就是说,核心关系和实体框架结构之间的映射相对简单。下表对其进行了总结。
数据库(模式)
实体类型(数据库表)的集合
数据库表
实体类型
具有类似属性集(表行)的实体集合(句柄)
数据库表行
实体(单个实体)
一组属性/值的(句柄),表示具有唯一标识的单个“事物”(表行)
数据库表字段(列名)
特定属性,通常具有特定类型
主键
对于给定的实体类型(db表),一个属性或一组属性保证是唯一的(当将一组属性组合在一起时)
外键
实体价值财产
值为单个实体的实体属性(可以是相同或不同的实体类型)。对于数据库,指向表中唯一行的字段(可以是同一个表,也可以是不同的表)。
(派生)表
同一类型实体的集合(句柄)(由查询注册或定义)。在数据库端,派生表是一个虚拟表/子查询,在FROM子句中使用。
如前所述,这种映射具有一定的不对称性。它使将现有关系数据库表示为实体存储变得简单。然而,在不创建额外抽象层的情况下,并非所有现有内存中实体存储都可以轻松映射到关系数据库。特别是,以下是存在的一些限制:
当需要通过关系数据库(实体框架当前不支持的功能)支持现有内存中实体存储时,这些考虑因素更为重要,但记住这些因素对于更好地理解整体情况仍然很有用。
也有一些限制在另一个方向上起作用。例如,从技术上讲,可以让数据库表没有主键,并与数据库端的表一起工作。然而,在实体框架方面,每个实体都必须有一个规范名称在给定的实体类中是唯一的。这意味着无法为此类数据库表定义单个实体,因此entity框架的所有与单个实体相关的功能都无法用于此类数据库表/类型。特别是,功能如下实体列表实体值带有某些修饰语(例如。“实体属性关联”)在这种情况下不起作用。该问题将在中进一步讨论这个部分.

使用EntityValue和EntityList执行查询

实体框架中只有两个“解析器”(可用于执行查询的命令):实体值实体列表.
主要和最常用的是实体值它通常用于执行给定的查询并以各种形式获取结果。在关系数据库的上下文中,查询通常表示(虚拟)数据库表,这些表在实体框架中对应于(虚拟)实体类型。的作用实体值在这个上下文中,是将给定的查询编译成合适的SQL方言,在数据库上执行,并以各种形式(值的列表或关联)提取某组实体属性(数据库术语中的结果表列)的值。
以下是使用实体值.
下面提取注册类型的几个属性的值“办公室”:
结果可以以不同的形式获得,例如作为关联列表,保留属性名称:
对于提取的属性,可以使用完整的实体属性代替短字符串名称,在这种情况下,数据关联中的结果键也将是实体属性[]表达:
还可以通过以下形式获得结果数据集:
的第三个参数的全部可能修饰符实体值可以在文档中找到实体值.
在某些情况下,可能需要获取给定实体类型/类中包含的实体列表。这可以通过实体列表.
以下代码返回包含在“办公室”实体类型:
请注意,除了将在后续章节中讨论的某些特殊情况外,对于重复调用实体列表实体值.

计算属性和EntityFunction

除了提取现有属性外,还可以提取计算的属性也就是说,动态创建属性,这可能需要在数据库端进行复杂的计算。这些属性必须使用实体函数.的语义实体函数在这种情况下类似于功能,在接下来的章节中将介绍一些重要的差异。人们可以想到实体函数作为类似于的构造实体属性(在本节的上下文中)获取单个实体并返回使用该实体执行的某些计算的结果。目前重要的是实体函数只能返回标量,但不能返回值列表、实体或实体类。
以下是使用实体函数检索需要在数据库端进行计算的属性。
以下查询提取属性“办公代码”和计算的属性,表示中每个办公室的完整字符串办公室地址“办公室”类型:
实体函数全部保留属性,就像功能,以防止过早评估其变量和主体。的主体实体函数是一个表达式,其中可以使用一组有限的基元实体函数能够理解并编译为SQL。这些原语的完整列表可以在中找到实体函数文档,而实际使用的更多示例实体函数可以在后续章节中找到。

使用单个实体

单个实体是实体框架中的一个重要概念和构建块。使用单个实体的能力为工作流增加了交互性,通常允许人们更好地理解数据,即使最终结果应该是对多个实体(实体类)起作用的查询。
重要的是要理解单个实体本质上是对实际数据的处理(或引用)。除了类型和规范名称外,它们不包含数据。因此,它们是懒惰的:每次需要从实体中提取数据时,都必须对其运行新的查询。实体的这种懒惰特性是entity框架的一个非常有用的特性,因为它允许以更抽象的方式处理实体。然而,它附带了一些需要注意的事项。例如,如果某个特定实体的某些属性在同一查询的两次连续执行之间发生更改,则返回的结果通常也会有所不同,从而反映出这些更改。也可能会发生这样的情况:给定实体类中的某个实体在某个时候被删除,不再存在。当然,这些复杂情况只会发生在时间变化的数据上。
下面是几个如何查询单个实体的示例。
考虑一个特定的单个实体:
属性值可以使用实体值:
还可以使用特殊的查找语法:
对于实体类,可以使用修改器控制结果的形状:
重要的是要认识到,可以使用实体函数这对于快速查找和构建更复杂的查询原型都非常有用。
例如,这将从“城市”“状态”给定办公室的属性,在数据库端:
也可以通过以下方式实现:
最后一个示例还说明了实体函数.

实体类型、单个实体的唯一性和规范名称

在实体框架中,每个实体在其实体类型/实体类中必须是唯一的。另一种说法是,每个实体都必须具有唯一的标识,并且不允许任何实体类/实体类型包含重复的实体。

实体类型和规范名称

从语法上讲,实体由包含两部分的表达式表示:实体类型和实体唯一标识符,即规范名称。
查看输入表单实体数量:
还可以使用提取实体的规范名称规范名称.
以下内容提取单个实体的规范名称:
这个规范名称函数也适用于实体列表:
规范名称通常是数字、字符串或其中的列表。
类型的实体“订单详细信息”将一对整数订单号和字符串产品代码作为其规范名称:
实体的实体类型始终是的第一个参数实体[],但不总是字符串。特别是,当类型不是注册的类型,而是由实体框架查询隐式定义的“运行时”类型时,它将是该查询本身。
以下查询定义了复杂类型:
看看这种类型的单个实体:
此实体的类型由查询定义:
对于与数据库中的数据库表相对应的实体类型,实体的规范名称正好是相应数据库表的主键。

规范名称何时可以是列表?

在关系数据库的上下文中,实体的规范名称可以是以下情况之一(或其组合)的列表:
前面的示例属于最后一类案例,其中分组属性为“城市”“国家”,而前面的示例中包含的实体“订单详细信息”类型属于第一个类别,主键为“订单详细信息”表是一个复合表,由属性组成“订单编号”“产品代码”.
下面是第二个类别的示例。
下面定义了一个组合类型的实体类“员工”“办公室”。此类的实体具有规范名称,即员工编号和办公室代码对的列表:

没有主键的单个实体和数据库表

某些数据库表在数据库模式中没有主键约束的情况可能并不典型,但在实践中很重要。请注意,并非在所有这些情况下,表中都不存在具有唯一值的列。然而,目前还没有办法指示Entity框架使用这样一列作为此类表的主键:关于主键的所有信息目前都是在数据库检查时获得的(当关系数据库对象)并完全基于现有数据库约束。
这对于基于Entity-framework的工作流意味着,对于此类表,无法定义单个实体,因为没有明确的方法可以将规范名称附加到此类表中的数据库行。这并不意味着不能对这些表做任何有用的事情,但实体框架的某些功能部分将无法与这些表一起工作。
为了说明这些表在实体框架功能方面存在的局限性泰坦尼克号将使用数据库,这是一个托管在Wolfram Cloud中的SQLite数据库,基于以下内容泰坦尼克号数据集:
从这个数据集的结构可以看出,没有字段或字段组合可以自然地作为主键。甚至无法保证数据集中没有重复记录,因为数据中甚至没有乘客姓名,只有乘客的旅行级别、年龄、性别以及他们是否幸存下来。尽管如此,这个数据集还是一个有用的信息来源。
示例工作流说明了一些实体框架操作,这些操作对于此类数据集是可行的,也不可行。
第一步是创建关系数据库对象:
这一步应该没有问题;正确标识数据库及其包含的单个表。
下一步是创建并注册实体存储基于此数据库的对象:
如图所示,警告由实体存储单个实体无法用于此类型/数据库表。特别地,实体列表和一些形式的实体值将不起作用。
实体列表不适用于没有规范名称(没有主键的表):
实体值对于某些修饰符也不起作用:
然而,不涉及单个实体的其他形式也起作用。
只要结果数据的形式不涉及单个实体,仍然可以提取特定属性的值:
许多有用的查询也是如此。
以下查询选择所有年龄<=15的头等舱幸存乘客:
以下查询为乘客引入了一个粗粒度的年龄等级,其中0至20岁的乘客属于第一组,20至40岁的乘客位于第二组,依此类推。然后计算每个乘客等级、年龄组和性别的幸存乘客比例,并按该比例将结果按降序排序:
总结本节:

实体值和实体类值属性

属性可以是实体值或实体类值。这些属性不对应于相应数据库表的任何数据库表列,而是由框架添加并表示相关表。
以下是“员工”实体类型。
下面获取的值为“员工”属性,它是一个(隐式)实体类:
可以使用以下命令将此实体类扩展为实体列表实体列表:
举一个实体价值财产的例子,考虑其中一名员工。
这将为给定办公室选择第一个员工,并提取该员工的实体属性和值:
除了“办公代码”属性,在数据库术语中,它是“办公室”表中,有一个生成的属性“办公室”其价值是代表该员工工作的办公室的实体:
请注意,此类实体和实体类值属性没有直接的数据库列来源,而是由框架基于数据库模式(关系/外键信息)生成的。
此时需要注意的一点是,实体类值属性不会自动解析为实体列表,因此实体列表如果想要获得实体列表,则必须显式应用于其值。
生成的属性在关系部分.

结果中缺少值和属性名无效

对于某些实体,某些属性在数据库表中可能没有值(值可能是无效的). 在Wolfram语言方面,这对应于缺少[]值。
一个例子将说明这一点。
再次考虑其中一个办公室:
该办公室位于法国巴黎,因此对“状态”字段。缺少这样的值对应于无效的值,并由表示缺少[“不可用”,...]结果是:
另一种类型的输入导致缺少[]值是无效属性名的请求值。然而,在这种情况下,缺少该值的原因不同:缺少[“未知属性”,...].
下面是一个示例,其中不存在属性的值“foo”请求:
核心查询构建块

介绍

本节描述实体框架为构建更复杂的查询而提供的核心原语。
首先,介绍了类属性查询。通过大量示例对其进行解释和说明,这些示例涉及如何使用计算属性显著扩展可用实体属性集,以及如何实体函数用于表示这些计算的属性。
本节的其余部分专门讨论接受实体类(和其他参数)并返回新的(转换的)实体类的各种原语。实体框架为以下典型的数据处理操作提供了此类原语:
这些实体类转换原语可以相互组合和嵌套,从而可以构造更复杂的查询。

实体函数和类属性查询

类属性查询是用于构建部分实体框架查询的Wolfram语言表达式,在查询编译和执行过程中,这些查询被编译为SQL表达式。用于构造类属性查询的主要构造是实体函数.
类属性查询在许多地方都很有用,例如定义计算属性、过滤数据的谓词、排序标准、聚合等等。在本教程中,将考虑其中的一些典型示例。
可以使用标准算术运算:次数,Plus(加),减去,划分,国防部等。
以下计算每个订购项目的付款总额,同时考虑订购项目的数量:
还支持许多字符串操作函数。
下面提取了“状态”中每个实体的属性“办公室”类型并计算布尔表达式,该表达式检查此属性值是否以字母开头“C”:
要测试缺少的值MissingQ公司可以使用谓词。
以下示例显示了如何使用MissingQ公司检查缺失值的谓词在这种情况下,对于属性“状态”对于类型“办公室”:
还支持标准比较运算符。请注意相等SameQ公司可以互换使用,以及不平等UnsameQ(取消命名).
下面的示例显示比较运算符的使用相等,SameQ公司,不平等,未命名Q在字符串比较上下文中:
可以构造相当复杂的数值表达式和其他类型的表达式,这些表达式将被转换为SQL并在数据库端执行。
以下查询计算建议零售价和购买价之间的差额与建议零售价之间的比率,即商店以建议零售价销售商品时从销售商品中获得的收入百分比:
下面是一个更复杂的度量,也涉及电源平方米,仅供说明:
布尔表达式尤其重要,因为布尔值实体函数表达式经常用作筛选谓词、条件组合实体类等。
以下查询计算一个布尔属性,该属性将生成真的对于订购数量>30且每项价格>=200美元的项目“订单详细信息”类型,其中Wolfram语言端后处理仅选择它为其生成的那些产品真的:
以下查询计算的属性真的对于可被100整除的订单号:
以下查询计算一个布尔属性,该属性将生成真的对于其所在城市中包含信件的所有办公室“a”,或其状态存在(不存在缺少[])他们的城市里有字母“o”:
以下查询计算一个布尔属性,该属性将生成真的对于要求在下订单之日起八天内发货的所有订单:
在下面的示例中,布尔值查询表达式用作内部的筛选条件筛选的实体类,查找订购数量大于30的项目的所有订单:

关于实体类变换器的一个注记

以下各节中描述的操作,即筛选的实体类,排序实体类,示例实体类,扩展实体类,聚合实体类组合实体类,都可以称为实体类转换器。它们都接受一个实体类(如果是组合实体类)并返回一个新的实体类。
重要的是要认识到,这些结构完全是象征性的和惰性的。当其中一个应用于实体类参数时,不会执行任何实际工作。结果查询保持符号化,需要调用其中一个解析器函数(实体值实体列表)以实际执行查询。
下面是一个查询示例,其中包含多个相互嵌套的变压器。
这将根据他们过去的付款选择五位付款最高的客户,并按付款总额递减的顺序列出:
求值时,符号Entity框架查询对自身求值,保持为惰性符号表达式。但是,仍然可以使用它做一些有用的事情,例如,查找此查询表示的实体类的所有实体属性(这不需要调用数据库)。
下面返回由查询定义的实体类的实体属性:
一种用途实体值以实际执行查询。
可以使用以下命令执行此查询实体值:
实体框架查询的符号性和惰性为程序化查询构造,因为查询本身是惰性的Wolfram语言表达式,可以手动或通过编程从较小的构建块构造。

使用FilteredEntityClass进行过滤

最常用的操作之一是根据特定标准筛选数据库数据。在实体框架中,筛选的实体类用于此目的,使用实体函数并传递给筛选的实体类作为第二个论点。
以下示例说明了筛选的实体类.
以下列出了除以下职务以外的所有员工的名字、姓氏和职务“销售代表”:
以下查询选择电子邮件名称短(长度<=5个字符)的所有员工:
以下查询查找名称以以下之一开头的所有员工“M”,“P”,“D”:
通过以下查询也可以获得相同的结果:

使用SortedEntityClass排序

根据某些标准对实体(数据库表中的行)进行排序是另一种常用的操作。在数据库方面,这是通过使用SQL的ORDERBY子句来实现的。在实体框架方面,排序实体类可以用来实现这一点。
在最简单的情况下,需要根据单个现有实体属性(数据库列)的值按升序排序。在这种情况下,字符串字段名传递给排序实体类作为第二个论点。
下面的示例对此进行了说明。
以下是根据办公室代码对员工进行排序:
如果需要结果的降序,可以使用fieldName->“降序”语法,如下所示。
以下查询根据办公室代码按降序对员工进行排序:
也可以按类似属性的查询进行排序。
以下查询列出了所有员工的员工编号、名字和姓氏,并按名字的长度以升序排序:
可以按多个属性排序。在这种情况下,第二属性的值被用作具有第一属性的相同值的项目组的平局决胜器,并且相同的逻辑扩展到第三等排序属性。可以将“升序”“降序”分别为每个排序属性指定限定符,以控制实体(子)组中的排序顺序。
以下查询按产品线排序产品,然后按库存数量降序排序:
可以限制返回的结果数排序实体类,利用第三个参数。
以下是前面的查询之一,列出了按名字长度排序的员工信息,其中结果现在仅限于前七条记录:

使用SampledEntityClass进行子集设置

在某些情况下,只从一个实体类中选取一定数量的实体(或者,从数据库术语中,只从表或虚拟表中选取行的特定子集)很有用。在实体框架中执行此操作的方法是使用示例实体类.
以下示例说明了它的典型用法。
以下查询从“付款”类型(表格):
以下操作相同,但跳过前10个条目:
注意,只有在以下情况下才能保证结果的顺序示例实体类应用于排序实体类然而,即使在其他情况下,它仍然非常有用,例如,当人们想查看大型数据集或使用小样本创建查询原型时。
以下查询返回前五种最昂贵产品的名称和价格:
在这种情况下,顺序是由数据集在子集之前已经排序这一事实来保证的。可以更经济地获得相同的结果排序实体类有三个参数:

使用ExtendedEntityClass引入新属性

可以将新的计算属性添加到给定类型/实体类的可用实体属性集。结果是一个新的实体类。新添加的属性可以在原始属性可以使用的任何位置使用在里面实体值,进一步在查询的外层等。创建这样一个新的扩展实体类的构造是扩展实体类.
下面的示例演示了一个简单的用例,其中向实体类添加了一个新属性。
以下查询添加了一个新属性“全名”对于所有员工:
可以添加多个属性,在这种情况下,应该使用列表。
以下使用关系(更详细地描述在这里,属性“员工报告到”)添加经理的名字和姓氏,对于有经理的员工:
新属性可以使用各种构造,包括条件逻辑等。此功能允许进行非平凡的计算。
以下查询通过将当前信用限额>=100000美元的客户的限额增加15000美元,为客户添加了一个新的调整后的信用限额:

使用AggregatedEntityClass进行聚合

聚合是一种非常常见的操作。它用于计算在多个实体(数据库中的表行)上聚合的值。聚合的典型示例包括计算一系列实体上某些属性或更复杂表达式的总计、平均值或最小值和最大值。在Entity框架中,可用于执行聚合的构造是聚合实体类.
正如其名称所示,其应用程序的结果是一个新的实体类。如果在整个原始实体类上执行聚合,则生成的实体类将仅包含一个实体,其属性为计算的聚合属性。还可以首先根据实体的特定属性集的值对实体进行分组,然后对每个这样的组执行聚合在这种情况下,新实体类将包含与此类组一样多的此类新聚合实体。
目前,实体框架为数据库支持的实体存储支持的核心聚合函数集有限且很小:总计,长度,分钟,马克斯,平均值,方差标准偏差。但是,可以使用标准算术函数和其他类似属性的查询构建块来组合这些核心操作并计算更复杂的表达式。
值得注意的是实体函数对于聚合的情况,它绑定到要聚合的整个实体类,而不是该类的单个实体。
下面是使用聚合实体类计算单个聚合值。
以下查询计算所有订购项目的总数:
理解的语义实体函数在这种情况下,使用Wolfram语言在顶层计算相同的内容很有用。
此代码使用Wolfram语言计算相同的数量进行聚合,其中功能用于使类比最为明显:
有更简洁的方法来计算聚合属性。
可以使用更简单的语法,即使用与所聚合属性名称相同的名称作为所生成聚合属性的名称。这适用于一种简单的情况,即在每种情况下都有一个属性被聚合,但一个属性可以有多个聚合:
还有一种更简洁的形式适用于存在单个聚合的情况(但是,返回标量而不是列表):
最后一个示例的一个重要备注是,对于当前版本的Entity框架实体值在顶级查询中用于聚合时,将在Wolfram语言端而不是数据库端执行聚合(在子查询中使用此语法时不会发生这种情况)。相反,所有聚合都显式使用聚合实体类将始终在数据库端执行。
可以计算多个聚合属性。
下面的查询定义了一个具有三个聚合属性的聚合实体类:所有订单的最大、最小和平均订购数量(即“订单详细信息”表提供):
可以计算特定实体组的聚合,而不是整个实体类。在这种情况下聚合实体类必须使用,以指示其值将定义用于聚合的唯一实体组的属性或属性列表。
以下示例说明了此类典型用例。
以下计算每个产品线内产品的平均购买价格:
以下计算了每个办公室的员工数量:
可以通过使用一组属性而不是单个属性对实体进行分组。
以下查询计算来自同一城市和国家的客户总数,按客户编号降序排序:
在这种情况下,对具有相同城市和国家组合的实体组进行聚合。
可以在聚合函数中使用比单个属性更复杂的表达式。
以下查询计算所有已下订单的总支付金额(在本例中,语义上是o个[“每个价格”]o个[“订购数量”]表示数据库中的列。它们的乘法是允许的,因为它们属于同一个表(因此具有相同的长度),并且必须理解为矢量化的元素操作。这个乘法运算的结果在语义上是另一列,它被传递给总计):
以下查询以两种不同的方式计算每个订购项的平均数量:通过显式计算和使用内置函数(平均值):

将实体类与CombinedEntityClass组合

在关系数据库中,数据通常存储在多个表中,通过外键约束相互连接。只有在极少数情况下,对于人们通常想要编写的实际查询,人们只使用一个表,并且考虑到数据经过规范化后通常被拆分并存储在(可能很大)数量的相关表中。
在SQL查询中使用多个表中的数据的最常见方法是使用SQLJOIN操作。SQL JOIN的实体框架对应物是组合实体类构造。它需要两个实体类/类型,说明如何组合它们的规范,以及可选的JOIN类型。结果是一个新的实体类,新的实体包含两个原始实体类的属性。
以下示例说明了组合实体类.
以下查询组合了实体类“员工”“办公室”,通过“办公代码”属性,然后提取属性“员工编号”,“名字”,“姓氏”,“城市”“国家”,其中前三个属于类型“员工”后两种类型“办公室”:
如果查看新类的完整属性列表,这可能会变得更加明显(实际查看它们的输入表单):
新类的实体是一种新类型,可以通过使用实体列表:
应用生成的新实体类组合实体类可以在更复杂的查询中进一步使用,就像任何其他实体类一样。
以下查询查找在法国或英国工作的所有员工,并列出他们的员工编号、名字和姓氏以及国家:
对于信用限额大于120000美元的客户,以下返回付款信息,包括客户编号、姓名、支付金额和付款日期(其中需要Wolfram Language后处理步骤,因为日期存储在数据库端的Unix时间中):
注意,在上一个示例中,有必要显式地指出实体属性["客户","客户编号"]在中的属性列表中实体值,因为这两种类型“客户”“付款”拥有具有此名称的属性,并且必须准确区分请求的属性。
对于来自法国的客户,以下显示客户名称及其销售代表的姓名:
将一个类型与其本身结合起来是可能的,而且通常也是可取的。在关系数据库操作的上下文中,这种情况对应于自联接。在这种情况下,需要更加小心地消除属性名称的歧义必须为组合的至少一个相同类型引入(字符串)别名。下面的示例说明了这种情况。
以下查询列出了员工的名字和姓氏,以及他们的经理姓名。这个“经理”第二个的别名“员工”类型,然后用于消除实体属性的歧义:
在某些情况下,测试用于确定是否应将来自两个被合并实体类的两个实体的组合包含在结果实体类中的条件比两个实体某些(组合)属性之间的简单等式更复杂。对于这种情况,可以使用实体函数使用两个参数,每个参数绑定到要组合的相应实体类的实体。这样一个实体函数必须返回布尔值,否则可以是任意复杂(可编译)的表达式。
下面的示例说明了这种情况。
下面是一个非常有趣的示例,因为它说明了组合实体类用法。此查询代表了查找拥有多个销售办事处的所有国家/地区的一种方法。想法是将“办公室”类型及其本身,并作为的条件组合实体类使用谓词检查合并的两个实体的国家是否相同,而办公代码是否不同。二者的任何此类组合“办公室”类型实体确实对应于一个拥有多个办事处的国家。因此,只需提取合并实体的国家名称并删除可能的重复项:
在最后一个示例中,还需要在组合的“办公室”类型,以消除属性的歧义,就像前面的自连接示例中一样。最后,删除重复项用作的第三个参数实体值要从结果中删除重复项(如前所述,重要的是要记住,在这种情况下实体值当前在Wolfram语言端执行,而不是在数据库上执行)。
其他工具

子查询

子查询是在更复杂的查询中用作构建块的查询。当子查询的结果是标量时,它们采用最简单的形式。在本节中,将使用几个子查询示例来说明它们的一般用途。
作为一个起点,考虑从某个实体类获取单个聚合属性的常见任务。这还不是子查询。以下示例说明了实现此目的的各种方法。
以下聚合查询返回“MSRP”所有产品的属性:
使用以下替代语法可以得到相同的结果:
然而,值得注意的是,虽然结果相同,但第一个(较短的)版本实际上在Wolfram语言中执行聚合,而最后两个版本在数据库端聚合。
然而,当此类查询用作较大查询中的子查询时,在这种情况下,它们总是在数据库端执行,包括实体值.
下面是包含子查询的查询的简单示例。
以下查询使用上一个查询作为子查询。从价格范围来看,它选择了所有MSRP价格在最昂贵产品中排名前10%的产品:
与上一示例中的子查询类似的子查询称为不相关查询,因为它们不引用查询外层的实体(表行)。还可以构建所谓的关联子查询,其中确实包含这样的引用(因此,如果不进行修改,就无法单独执行)。
以下示例说明了构造涉及更复杂和/或相关子查询的查询通常要经历的步骤。
以下查询计算办公室代码为的办公室的员工总数"4":
也可以对办公室实体进行相同的计算,其中硬编码"4"已从条件中删除,现在有嵌套实体函数表达式,内部表达式引用外部表达式的变量:
现在很容易理解以下代码,它计算每个办公室的员工人数,并根据员工人数按降序对办公室进行排序:
最后一个示例是相关子查询的示例。相关性通过内部实体函数的主体引用外部实体函数的变量。
请注意,在许多情况下,用联接替换相关子查询很简单。
最后一个示例查询的联接版本要简单得多:
子查询是一个强大的工具,但它们很容易被滥用。根据具体情况,它们可能是也可能不是解决给定问题的最佳方法。深度相关的子查询可能会导致性能低下,因此应该谨慎使用此强大的工具。

成员Q

这个成员Q实体框架查询中使用数据库支持实体存储上下文中的谓词来公开SQL英寸操作员。在最简单的形式中,它用于测试显式值列表中值的成员身份。
以下示例说明了此类用例。
以下属性由定义实体函数产量真的对于办公室代码为"2","4""7"、和False(错误)否则:
也可以使用成员Q与子查询结合使用,在这种情况下成员Q不是文字列表,而是子查询的结果。在这种情况下,子查询应该返回列而不是标量,通常可以写为[“属性”]或者,同等地,实体值[,“属性”],其中是某个实体类(由内部查询注册或定义)。
以下示例说明了此类用例。
以下查询选择居住在美国境内办事处所在城市的客户:

删除重复项和SQL DISTINCT

在某些情况下,只需保留那些不同的选定值或值组。在SQL中与众不同关键字用于此目的。在实体框架中,可以使用删除重复项作为第三个论点实体值以达到类似的效果。
以下查询返回“状态”类型的属性“订单”:
如前所述,在前面的示例中,删除重复项当前在Wolfram语言端执行。但是,当将上述查询用作子查询时,将在数据库端删除重复项。
一个稍微不同的用例是当一个人需要在不同的值上进行聚合时。在这种情况下,还可以使用删除重复项里面实体函数实现这一目标。
下面计算不同精确价格的数量以及四舍五入价格,以及平均不同价格和平均四舍五进价格(忽略价格乘数):

关系

在本教程中,由实体框架为相关实体类型(数据库表)生成的实体值和实体类值实体属性称为关系。
除了与现有表列对应的属性外,Entity框架还为与其他表相关的表创建了新属性。它们构成了一种机制,提供了一种更高级(w.r.t.显式联接或子查询)的方法,以在查询中使用来自多个相关实体类型(数据库表)的数据。
已经考虑了此类属性以前,但在最基本的层面上。
重要的是要认识到,没有什么是可以用关系完成的,而核心原语则无法完成。然而,在许多情况下使用关系可以导致更简洁的查询,这需要更少的构造和理解工作。
下面的示例说明了这些关系。
考虑类型的属性“员工”:
在此示例中,有四个属性与现有数据库列不对应:“办公室”,“客户”,“员工-报告To”“员工反向”.
理解关系的最简单方法之一是在单个实体的上下文中。
考虑一些特定的员工:
相关财产分别对应于该员工服务的客户、该员工工作的办公室、该员工的经理以及该员工担任经理的所有员工:
关系可以是实体值或实体类值。
以下查询查找给定员工的所有同事的实体类:
以下是一个更有趣的查询,它返回在同一办公室工作的所有员工,并与给定员工向同一经理报告:
可以使用关系来经济地表示查询。以下示例对此进行了说明。
以下是如何使用关系计算每个办公室的员工数量:
如果在查询中进一步需要该属性,可以使用扩展实体类使用该属性扩展给定实体类:
关系的一个重要特征是人们可以多次遵循它们,特别是对于实体价值关系。
以下查询通过向每个订单添加两个新属性来演示关系的使用:下此订单的客户的姓名和为该客户提供服务的员工的全名。请注意如何遵循关系来提取属于相关类型的数据(数据库表):
在需要实体类的任何地方,都可以在查询中使用实体类值关系。
以下示例计算每个员工的所有客户总数以及具有高信贷限额(>50000美元)的客户总数,并将这些值添加为新属性。在这种情况下,e(电子)[“客户”]是实体类值关系,可用于筛选的实体类.
关于如何使用关系来构建更复杂的查询的更多示例,请参阅最后一段本教程的。
实体框架和SQL

介绍

本节的目的是对框架为各种受支持的构造通常生成的SQL代码的类型提供一个基本概念。此处提供的生成的SQL主要用于演示。
虽然本节中提供的许多SQL查询实际上与当前版本的Entity framework查询编译器(用于SQLite后端)生成的SQL代码相对应,但不应假定生成的SQL总是采用这种形式。它可能会因版本而异。从实体框架的观点来看,只要最终结果正确且具有合理的效率,生成的SQL的确切形式就是内部实现细节。因此,不应以任何方式依赖本节中显示的生成的SQL代码的详细信息。

EntityValue和EntityList调用

你可以从一些基本的开始实体值呼叫:
这将对应于一个简单的SQL选择声明如下:
选择办公室代码,城市,州,国家
来自办公室
在使用基于实体函数在里面实体值,它们在SQL端以自动生成的属性名称作为别名。
例如,以下查询:
转换为如下SQL代码:
选择REGEXP(state,'^C',0)作为synthetic_prop_9
来自办公室
生成的属性如“合成_提升_9”对用户不可见,因为实体值当属性基于实体函数请求。
对于以下情况实体列表,数据库调用实际提取计算规范名称对于给定类型的实体(对应数据库表的主键)。
例如,对于“办公室”类型:
那将是“办公代码”属性:
选择办公室代码
来自办公室

计算属性和EntityFunction表达式

计算属性使用定义实体函数。这些通常会编译成SQL表达式。这里有几个例子。
考虑一下“订单详细信息”类型。表达式:
将编译为类似于以下内容的SQL表达式:
orderdetails.price每个*订单详细信息.quantity已订购
考虑一下“办公室”类型。以下内容:
将编译为:
offices.state为空
逻辑运算符的翻译相当直接。
例如:
将转换为如下内容:
REGEXP(offices.city,'a',0)或REGEXP(officescity,'o',0
在简单的情况下,算术运算会编译成类似的SQL级别的算术运算。例如,对于“产品”类型:
将编译为如下内容:
(products.MSRP-products.buyPrice)/products。建议零售价
然而,在某些情况下,生成的SQL代码可能涉及类型转换/强制。
例如,以下内容:
可以编译成类似这样的内容(其中执行到reals的转换以保留此处涉及的Wolfram Language操作在数据库端的语义):
功率(CAST((功率(CAST(products.MSRP AS REAL),2)-功率
比较运算符直接翻译。
例如,对于“订单详细信息”类型,如下所示:
将转换为如下内容:
orderdetails.quantityOrdered>30和orderdeails.priceEach>=200
例如:
将翻译为:
orderdetails.orderNumber%100=0
聚合表达式也进行了类似的转换。
例如,以下内容:
将转换为:
总和(orderdetails.quantityOrdered)
在此期间:
翻译成如下内容:
sum(orderdetails.quantityOrdered*orderdeails.priceEach)/CAST(count(orderdetails.orderLineNumber)AS REAL)总计(订单详情.quantity有序*订单详情.priceEach)
更复杂的表达式被转换为更复杂的SQL表达式。
例如,对于“员工”类型,如下所示:
将被翻译成如下内容:
案例
WHEN(1>长度(employees.firstName))THEN NULL
WHEN 1 THEN substr(employees.firstName,1,1)
结束IN('M','P','D')

核心查询生成基元

筛选的实体类

此构造通常对应于SQL哪里子句,带有第二个参数(实体函数表达式)对应于哪里子句SQL表达式。
例如,此查询:
将转化为:
选择employees.firstName、employeres.lastName、empleoyees.jobTitle
来自员工
WHERE employees.jobTitle!='销售代表
如果可以,查询编译器会尝试优化查询。特别是,连续应用几个条件通常不会导致选择在生成的SQL代码中。
例如,以下查询使用三个嵌套的筛选的实体类旨在寻找来自美国、加利福尼亚州、位于旧金山、洛杉矶或圣何塞等城市之一的所有客户:
生成的SQL将如下所示:
选择
customers.customer编号,
customers.customerName,
customers.creditLimit,
客户.城市
来自客户
WHERE customers.country='USA'AND customers.state='CA'AND customers.city IN('San Francisco','Los Angeles','San Jose')
这三种不同的条件都被压缩成了一种。

排序实体类

此构造对应于SQL订购依据条款。
对于此示例:
结果查询可能如下所示:
选择employees.employeeNumber、employers.lastName、emplyees.officeCode
来自员工
按员工订购.officeCode DESC
SQL和Entity框架排序工具之间的一个显著区别是,并非所有SQL后端都直接支持订购依据子句,while for排序实体类排序所涉及的属性可以是简单的实体属性,也可以是实体函数表达。
例如,以下查询按员工名字的长度对员工进行排序:
在这种情况下,生成的查询会变得更加复杂:
选择“T308”。“员工编号”,“T308”。“firstName”,“T308”。“姓氏”
发件人(
选择
“员工_T306”。“employeeNumber”AS“员工编号”,
“员工_T306”。“firstName”作为“firstName”,
“员工_T306”。“lastName”AS“lastName”,
长度(“employees_T306”.“firstName”)AS synthetic_prop_17
从员工到“员工_T306”
)AS“T308”
按“T308”订购。synthetic_prop_17

示例实体类

此构造最直接的SQL对应项是极限抵消SQL关键字。然而,细分的实际策略和内部实施可能不同,因为在某些情况下,其他策略可能更有效地获得相同的结果。
对于以下查询:
生成的SQL可能如下所示:
选择
“payments_T316”。“customerNumber”AS“customerNumber”,
“payments_T316”.金额AS金额
从付款中“payments_T316”
限制10偏移10

扩展实体类

在SQL端,此构造对应于使用SQL表达式定义选择列表中,只要需要计算比数据库表或查询中的原始字段更复杂的属性。
此类表达式可以相对简单,如以下查询的情况:
生成的SQL类似于:
选择
“员工_T328”。“employeeNumber”AS“员工编号”
(“employees_T328”.“firstName”||'')||“emplyees_T32.8”。“lastName”AS“全名”
来自员工AS“employees_T328”
或者,它们可能很复杂,并且包含相关子查询,例如,对于以下查询,使用关系:
其中,在生成的SQL中选择列表由(相关的)标量子查询表示:
选择
“员工_T352”。“employeeNumber”AS“员工编号”,
“员工_T352”。“firstName”作为“firstName”,
“员工_T352”。“lastName”AS“lastName”,
    (
选择“employees_T355”。“firstName”AS“firstName_1”
来自员工AS“employees_T355”
其中“employees_T355”。“employeeNumber”=“employees_T352”。“报告收件人”
)AS“经理名字”,
    (
选择“employees_T358”。“lastName”AS“lastName_1”
来自员工AS“employees_T358”
其中“employees_T358”。“employeeNumber”=“emplyees_T352”。“报告收件人”
)AS“经理姓氏”
来自员工AS“employees_T352”

聚合实体类

在没有第三个参数的情况下使用时,聚合实体类表示对整个第一个参数(实体类)执行的聚合,并对应于SQL聚合查询。在这种情况下,通常没有与聚合实体类[...],但在选择列表必须全部使用SQL聚合函数。
例如,以下查询:
将转换为类似于以下内容的SQL:
选择
max(“orderdetails_T403”.“quantityOrdered”)AS“maxOrdered“,
min(“orderdetails_T403”.“quantityOrdered”)AS“minOrdered“,
avg(“orderdetails_T403”.“quantityOrdered”)AS“avgOrdered
来自orderdetails AS“orderdeails_T403”
当第三个参数聚合实体类使用,这对应于SQL分组依据条款。
例如,查询:
将转换为如下内容:
选择
“customers_T434”.城市AS城市,
“customers_T434”.国家AS国家,
count(“customers_T434”.“customerNumber”)AS“customer count”
来自客户AS“customers_T434”
按“customers_T434”.city,“customers _T434“.country分组
需要注意的是,对于可能需要在聚合实体类,通常生成的SQL会有一个额外的层选择.
例如,如果希望根据“客户计数”字段:
生成的SQL查询现在包含一个额外级别的选择:
选择
“T497”。城市,
“T497”国家,
“T497”。“客户计数”
发件人(
选择
“customers_T495”.城市AS城市,
“customers_T495”.国家AS国家,
count(“customers_T495”.“customerNumber”)AS“customer count”
来自客户AS“customers_T495”
按“customers_T495”.city,“customers _T495“.country分组
)AS“T497”
按“T497”排序。“customerCount”DESC(客户计数)

组合实体类

此构造对应于SQL加入.
下面是这种类型的简单查询的一个示例,它组合了类型“员工”“办公室”:
生成的SQL可能如下所示:
选择
“员工_T529”。“employeeNumber”AS“员工编号”,
“员工_T529”。“firstName”作为“firstName”,
“员工_T529”。“lastName”AS“lastName”,
“T534”。城市,
“T534”。国家
从员工到“员工_T529”
加入(
选择
“offices_T532”。城市AS城市,
“offices_T532”.国家AS国家,
“办公室_T532”。“officeCode”AS“officeCode”
来自办公室AS“offices_T532”
)AS“T534”
打开“employees_T529”。“officeCode”=“T534”。“办公代码”
下面是一个考虑过的早期查询示例,它使用带有更复杂连接条件的self-join来查找所有国家/地区至少有两个不同的办事处:
它将转换为类似于以下内容的SQL(请注意,当前删除重复项在顶层实体值在Wolfram语言端执行,因此没有与众不同SQL查询中的关键字):
选择“offices_T571”。country AS country
来自办公室AS“offices_T571”
加入(
选择
“offices_T574”。country AS country_1,
“办公室_T574”。“officeCode”AS“officeCode”
FROM办公室AS“办公室_T574”
)AS“T576”
打开“offices_T571”。country=“T576”.country_1和“offices.T571”。“officeCode”!=“T576”。“办公代码”

子查询

在本教程中,子查询通常表示较大查询的一部分,可以使用实体值。在SQL方面,大多数情况下这对应于内部选择带有单个字段的语句,通常返回标量(因为只有一行或因为正在执行聚合),但有时也返回列(在英寸条款,对应于成员Q实体框架方面)。
以下示例(在前面关于子查询和返回昂贵产品的部分中已经考虑过)表示一个简单的、不相关的子查询:
它被翻译成这样的东西:
选择
“products_T583”。“productName”AS“product_Name”,
“products_T583”。“MSRP”作为“MSRP“
来自产品AS“products_T583”
其中“products_T583”。“建议零售价”>=0.9*(
选择最大值(“products_T586”.“MSRP”)
来自产品AS“products_T586”
)
其中产品内的查询是标量子查询。
以下更复杂的前一个查询版本扩展了每个产品的窗口中当前产品15美元内的产品数量,以建议零售价计算:
其中子查询位于实体函数定义“closelyPricedProductsCount”扩展属性是一个相关子查询,因为产品的筛选现在取决于当前产品的价格,必须从该筛选/聚合子查询中引用当前产品。
这将导致一个包含相关子查询的查询,其中相关子查询位于选择内部查询的列表,别名为“closelyPricedProductsCount”,成为新的扩展属性:
选择
“T637”。“产品名称”,
“T637”。“MSRP”,
“T637”。“closelyPricedProductsCount”
发件人(
选择
        (
选择“T640”。计数
发件人(
选择计数(“products_T638”.“productCode”)作为计数
来自产品AS“products_T638”
WHERE abs(“products_T638”.“MSRP”-“producss_T635”.“MSRP”)<=15
)AS“T640”
)AS“closelyPricedProductsCount”,
“products_T635”。“MSRP”作为“MSRP“,
“products_T635”。“productName”AS“productName”
来自产品AS“products_T635”
)AS“T637”
按“T637”订购。“closelyPricedProductsCount”DESC,“T637”。“MSRP”DESC
目前没有自动优化生成的SQL代码,这些代码涉及实体框架查询编译器执行的子查询(例如尝试将其转换为JOIN等)。人们应该知道在Entity框架中使用相关子查询的性能影响,这与SQL相似。

关系

关系提供了一种高级方法,可以在与给定实体类/类型(相关数据库表)相关的实体类/类中查找属性,而无需执行显式联接。它们的内部实现可以利用不同的工具来实现这一目标,例如子查询和/或联接,但这些都是对用户隐藏的。
有关可以从使用关系的查询生成的SQL类型的示例,请考虑以下查询,该查询计算每个办公室中单个员工处理的最大客户数(本教程最后一节也将考虑此问题):
对于当前版本的查询编译器,为此查询生成的SQL可能如下所示(诚然,不太容易阅读):
选择
“offices_T652”。“officeCode”AS“officeCode”,
    (
选择“T662”。synthetic_prop_20
发件人(
选择最大值(“T657”.“customerCount”)AS synthetic_prop_20
发件人(
选择
                (
选择“T660”。synthetic_prop_21
发件人(
选择计数(“customer_T658”.“customerNumber”)AS synthetic_prop_21
来自客户AS“customers_T658”
其中“employees_T655”。“employeeNumber”=“customers_T658”。“销售代表员工编号”
)AS“T660”
)AS“customerCount”
来自员工AS“employees_T655”
其中“offices_T652”。“officeCode”=“employees_T655”。“办公代码”
)AS“T657”
)AS“T662”
)AS“maxEmployeeCustomerCount”
来自办公室AS“offices_T652”
由于优化或内部实现的变化,使用关系为查询实际生成的SQL在未来可能会发生变化。
程序化查询的构造和生成

介绍

实体框架查询的符号特性允许以编程方式生成此类查询。此功能有许多用例。
其中一个用例是在多个位置重用查询的某些部分。它可以位于多个不同的查询中,也可以位于同一个查询中。
需要记住的一点是实体函数全部保留,因此,如果需要使用存储在变量中的查询的某些部分,请在实体函数,必须使用使用(或类似的构造)将该查询部分注入实体函数.

在不同查询中重用查询部件

在实践中,经常需要在几个较大的查询中重用惰性查询块。由于查询是惰性的,所以可以很容易地做到这一点。特别是,可以将查询的较小部分存储在变量中,并在较大的查询中使用这些变量。
以下示例说明了这些用法。
考虑汇总付款并为所有客户生成总付款的查询:
这些可以单独使用,例如,用于查找五个支付最高的客户:
但是,它还可以用于获取特定员工服务的所有客户的总付款:

在同一查询中重用查询部件

在某些情况下,可能需要在同一个查询和同一个较大的查询中多次重用同一查询。在SQL中,WITH关键字用于实现这一点。虽然实体框架将来可能会获得此构造的本机支持,但可以很容易地进行模拟。
以下示例说明了此类用例。
考虑以下查询,该查询通过汇总所有订单来计算每个特定产品的订购总次数:
它可以单独使用:
但它也可以用于查找订购最多的所有项目,在这种情况下,它将在查询中出现两次。注意,在这种情况下,使用使用,因为总计订单详细信息应该注射到实体函数:

编程查询生成

可以使用标准的Wolfram语言技术以编程方式生成查询表达式。
下面的示例通过构建计算各种聚合数量的聚合查询来说明这一点。
首先,生成聚合实体类:
然后执行查询,生成字符串属性名称列表:
这个示例可能有些人为,但它说明了主要思想,即可以使用标准的Wolfram语言函数和习惯用法来自动化查询构造过程。

符号查询转换

对查询及其符号性质的编程访问对于某些更高级的场景也很有用,例如非平凡的查询转换或生成。
例如,考虑常用查询构建块的运算符窗体。目前,由于各种原因,对于核心实体框架查询构建块,还没有直接的支持。然而,用户通常更喜欢这种查询编写方式,在某些情况下,它可以通过减少查询中的嵌套量使查询更具可读性。
下面是一个支持运算符形式的替代语法的简单实现。
首先可以定义一组新的符号,这些符号将用作实体框架查询原语的代理,但将支持运算符形式:
下一步是定义实体类别Q谓语:
接下来,定义“compile”函数:
最后,定义操作符表单:
您现在可以在一些示例查询中尝试此操作。
以下示例查询是以操作员风格编写的,并执行以下操作:选择五个付款最多的客户,并返回他们的客户编号、客户名称和支付的总金额,按总金额递减的顺序排序:
当然,查询的符号特性和用于符号表达式操作的出色Wolfram语言功能为我们提供了更多的可能性;这只是一个这样的例子。
实用查询构建技术

介绍

本节简要介绍了查询构造的一些实用技术,这些技术在工具箱中可能很有用。
构建复杂查询的一个障碍是,该过程可能看起来和感觉有点类似于用编译语言编写代码,在这种情况下,很难测试中间结果或代码片段,必须想出一些完整的函数或程序来编译和测试它。
本节旨在说明实体框架查询并非如此,通过适当的技术,可以使查询构建过程与Wolfram语言中的大多数其他活动一样具有交互性。

增量生成查询

用于说明本节所述技术的查询应执行以下操作:选择五个支付最高的客户,并按总金额递减的顺序返回他们的客户编号、客户名称和支付的总金额。
这里显示了几个步骤,它们从一个非常基本的查询开始,并在中间结果的指导下逐步构建感兴趣的查询。
首先可以查看“付款”表/实体类型。但出于原型设计的目的,对“付款”类型:
可以看到,对于给定的客户,通常会有多笔付款。要计算每个客户支付的总金额,需要将这些值加总,按客户编号分组:
由于还需要客户名称,因此获得客户名称的一种方法是使用组合实体类,如下所示:
请注意,在最后一个查询中,在属性列表中使用完整的实体属性[“客户”,“客户编号”],而不仅仅是“客户编号”,自组合实体类[...]现在包含两个“客户编号”属性,并且有必要消除真正请求哪个属性的歧义。
下一步是使用排序实体类具有适当规格:
接下来,使用示例实体类:
最后一步是更换目前使用的样品(示例付款)带着满满的“付款”类型:
此示例说明了如何逐步构建查询,一次一步,每次都以在前一步上构建的查询为起点,并检查中间结果。

使用单个实体创建复杂嵌套查询的原型

在实际为整个实体类型构造查询之前,使用给定实体类型的单个实体对查询进行原型化通常很有用。本节使用一个具体示例说明了此过程。
感兴趣的查询是选择至少有两名员工有客户的所有办公室。
作为起点,选择一个办公室:
在构建实际的查询之前,请使用顶级查询获取所能获得的信息。出发点是看看哪些员工在这个办公室工作:
下一个快速检查是查看这些员工为哪些客户提供服务:
注意,这是一种效率很低的方法,只有在原型阶段才有意义,因为它会导致执行大量查询。
下一步是构造一个查询,计算每个员工的客户数量。作为第一步,可以对特定的员工编号进行硬编码。例如,从前面可以看出,编号为1165的员工应该有六个客户。
以下查询证实了这一点:
下一步是删除硬编码的员工编号,可以按以下方式执行:
其中,在最后一个查询中实体函数引入了,这允许将特定的员工实体传递到查询中,而不是在查询中对其编号进行硬编码。
现在可以测试结果实体函数对某个特定办公室的所有员工进行评估,根据上述顶层分析得出的结果实体列表:
下一步可以按照相同的逻辑进行,但现在是针对办公实体。
以下是计算有问题办公室的客户员工数量的查询:
以前构造的查询已被用作内部构建块o个[“员工”]已使用,就像以前在实体列表.
现在一切都准备好了,可以构建最初的兴趣查询了。它可能如下所示:
请注意,如何使用单个实体,可以增量地构建嵌套的复杂查询,确保在每个级别都能正常工作。从技术上讲,这个特定查询包含一个双重嵌套的子查询,其中一个级别是相关的。
这种逻辑也反过来工作:给定一个功能不正常且包含复杂内部结构(涉及嵌套实体函数表达式等),可以将其分解为多个部分,并在单个实体上测试查询的内部部分,以快速定位并修复有问题的位置。
错误和错误处理
查询执行期间可能发生的各种错误分为两大类:软错误和硬错误。

软错误

软错误是指导致正常查询求值的错误,但不会返回使用完全正确的查询生成的结果。
软错误的一个例子是,试图从类型中提取不存在的属性值:
在这种情况下,缺少[“未知属性”,]返回不存在属性的值。
软错误的另一个例子是当一个人试图调用实体列表实体属性在不存在的类型上:
在这种情况下,缺少[]也会返回值。

硬错误

在本教程中,硬错误是指返回失败物体。因此,用户级错误处理可能相当于检查实体值的返回值失败物体。
下面列出了一些最常见的硬错误。

EntityFunction中的属性无效

此类错误的一个示例是在中使用无效属性实体函数.
以下尝试计算包含不存在属性的表达式“foo”:

EntityFunction中的不可编译表达式

每当实体函数包含无法安装的部件。
由于存在全局变量,以下查询失败在没有值且无法编译为SQL的查询中:
这里也发生了同样的情况,但这次是因为数据库端贝塞尔J当前不支持计算:

EntityFunction中表达式的类型不兼容

硬错误的另一个常见来源是在中使用了错误类型的表达式实体函数.
在以下查询中更大的运算符不能接受字符串参数:
在以下情况下,试图将整数和字符串相加会导致类型错误:
通常,在这种情况下,错误消息在识别错误原因方面信息量很大。

查询中存在不支持的类型的值

关系数据库上下文中的实体框架目前不支持某些类型的Wolfram语言表达式。
以下查询失败,因为的值10^100太大,无法在数据库查询中使用:
然而,以下内容并未失败,因为在这里,数字已明确转换为机器精度双精度:
数据库后端不直接支持复数,因此以下内容也会导致错误:

EntityFunction中的返回类型不兼容

一些操作需要实体函数作为参数,需要特定的返回类型。例如,在中使用时筛选的实体类组合实体类,实体函数应具有布尔返回类型。
在以下示例中,筛选谓词的返回类型实体函数是整数,而布尔类型是必需的:

尝试从EntityFunction返回非标量

目前不支持此类功能。
下面是一个示例,其中实体函数语义上是值的列表,该类型不能从实体函数:

聚合中关系查找的使用不当

通过在同一聚合操作中组合不同表中的列,可以使用关系构造无效的聚合查询。这样的查询无法编译。
以下查询失败,因为减法实际上是用不同表的不同列进行的,这不是有效的查询:

未在AggregatedEntityClass的EntityFunction中使用聚合函数

用于计算中聚合属性的表达式实体函数对于聚合,必须使用其中一个聚合函数。从技术上讲,可以构造不需要编译的查询。
以下查询失败,因为实体函数这里本质上是一种“列”类型(值列表),而不是标量这看起来类似于已经讨论过的另一个错误,但这里是在聚合的上下文中:
以下操作不会失败,因为它包含一个聚合函数(总计在这种情况下),从而使身体实体函数是标量:

操作错误

操作错误是发生在数据库端的错误。虽然实体框架努力尽早拦截查询中的大多数语法错误、类型相关错误和其他错误,但在某些情况下,这要么尚未完成,要么可能不容易完成。
下面的示例尝试扩展类型“办公室”具有一个实际上是实体类的新属性。数据库支持的实体目前不支持此类操作,这种情况下的错误发生在数据库端:
将所有内容结合在一起:更复杂的查询示例
最有趣的现实世界问题需要以非平凡的方式将几个查询构建原语组合在一起的查询。本节通过一些更有趣的示例说明了如何实现这一点。

示例:按客户支付的总金额排序的客户

以下查询扩展了“客户”具有属性的类型“支付总额”它给出了该客户支付的总金额,然后进行排序“支付总额”按降序排列的值。
一种解决方案是使用相关的子查询。
以下是使用相关子查询的版本:
同样可以使用组合实体类和带分组的聚合(注意这里必须添加“客户”在聚合到用于分组的属性列表后,希望可用的类型。即使只有一个值“客户名称”对应于的特定值“客户编号”,除非我们明确地告诉它,否则查询不会知道这一点)。
以下版本使用组合实体类:
通过使用关系,人们可以更经济地做同样的事情。
此版本使用关系:

示例:每个办事处处理的客户总数

处理这类问题的一种方法是使用组合实体类将这些类型组合在一起,然后对组合的类型进行聚合,对某些属性进行分组。
以下查询计算每个办公室中所有员工服务的客户总数。它通过结合三种类型来实现这一点:“办公室”,“员工”“客户”,然后对办公代码值执行聚合:
请注意,在最后一个查询中,使用较长的表单很重要实体属性[“办公室”,“办公代码”]而不是“办公代码”消除财产的歧义,因为组合实体类[“办公室”,“员工”,“办公代码”]包含两个短名称属性“officeCode”(办公室代码)":实体属性[“办公室”,“办公代码”]实体属性[“员工”,“办公代码”](在这种情况下,可以使用任何一种)。
下面是前面查询的更复杂版本,它计算每个办公室服务的客户总数居住在该办事处所在国。在这种情况下,必须使用实体属性对于“国家”属性以消除属性的歧义,因为这两种类型“办公室”“客户”拥有财产“国家”:

示例:每个办公室每个员工的最大客户数

对于这种情况,关系通常提供非常经济的解决方案。
以下查询为每个办公室计算该办公室中单个员工处理的最大客户数量。它严重依赖关系:
为了了解这里的工作关系为用户做了多少工作,下面是当前版本的查询编译器为此查询生成的SQL:
选择
“offices_T652”。“officeCode”AS“officeCode”,
    (
选择“T662”。synthetic_prop_20
发件人(
选择最大值(“T657”.“customerCount”)AS synthetic_prop_20
发件人(
选择
                (
选择“T660”。synthetic_prop_21
发件人(
选择计数(“customers_T658”.“customerNumber”)AS synthetic_prop_21
来自客户作为“客户_T658”
其中“employees_T655”。“employeeNumber”=“customers_T658”。“销售代表员工编号”
)AS“T660”
)AS“customerCount”
来自员工AS“employees_T655”
其中“offices_T652”。“officeCode”=“employees_T655”。“办公代码”
)AS“T657”
)AS“T662”
)AS“maxEmployeeCustomerCount”
来自办公室AS“offices_T652”

示例:欠钱的客户

这是另一个展示关系力量的窗口。任务是选择仍然欠款的客户换言之,那些订单总额超过迄今为止总付款金额的人。
要计算要支付的总金额,可以从给定客户的所有订单开始,这些订单由关系给出c(c)[“订单”]并且是类型为的实体类“订单”。由于每个订单可能包含多个项目,并且每个项目可能订购多个数量,下一步是使用属性扩展此类“totalToPay”,它通过联系所有人来计算每个订单的支付金额“订单详细信息”与此订单相关的实体,并计算商品价格和订购数量的乘积的总和。注意这里的关系o个[“订单详细信息”]用于每个订单。然后可以进行第二次聚合,这一次是对给定客户的所有订单进行聚合,以获得他们应该支付的总金额。
计算客户已经支付的总金额要简单得多,因为它需要一次关系查找c(c)[“付款”]引入小截点0.00001以避免舍入误差。
查询如下:
要在不使用关系的情况下获得相同的结果,需要做大量的工作。
以下查询是一种可能的方法,它使用相关的子查询和组合实体类.
这个使用在最后一个例子中用于可读性,而不是必需的;可以使用单个大型查询。但请注意使用与一起使用:=初始化作用域变量,以便将它们注入main的主体实体函数没有评估。

示例:五大支付客户为每位员工支付的总金额

本例的目标是计算每位员工所服务的五位薪酬最高的客户应支付的总金额,并按总金额的降序对员工进行排序(可用于衡量给定员工的成功程度)。
在这里使用关系可以大大简化查询。
以下查询正在使用关系。它说明了如何方便地遵循关系和计算聚合(如总计[c(c)[“付款”][“金额”]](见下文)。注意,在这种情况下,使用关系可以使用三个不同数据库表中的数据(“员工”,“客户”“付款”)以简洁、经济的方式:
以下版本不使用关系。
同样,在不使用关系的情况下获得相同的结果需要付出更多的努力:
在前面的示例中,正如前面的示例一样,使用主要用于可读性。