Contents

  • 3.1 SQL 查询语言概览
  • 3.2 SQL 数据定义
    • 3.2.1基本类型
    • 3.2.2 基本模式定义
  • 3.3 SQL查询的基本结构
    • 3.3.1 单关系查询
    • 3.3.2 多关系查询
    • 3.3.3 自然连接
  • 3.4 附加的基本运算
    • 3.4.1 更名运算
    • 3.4.2 字符串运算
    • 3.4.3 select 子句中的属性说明
    • 3.4.4 排列元组的显示次序
    • 3.4.5 where 子句谓词
  • 3.5 集合运算
    • 3.5.1 并运算
    • 3.5.2 交运算
    • 3.5.3 差运算
  • 3.6 空值
  • 3.7聚集函数
    • 3.7.1基本聚集
    • 3.7.2分组聚集

● 商业性使用或实验性使用的数据库査询语言有好几种。学习使用最为广泛的査询语言: SQL 。

● 尽管我们说 SQL 语言是一种“查询语言”,但是除了数据库查询,它还具有很多别的功能,它可以定义数据结构、修改数据库中的数据以及说明安全性约束条件等。

● 我们的目的并不是提供一个完整的 SQL 用户手册,而是介绍 SQL 的基本结构和概念。 SQL 的各种实现可能在一些细节上有所不同,或者只支持整个语言的一个子集。

3.1 SQL 查询语言概览

● SQL 最早的版本是由 IBM 开发的,它最初被叫做 Sequel ,在20世纪70年代早期作为 System R 项目的一部分。 Sequel 语言一直发展至今,其名称已变为 SQL (结构化查询语言)。现在有许多产品支持 SQL 语言, SQL 已经很明显地确立了自己作为标准的关系数据库语言的地位。

● 1986年美国国家标准化组织( ANSI )和国际标准化组织( ISO )发布了SQL 标准: SQL -86。

● 1989年 ANSI 发布了一个 SQL 的扩充标准: SQL -89。该标准的下一个版本是 SQL -92标准,接着是 SQL :1999, SQL :2003, SQL :2006,最新的版本是 SQL :2008。

●SQL 语言有以下几个部分:

• 数据定义语言( Data - Definition Language , DDL ): SQLDDL 提供定义关系模式、删除关系以及修改关系模式的命令。
• 数据操纵语言( Data - Manipulation Language , DML ): SQL DML 提供从数据库中查询信息,以及在数据库中插人元组、删除元组、修改元组的能力。
• 完整性( integrity ): SQL DDL 包括定义完整性约束的命令,保存在数据库中的数据必须满足所定义的完整性约束。破坏完整性约束的更新是不允许的。
• 视图定义( view definition ): SQL DDL 包括定义视图的命令。
• 事务控制( transaction control ): SQL 包括定义事务的开始和结束的命令。
• 嵌入式 SQL 和动态 SQL ( embedded SQL and dynamic SQL ):嵌人式和动态 SQL 定义 SQL 语句如何嵌人到通用编程语言,如 C 、 C ++和 Java 中。
• 授权( authorization ): SQL DDL 包括定义对关系和视图的访问权限的命令。

● 对 SQL 的基本 DML 和 DDL 特征的概述。在此描述的特征自 SQL -92以来就一直是 SQL 标准的部分。

3.2 SQL 数据定义

数据库中的关系集合必须由数据定义语言( DDL )指定给系统。 SQL 的 DDL 不仅能够定义一组关系,还能够定义每个关系的信息,包括:
●每个关系的模式。

●每个属性的取值类型。

●完整性约束。

●每个关系维护的索引集合。

●毎个关系的安全性和权限信息。

●每个关系在磁盘上的物理存储结构。

在此只讨论基本模式定义和基本类型

3.2.1基本类型

SQL 标准支持多种固有类型,包括:

● char ( n ):固定长度的字符串,用户指定长度 n 。也可以使用全称 character 。

● varchar ( n ):可变长度的字符串,用户指定最大长度 n ,等价于全称 character varying 。

● int ;整数类型(和机器相关的整数的有限子集),等价于全称 integer 。

● smallint :小整数类型(和机器相关的整数类型的子集)。

● numeric ( p , d ):定点数,精度由用户指定。这个数有 p 位数字(加上一个符号位),其中 d 位数字在小数点右边。所以在一个这种类型的字段上, numeric (3,1)可以精确储存44.5,但不能精确存储444.5或0.32这样的数。

● real , double precision :浮点数与双精度浮点数,精度与机器相关。

● float ( n ):精度至少为 n 位的浮点数。

● 每种类型都可能包含一个被称作空值的特殊值。空值表示一个缺失的值,该值可能存在但并不为人所知,或者可能根本不存在。在可能的情况下,我们希望禁止加人空值,正如我们马上将看到的那样。

● char 数据类型存放固定长度的字符串。

● 例如,属性 A 的类型是 char (10)。如果我们为此属性存人字符串“ Avi ”,那么该字符串后会追加7个空格来使其达到10个字符的串长度。反之,如果属性 B 的类型是 varchar (10),我们在属性 B 中存人字符串“ Avi ”,则不会增加空格。

● 当比较两个 char 类型的值时,如果它们的长度不同,在比较之前会自动在短值后面加上额外的空格以使它们的长度一致。

● 当比较一个 char 类型和一个 varchar 类型的时候,也许读者会期望在比较之前会自动在 varchar 类型后面加上额外的空格以使长度一致;然而,这种情况可能发生也可能不发生,这取决于数据库系统。其结果是,即便上述属性 A 和 B 中存放的是相同的值“ Avi ”, A = B 的比较也可能返回假。

● 我们建议始终使用 varchar 类型而不是 char 类型来避免这样的问题。

● SQL 也提供 nvarchar 类型来存放使用 Unicode 表示的多语言数据。然而,很多数据库甚至允许在 varchar 类型中存放 Unicode (采用 UTF -8表示)。

3.2.2 基本模式定义

用create table 命令定义SQL关系,下面的命令在数据库中创建了一个department 关系:

create table department
(dept name varchar (20),
building varchar (15),
budget numeric (12,2),
primary key (dept name));

上面创建的关系具有三个属性,dept_name是最大长度为20的字符串,building 是最大长度为15 的字符串,budget 是一个12位的数,其中2位数字在小数点后面。create table命令还指明了dept_name 属性是department关系的主码;

create table 命令的通用形式是:

integrity-constraint :完整性约束

其中r 是关系名,每个Ai 是关系r 模式中的一个属性名,Di是属性Ai的域,也就是说Di 指定了属性Ai的类型以及可选的约束,用于限制所允许的Ai取值的集合;

create table 命令后面用分号结束;

SQL支持许多不同的完整性约束,其中少数几个如下:
● primary key ( A j1, Aj2 ,…, Ajm ): primary - key 声明表示属性 A j1, Aj2 ,…, Ajm 构成关系的主码。主码属性必须非空且唯一,也就是说没有一个元组在主码属性上取空值,关系中也没有两个元组在所有主码属性上取值相同。虽然主码的声明是可选的,但为每个关系指定一个主码通常会更好。

● foreign key ( Ak1 , Ak2 ,…, Akm) references : foreign key 声明表示关系中任意元组在属性( Ak1 , Ak2 ,…, Akm)上的取值必须对应于关系 s 中某元组在主码属性上的取值。

图3-1给出了我们在书中使用的大学数据库的部分 SQL DDL 定义。 course 表的定义中声明了“ foreign key ( dept _ name ) references department "。此外码声明表明对于毎个课程元组来说,该元组所表示的系名必然存在于 department 关系的主码属性( dept _ name )中。没有这个约束的话,就可能有某门课程指定了一个不存在的系名。图3-1还给出了表 section 、 instructor 和 teaches 上的外码约束。

create table department
(dept_name varchar (20),
building     varchar (15),
budget       numeric (12,2),
primary key (dept_name));create table course
(course_id   varchar (7),
title      varchar (50),
dept_name   varchar (20),
credits     numeric (2,0),
primary key (course_id),
foreign key (dept_name) references department);create table instructor
(ID  varchar (5),
name  varchar (20) not null,
dept_name  varchar (20),
salary  numeric (8,2),
primary key (ID),
foreign key (dept_name) references department);create table section
(course_id   varchar (8),
sec_id     varchar (8),
semester   varchar (6),
year      numeric (4,0),
building    varchar (15),
room_number   varchar (7),
time_slot_id    varchar (4),
primary key (course_id, sec_id, semester, year),
foreign key (course_id) references course);create table teaches
(ID varchar (5),
course_id varchar (8),
sec_id varchar (8),
semester varchar (6),
year numeric (4,0),
primary key (ID, course_id, sec_id, semester, year),
foreign key (course_id, sec_id, semester, year) references section,
foreign key (ID) references instructor);

● not null :一个属性上的 not null 约束表明在该属性上不允许空值。换句话说,此约束把空值排除在该属性域之外。

例如在图3-1中, instructor 关系的 name 属性上的 not null 约束保证了教师的姓名不会为空。

● SQL 禁止破坏完整性约束的任何数据库更新。

例如,如果关系中一条新插人或新修改的元组在任意一个主码属性上有空值,或者元组在主码属性上的取值与关系中的另一个元组相同, SQL 将标记一个错误,并阻止更新。

类似地,如果插人的 course 元组在 dept _ name 上的取值没有出现在 department 关系中,就会破坏 course 上的外码约束, SQL 会阻止这种插人的发生。

● 一个新创建的关系最初是空的。我们可以用 insert 命令将数据加载到关系中。例如,如果我们希望插入如下事实:在 Biology 系有一个名叫 Smith 的教师,其 instructor _ id 为10211,工资为66000美元,可以这样写:
insert into instructor values (10211, ’Smith’, ’Biology’, 66000);

值被给出的顺序应该遵循对应属性在关系模式中列出的顺序;

● 使用 delete 命令从关系中删除元组。命令:

 delete from student ;

将从 student 关系中删除所有元组。其他格式的删除命令允许指定待删除的元组;

如果要从 sQL 数据库中去掉一个关系,我们使用 drop table 命令。 drop table 命令从数据库中删除关于被去掉关系的所有信息。命令:

 drop table r ;

是比

 delete from  r ;

更强的语句。后者保留关系 r ,但删除 r 中的所有元组。前者不仅删除 r 的所有元组,还删除 r 的模式。一旦 r 被去掉,除非用 create table 命令重建 r ,否则没有元组可以插人到 r 中。

● 使用 alter table 命令为已有关系增加属性。关系中的所有元组在新属性上的取值将被设为 null 。 alter table 命令的格式为:

 alter table r add A D ;

其中 r 是现有关系的名字, A 是待添加属性的名字, D 是待添加属性的域。

可以通过命令

 alter table r drop A ;

从关系中去掉属性。其中 r 是现有关系的名字, A 是关系的一个属性的名字。很多数据库系统并不支持去掉属性,尽管它们允许去掉整个表。

3.3 SQL查询的基本结构

SQL查询的基本结构由三个子句构成:select 、from 和where;查询的输入是在from子句中列出的关系,在这些关系上进行where 和select 子句中指定的运算,然后产生一个关系作为结果;

3.3.1 单关系查询

● 用大学数据库例子做一个简单查询:‘找出所有教师的名字’ ;教师的名字可以从instructor 关系中找到,因此应该把关系放到from子句中;教师的名字出现在name属性中,因此把它放到select子句中;

select name from instructor;

其结果为由属性名为name 的单个属性构成的关系;其查询结果如图3-2所示:

● 另外一个查询:‘找出所有教师所在的系名’,其查询可以写为:

select dept_name from instructor;

因为一个系由多个教师,所以在instructor 关系中,一个系的名称可以出现多次;故查询结果如图3-3所示:

● 在关系模型的形式化数学定义中,关系是一个集合。因此,重复的元组不会出现在关系中。在实践中,去除重复是相当费时的,所以 SQL 允许在关系以及 SQL 表达式结果中出现重复。因此,在上述 SQL 査询中,每个系名在 instructor 关系的元组中每出现一次,都会在查询结果中列出一次。

● 有时候我们想要强行删除重复,可在 select 后加人关键词 distinct 。如果我们想去除重复,可将上述查询重写为:

 select distinct dept _ name  from instructor ;

在上述査询的结果中,每个系名最多只出现一次。

● SQL 允许我们使用关键词 all 来显式指明不去除重复:

 select all dept _ name  from insiructor ;

既然保留重复元组是默认的,那么就可以不再使用all;

● select 子句还可带含有+、-、*、/运算符的算术表达式,运算对象可以是常数或元组的属性。
例如,查询

 select ID , name , dept _name , salary *1.1  from instructor ;

返回一个与 instructor 一样的关系,只是属性 salary 的值是原来的1.1倍。这显示了如果我们给每位教师增长10%的工资的结果。注意这并不导致对 instructor 关系的任何改变。

● SQL 还提供了一些特殊数据类型,如各种形式的日期类型,并允许一些作用于这些类型上的算术函数。

● where 子句允许我们只选出那些在 from 子句的结果关系中满足特定谓词的元组。考虑查询“找出所有在 computer Science 系并且工资超过70000美元的教师的姓名”,该査询用 SQL 可以写为:

 select name   from instructor where dept _ name =‘ comp . Sci .’  and salary >70000;

如果 instructor 关系如图2-1所示,那么上述查询的结果关系如图3-4所示。

● SQL 允许在 where 子句中使用逻辑连词 and 、 or 和 not 。逻辑连词的运算对象可以是包含比较运算符<、<=、>、>=、=和<>的表达式。SQL 允许我们使用比较运算符来比较字符串、算术表达式以及特殊类型,如日期类型。

3.3.2 多关系查询

● 查询:‘找出所有教师的姓名,以及他们所在系的名称和系所在的建筑名称’;

考虑到instructor 关系的模式,发现可以从dept_name 属性得到系名,但是系所在建筑的名称是在department 关系的building 属性中给出的;为了回答查询,instructor 关系中的每个元组必须与的department关系中的元组匹配,后者在dept_name 上的取值相配于instructor元组在dept_name上的取值;

可以写作为:

select name, instructor.dept_name, building
from instructor, department
where instructor.dept_name= department.dept_name;

其查询结果如图3-5 所示:

● 注意 dept _ name 属性既出现在 instructor 关系中,也出现在 department 中,关系名被用作前缀(在 instructor . dept _ name 和 department . dept _ name 中)来说明我们使用的是哪个属性。相反,属性 name 和 building 只出现在一个关系中,因而不需要把关系名作为前缀。

这种命名惯例需要出现在 from 子句中的关系具有可区分的名字。在某些情况下这样的要求会引发问题,比如当需要把来自同一个关系的两个不同元组的信息进行组合的时候。

● 现在我们考虑涉及多个关系的 SQL 査询的通用形式。正如我们前面已经看到的,一个 SQL 査询可以包括三种类型的子句:select 子句、from 子句和where 子句,每种子句的作用如下:

● select 子句用于列出查询结果中所需要的属性。
● from 子句是一个査询求值中需要访间的关系列表。
● where 子句是一个作用在 from 子句中关系的属性上的谓词。

● 一个典型的 SQL 査询具有如下形式:

select A1, A2, . . . , An
from r1, r2, . . . , rm
where P;

每个 A ,代表一个属性,每个 r ,代表一个关系。 P 是一个谓词。如果省略 where 子句,则谓词 P 为 true 。

● 尽管各子句必须以 select 、 from 、 where 的次序写出,但理解查询所代表运算的最容易的方式是以运算的顺序来考察各子句:首先是 from ,然后是 where ,最后是 selecte ;

● 通过 from 子句定义了一个在该子句中所列出关系上的笛卡儿积。它可以用集合理论来形式化地定义,但最好通过下面的迭代过程来理解,此过程可为 from 子句的结果关系产生元组。

for each   元组t1,    in  关系r1for each   元组t2, in  关系r2 . . . for each   元组tnm   in  关系rm把t1,t2,…,连接成单个元组t把t加入结果关系中

此结果关系具有来自 from 子句中所有关系的所有属性。由于在关系 ri和 rj 中可能出现相同的属性名,正如我们此前所看到的,我们在属性名前加上关系名作为前缀,表示该属性来自于哪个关系。

● 例如,关系 instructor 和 teaches 的笛卡儿积的关系模式为:

( instructor . ID , instructor . name , instructor . dept _ name , instructor . salary teaches . ID , teaches . course _ id , teaches . sec _ id , teaches . semester , teaches . year )

有了这个模式,我们可以区分出 instructor . ID 和 teaches . ID 。对于那些只出现在单个模式中的属性,我通常去掉关系名前缀。这种简化并不会造成任何混淆。这样我们可以把关系模式写为:

( instructor . ID , name , dept _ name , salary teaches . ID , course _ id , sec _ id , semester , year )

● 为举例说明,考察图2-1中的 instructor 关系和图2-7中的 teaches 关系。它们的笛卡儿积如图3-6所示,图中只包括了构成笛卡儿积结果的一部分元组。

● 通过笛卡儿积把来自 instructor 和 teaches 中相互没有关联的元组组合起来。 instructor 中的每个元组和 teaches 中的所有元组都要进行组合,即使是那些代表不同教师的元组。其结果可能是一个非常庞大的关系,创建这样的笛卡儿积通常是没有意义的。

● 反之, where 子句中的谓词用来限制笛卡儿积所建立的组合,只留下那些对所需答案有意义的组合。我们希望有个涉及 instructor 和 teaches 的査询,它把 instructor 中的特定元组 t 只与 teaches 中表示跟 t 相同教师的元组进行组合。也就是说,我们希望把 teaches 元组只和具有相同 ID 值的 insiructor 元组进行匹配。下面的 SQL 査询满足这个条件,从这些匹配元组中输出教师名和课程标识。

select name, course_id
from instructor, teaches
where instructor.ID= teaches.ID;

● 注意上述査询只输出讲授了课程的教师,不会输出那些没有讲授任何课程的教师。如果我们希望输出那样的元组,可以使用一种被称作外连接的运算。

● 如果 instructor 关系如图2-1所示, teaches 关系如图2-7所示,那么前述查询的结果关系如图3-7所示。注意教师 Gold 、 Califieri 和 Singh ,由于他们没有讲授任何课程,就不出现在上述结果中。

● 如果我们只希望找出 computer Science 系的教师名和课程标识,我们可以在 where 子句中增加另外的谓词,如下所示:

select name, course_id
from instructor, teaches
where instructor.ID= teaches.ID and instructor.dept_name = ’Comp. Sci.’;

● 注意既然 dept _ name 属性只出现在 instructor 关系中,我们在上述査询中可以只使用 dept _ name 来替代 instructor . dept _ name 。

通常说来,一个 SQL 查询的含义可以理解如下:

1.为 from 子句中列出的关系产生笛卡儿积。

2.在步骤1的结果上应用 where 子句中指定的谓词。

3.对于步骤2结果中的每个元组,输出 select 子句中指定的属性(或表达式的结果)。

● 上述顺序有助于明白一个SQL查询的结果应该是什么样的,而不是这届结果是怎么执行的;在SQL的实现实际中不会执行这种形式的查询,它会通过(尽可能)只产生满足 where 子句谓词的笛卡儿积元素来进行优化执行。

当书写查询时,需要小心设置合适的 where 子句条件。如果在前述 SQL 查询中省略 where 子句条件,就会输出笛卡儿积,那是一个巨大的关系。

● 对于图2-1中的 instructor 样本关系和图2-7中的 tenches 样本关系,它们的笛卡儿积具有12x13=156个元组,比我们在书中能显示的还要多!在更糟的情况下,假设我们有比图中所示样本关系更现实的教师数量,比如200个教师。假使每位教师讲授3门课程,那么我们在 teaches 关系中就有600个元组。这样上述迭代过程会产生出200x600=120000个元组作为结果。

3.3.3 自然连接

● 在我们的查询示例中,需要从 instructor 和 teaches 表中组合信息,匹配条件是需要 instructor . ID 等于 teaches . ID 。这是在两个关系中具有相同名称的所有属性。实际上这是一种通用的情况,也就是说, from 子句中的匹配条件在最通常的情况下需要在所有匹配名称的属性上相等。

● 为了在这种通用情况下简化 SQL 编程者的工作, SQL 支持一种被称作自然连接的运算,下面我们就来讨论这种运算。

● 事实上 SQL 还支持几种另外的方式使得来自两个或多个关系的信息可以被连接( join )起来。我们已经见过怎样利用笛卡儿积和 where 子句谓词来连接来自多个关系的信息。

● 自然连接( natural join )运算作用于两个关系,并产生一个关系作为结果。不同于两个关系上的笛卡儿积,它将第一个关系的每个元组与第二个关系的所有元组都进行连接;自然连接只考虑那些在两个关系模式中都出现的属性上取值相同的元组对。因此,回到 instructor 和 teaches 关系的例子上, instructor 和 teaches 的自然连接计算中只考虑这样的元组对:来自instructor的元组 和 teaches的元组在共同属性ID上的取值相同;

● 结果关系如图3-8所示,只有13个元组,它们给出了关于每个教师以及该教师实际讲授的课程的信息。注意我们并没有重复列出那些在两个关系模式中都出现的属性,这样的属性只出现一次。还要注意列出属性的顺序:先是两个关系模式中的共同属性,然后是那些只出现在第一个关系模式中的属性,最后是那些只出现在第二个关系模式中的属性。

● 考虑查询“对于大学中所有讲授课程的教师,找出他们的姓名以及所讲述的所有课程标识”,此前我们曾把该查询写为:

select name, course id
from instructor, teaches
where instructor.ID= teaches.ID;

该查询可以用SQL的自然连接运算更简洁的写作:(这两个查询的结果一致)

select name, course id
from instructor natural join teaches;

● 正如我们此前所见,自然连接运算的结果是关系。

● 从概念上讲, from 子句中的“ instructor natural join teaches ”表达式可以替换成执行该自然连接后所得到的关系。然后在这个关系上执行 where 和 select 子句。

●在一个 SQL 査询的 from 子句中,可以用自然连接将多个关系结合在一起,如下所示:

select A1, A2, . . . , An
from r1 natural join r2 natural join . . . natural join rm
where P;

更为一般地, from 子句可以为如下形式:

from E1, E2, . . . , En

其中毎个 Ei 可以是单个关系或一个包含自然连接的表达式。
● 例如,假设我们要回答查询“列出教师的名字以及他们所讲授课程的名称”。此查询可以用 SQL 写为:

 select name , title from instructor natural join teaches , course where teaches , course _ id = course , course _ id ;

先计算 instructor 和 teaches 的自然连接,正如我们此前所见,再计算该结果和 course 的笛卡儿积, where 子句从这个结果中提取出这样的元组:来自连接结果的课程标识与来自 course 关系的课程标识相匹配。注意 where 子句中的 teaches . course _ id 表示自然连接结果中的 course _ id 域,因为该域最终来自 teaches 关系。

●相反,下面的 SQL 查询不会计算出相同的结果:

select name, title
from instructor natural join teaches natural join course;

● 为了说明原因,注意 instructor 和 teaches 的自然连接包括属性( ID , name , dept _ name , salary , course _ id , sec _ id ),而 course 关系包含的属性是( course _ id , title , dept _ name , credits);作为两者自然连接的结果,需要来自这两个输人的元组既要在属性 dept _ name 上取值相同,还要在 course _ id 上取值相同。该查询将忽略所有这样的(教师姓名,课程名称)对:其中教师所讲授的课程不是他所在系的课程。而前一个查询会正确输出这样的对。

● 为了发扬自然连接的优点,同时避免不必要的相等属性带来的危险, SQL 提供了一种自然连接的构造形式,允许用户来指定需要哪些列相等。下面的査询说明了这个特征:

select name, title
from (instructor natural join teaches) join course using (course id);

join … using 运算中需要给定一个属性名列表,其两个输人中都必须具有指定名称的属性。考虑运算 r1 join r2 using ( A1 ,A2),它与r1和 r2的自然连接类似,只不过在t1.A1=t2. A1 并且t1.A2=t2. A2 成立的前提下,来自 r1的元组t1和来自 r2的元组t2就能匹配,即使 r1 和 r2 都具有名为 A3 的属性,也不需要t1 . A3 = t2.A3成立。

● 这样,在前述 SQL 査询中,连接构造允许 teaches . dept _ name 和 course . dept _ name 是不同的,该 SQL 查询给出了正确的答案。

3.4 附加的基本运算

SQL中支持几种附加的运算

3.4.1 更名运算

● 重新考察我们此前使用过的查询:

select name, course id
from instructor, teaches
where instructor.ID= teaches.ID;

● 此査询的结果是一个具有下列属性的关系:

 name , course _ id

结果中的属性名来自 from 子句中关系的属性名。

● 但我们不能总是用这个方法派生名字,其原因有几点:首先, from 子句的两个关系中可能存在同名属性,在这种情况下,结果中就会出现重复的属性名;其次,如果我们在 select 子句中使用算术表达式,那么结果属性就没有名字;再次,尽管如上例所示,属性名可以从基关系导出,但我们也许想要改变结果中的属性名字。因此, SQL 提供了一个重命名结果关系中属性的方法。即使用如下形式的 as 子句:

old-name as new-name

● as 子句既可出现在 select 子句中,也可出现在 from 子句中。

● 例如,如果我们想用名字 instructor _ name 来代替属性名 name ,我们可以重写上述査询如下:

select name as instructor name, course id
from instructor, teaches
where instructor.ID= teaches.ID;

● as 子句在重命名关系时特别有用。重命名关系的一个原因是把一个长的关系名替换成短的,这样在查询的其他地方使用起来就更为方便。为了说明这一点,我们重写查询“对于大学中所有讲授课程的
教师,找出他们的姓名以及所讲述的所有课程标识”:

select T.name, S.course id
from instructor as T, teaches as S
where T.ID= S.ID;

● 重命名关系的另一个原因是为了适用于需要比较同一个关系中的元组的情况。为此我们需要把一个关系跟它自身进行笛卡儿积运算,如果不重命名的话,就不可能把一个元组与其他元组区分开来。假设我们希望写出査询:“找出满足下面条件的所有教师的姓名,他们的工资至少比 Biology 系某一个教师的工资要高”,我们可以写出这样的 SQL 表达式:

select distinct T.name
from instructor as T, instructor as S
where T.salary > S.salary and S.dept name = ’Biology’;

● 注意我们不能使用 instructor . salary 这样的写法,因为这样并不清楚到底是希望引用哪一个 instructor 。

● 在上述查询中, T 和 S 可以被认为是 instructor 关系的两个拷贝,但更准确地说是被声明为 instructor 关系的别名,也就是另外的名字。像 T 和 S 那样被用来重命名关系的标识符在 SQL 标准中被称作相关名称( correlation name ),但通常也被称作表别名( table alias ),或者相关变量( correlation variable ),或者元组变量( tuple variable )。

● 注意用文字表达上述査询更好的方式是:“找出满足下面条件的所有教师的姓名,他们比 Biology 系教师的最低工资要高”。

3.4.2 字符串运算

① SQL 使用一对单引号来标示字符串,例如 computer '。如果单引号是字符串的组成部分,那就用两个单引号字符来表示,如字符串“ it’s right '可表示为“ it ” s right ”。

② 在 SQL 标准中,字符串上的相等运算是大小写敏的,所以表达式“ 'comp . sci .'=' comp . Sci .'“的结果是假。然而一些数据库系统,如 MySQL 和 SQL Server ,在匹配字符串时并不区分大小写,所以在这些数据库中“' comp . sci .'=’ comp . Sci .'”的结果可能是真。然而这种默认方式是可以在数据库级或特定属性级被修改的。

③ SQL 还允许在字符串上有多种函数,例如串联(使用“ || ”)、提取子串、计算字符串长度、大小写转换(用 upper ( s )将字符串 s 转换为大写或用 lower ( s )将字符串 s 转换为小写)、去掉字符串后面的空格(使用 trim ( s )),等等。不同数据库系统所提供的字符串函数集是不同的,请参阅你的数据库系统手册来获得它所支持的实际字符串函数的详细信息。

④ 在字符串上可以使用 like 操作符来实现模式匹配。我们使用两个特殊的字符来描述模式:
●百分号(%):匹配任意子串。
●下划线(_):匹配任意一个字符。
模式是大小写敏感的,也就是说,大写字符与小写字符不匹配,反之亦然。为了说明模式匹配,考虑下列例子:

   ●‘  Intro %'匹配任何以“ Intro "打头的字符串。●‘% comp %’匹配任何包含“ Comp ”子串的字符串,例如‘ Intro , to Computer Science ’和 ‘ computational Biology '。●‘_  _  _   '匹配只含三个字符的字符串。●‘_  _  _ % ' 匹配至少含三个字符的字符串。

⑤ 在 SQL 中用比较运算符 like 来表达模式。考虑查询“找出所在建筑名称中包含子串‘ Watson '的所有系名”,该査询的写法如下:

select dept name
from department
where building like ’%Watson%’;

⑥ 为使模式中能够包含特殊模式的字符(即%和_), SQL 允许定义转义字符。转义字符直接放在特殊字符的前面,表示该特殊字符被当成普通字符。我们在 like 比较运算中使用 escape 关键词来定义转义字符。为了说明这一用法,考虑以下模式,它使用反斜线( \)作为转义字符:

● like ‘ ab \% cd %’ escape ‘ '\ ’匹配所有以" ab % cd ”开头的字符串。●  like ‘ ab\\cd%’ escape  ‘\’匹配所有以“ ab\ cd ”开头的字符串。

SQL 允许使用 not like 比较运算符搜寻不匹配项。一些数据库还提供 like 运算的变体,不区分大小写。

⑦ 在 SQL :1999中还提供 similar to 操作,它具备比 like 运算更强大的模式匹配能力。它的模式定义语法类似于 UNIX 中的正则表达式。

3.4.3 select 子句中的属性说明

星号“ * "可以用在 select 子句中表示“所有的属性”,因而,如下査询的 select 子句中使用 "instructor. * ":

select instructor.*
from instructor, teaches
where instructor.ID= teaches.ID;

表示instructor中所有属性都被选中;形如select * 的select 子句表示from子句结果关系的所有属性都被选中;

3.4.4 排列元组的显示次序

① SQL为用户提供了一些对关系中元组显示次序的控制;order by 子句就可以让查询结果中元组按排列顺序显示;

②为了按字母顺序列出Physical 系的所有教师,可以写成:

select name
from instructor
where dept name = ’Physics’
order by name;

③ order by 子句默认使用升序。要说明排序顺序,我们可以用 desc 表示降序,或者用 asc 表示升序。此外,排序可在多个属性上进行。

④ 假设我们希望按 salary 的降序列出整个 insiructor 关系。如果有儿位教师的工资相同,就将它们按姓名升序排列。我们用 SQL 将该查询表示如下:

select *
from instructor
order by salary desc, name asc;

3.4.5 where 子句谓词

① 为了简化 where 子句, SQL 提供 between 比较运算符来说明一个值是小于或等于某个值,同时大于或等于另一个值的。

② 如果我们想找出工资在90000美元和100000美元之间的教师的姓名,我们可以使用 between 比较运算符,如下所示:

select name
from instructor
where salary between 90000 and 100000;

它可以取代

 select name from instructor where salary <=100000 and salary >=90000;

③ 类似地,我们还可以使用 not between 比较运算符。

我们可以扩展前面看到过的查找教师名以及课程标识的查询,但考虑更复杂的情况,要求教师是生物系的:“查找 Biology 系讲授了课程的所有教师的姓名和他们所讲授的课程”。为了写出这样的查询,我们可以在前面看到过的两个 SQL 查询的任意一个的基础上进行修改,在 where 子句中增加一个额外的条件。我们下面给出修改后的不使用自然连接的 SQL 査询形式:

 select name, course id
from instructor, teaches
where instructor.ID= teaches.ID and dept name = ’Biology’;

⑤ SQL允许我们用记号(v1, v2, . . . , vn)来表示一个分量值分别为 v1, v2, . . . , vn 的n维元组;在元组上可以运用比较运算符,按字典顺序进行比较运算;如:(a1, a2) <= (b1, b2) 在a1 <= b1 且 a2 <= b2时为真;类似的,当两个元组在所有属性上相等,它们时相等的;

这样前面的查询可以被重写为:

select name, course id
from instructor, teaches
where (instructor.ID, dept name) = (teaches.ID, ’Biology’);

3.5 集合运算

① SQL 作用在关系上的 union 、 intersect 和 except 运算对应于数学集合论中的 ∪ 、∩ 和﹣运算。我们现在来构造包含在两个集合上使用 union 、 intersect 和 except 运算的查询。

● 在2009年秋季学期开设的所有课程的集合:

select course id
from section
where semester = ’Fall’ and year= 2009;

●在2010年春季学期开设的所有课程的集合:

select course id
from section
where semester = ’Spring’ and year= 2010;

② 在我们后面的讨论中,将用 cl 和c2分别指代包含以上査询结果的两个关系,并在图3-9和图3-10中给出作用在如图2-6所示的 section 关系上的查询结果。注意c2包含两个对应于 course _ id 为 CS -319的元组,因为该课程有两个课程段在2010年春季开课。


3.5.1 并运算

① 为了找出在2009 年球季开课,或者在2010年春季开课或两个学期都开课的所有课程,可以写查询语句:

(select course id
from section
where semester = ’Fall’ and year= 2009)
union
(select course id
from section
where semester = ’Spring’ and year= 2010);

② 与 select 子句不同, union 运算自动去除重复。

③ 这样,在如图2-6所示的 section 关系中,2010年春季开设 CS -319的两个课程段, CS -101在2009年秋季和2010年秋季学期各开设一个课程段, CS -101和 CS -319在结果中只出现一次,如图3-11所示。

④ 如果想保留重复,可以使用union all 代替union :

(select course id
from section
where semester = ’Fall’ and year= 2009)
union all
(select course id
from section
where semester = ’Spring’ and year= 2010);

⑤ 在结果中的重复元组数等于在 cl 和c2中出现的重复元组数的和。因此在上述查询中,每个 CS -319和 CS -101都将被列出两次。作为一个更深入的例子,如果存在这样一种情况: ECE -101在2009年秋季学期开设4个课程段,在2010年春季学期开设2个课程段,那么在结果中将有6个 ECE -101元组。

3.5.2 交运算

① 为了找出2009年球季和2010年春季同时开课的所有课程的集合,可以写作:

(select course id
from section
where semester = ’Fall’ and year= 2009)
intersect
(select course id
from section
where semester = ’Spring’ and year= 2010);

② 结果关系如图3-12所示,它只包括一个 CS -101元组。 intersect 运算自动去除重复。例如,如果存在这样的情况: ECE -101在2009年秋季学期开设4个课程段,在2010年春季学期开设2个课程段,那么在结果中只有1个 ECE -101元组。

③ 如果想保留所有的重复,就必须用intersect all 代替intersect:

(select course id
from section
where semester = ’Fall’ and year= 2009)
intersect all
(select course id
from section
where semester = ’Spring’ and year= 2010);

④ 在结果中出现的重复元组数等于在c1和c2中出现的重复次数里最少的那个;如:如果ECE-101在2009年球季学期开设4个课程段,在2010年春季学期开设2个课程段,那么在结果中有2个ECE-101元组;

3.5.3 差运算

① 为了找出在2009年秋季学期开课但不在2010年春季学期开课的所有课程,可以写作:

(select course id
from section
where semester = ’Fall’ and year= 2009)
except
(select course id
from section
where semester = ’Spring’ and year= 2010);

② 该查询结果如图3-13所示。注意这正好是图3-9的 cl 关系减去不出现的 CS -101元组。 except 运算从其第一个输入中输出所有不出现在第二个输入中的元组,也即它执行集差操作。此运算在执行集差操作之前自动去除输人中的重复。例如,如果 ECE -101在2009年秋季学期开设4个课程段,在2010年春季学期开设2个课程段,那么在except 运算的结果中将没有ECE-101 的任何拷贝;

③ 如果想保留重复,就必须用except all 代替except:

(select course id
from section
where semester = ’Fall’ and year= 2009)
except all
(select course id
from section
where semester = ’Spring’ and year= 2010);

④ 结果中的重复元组数等于在 cl 中出现的重复元组数减去在c2中出现的重复元组数(前提是此差为正)。因此,如果 ECE -101在2009年秋季学期开设4个课程段,在2010年春季学期开设2个课程段,那么在结果中有2个 ECE -101元组。然而,如果 ECE -101在2009年秋季学期开设2个或更少的课程段,在2010年春季学期开设2个课程段,那么在结果中将不存在 ECE -101元组。

3.6 空值

① 空值给关系运算带来了特殊的问题,包括算术运算、比较运算和集合运算。

② 如果算术表达式的任一输人为空,则该算术表达式(涉及诸如+、-、*或/)结果为空。

例如,如果查询中有一个表达式是 r . A +5,并且对于某个特定的元组, r . A 为空,那么对此元组来说,该表达式的结果也为空。

③ 涉及空值的比较问题更多。例如,考虑比较运算“1< null ”。因为我们不知道空值代表的是什么,所以说上述比较为真可能是错误的。但是说上述比较为假也可能是错误的,如果我们认为比较为假,那么“ not (1< nul )”就应该为真,但这是没有意义的。

因而 SQL 将涉及空值的任何比较运算的结果视为 unknown (既不是谓词 is null ,也不是 is not null )。这创建了除 true 和 false 之外的第三个逻辑值。

●  and : true and unknown 的结果是 unknown ,  false and unknoun 结果是 false ,
unknoun and unknoun 的结果是unknown 。
●  or : true or unknown 的结果是 true ,  false or unknown 结果是 unknown ,
unknown or unknown 结果是 unknown 。
●  not : not unhnown 的结果是 unknoun 。

可以验证,如果 r . A 为空,那么“1< r . A ”和“ not (1< r . A )"结果都是 unknown 。
如果 where 子句谓词对一个元组计算出 false 或 unknown ,那么该元组不能被加人到结果集中。

④ SQL 在谓词中使用特殊的关键词 nul 测试空值。因而为找出 instructor 关系中 salary 为空值的所有教师,我们可以写成:

select name
from instructor
where salary is null;

如果为此is null 所作用的值非空,那么它为真;

⑤ 某些 SQL 实现还允许我们使用子句 is unknown 和 is not unknown 来测试一个表达式的结果是否为 unknown ,而不是 true 或 false 。

⑥ 当一个查询使用 select distinct 子句时,重复元组将被去除。为了达到这个目的,当比较两个元组对应的属性值时,如果这两个值都是非空并且值相等,或者都是空,那么它们是相同的。所以诸如{(’A’,null),(’A’,null)} 这样的两个元组拷贝被认为是相同的,即使在某些属性上存在空值。使用 distinct 子句会保留这样的相同元组的一份拷贝。注意上述对待空值的方式与谓词中对待空值的方式是不同的,在谓词中“ null = null ”会返回 unknown ,而不是 true 。

如果元组在所有属性上的取值相等,那么它们就被当作相同元组,即使某些值为空。上述方式还应用于集合的交、并和差运算;

3.7聚集函数

聚集函数是以值的一个集合(集或多重集)为输人、返回单个值的函数。 sQL 提供了五个固有聚集函数:
● 平均值: avg 。
● 最小值: min 。
● 最大值: max 。
● 总和: sum 。
● 计数: count 。
sum 和 avg 的输人必须是数字集,但其他运算符还可作用在非数字数据类型的集合上,如字符串。

3.7.1基本聚集

● 查询“找出 computer Science 系教师的平均工资”。书写该查询如下:

 select avg ( salary )from instructor where dept _ name ='comp.Sci.';

● 该査询的结果是一个具有单属性的关系,其中只包含一个元组,这个元组的数值对应 computer Science 系教师的平均工资。数据库系统可以给结果关系的属性一个任意的名字,该属性是由聚集产生的。然而,我们可以用 as 子句给属性赋个有意义的名称,如下所示:

 select avg ( salary ) as avg _ salan from instructor where dept _ name ='comp.Sci.';

● 在 insiructor 关系中, computer Science 系的工资值是75000美元、65000美元和92000美元,平均工资是232000/3=77333.33美元。

● 在计算平均值时保留重复元组是很重要的。假设 computer Science 系增加了第四位教师,其工资正好是75000美元。如果去除重复的话,我们会得到错误的答案(232000/4=58000美元),而正确的答案是76750美元。

● 有些情况下在计算聚集函数前需先掉重复元组。如果我们确实想删除重复元组,可在聚集表达式中使用关键词 distinct 。比方有这样一个查询示例“找出在2010年春季学期讲授一门课程的教师总数”,在该例中不论一个教师讲授了几个课程段,他只应被计算一次。所需信息包含在 teaches 关系中,我们书写该查询如下:

select count (distinct ID)
from teaches
where semester = ’Spring’ and year = 2010;

● 经常使用聚集函数 count 计算一个关系中元组的个数。 SQL 中该函数的写法是 count (*)。因此,要找出 course 关系中的元组数,可写成:

select count (*)
from course;

● 由于在 ID 前面有关键字 distinct ,所以即使某位教师教了不止一门门课程,在结果中他也仅被计数一次。 SQL 不允许在用 count (*)时使用 distinct 。在用 max 和 min 时使用 distinct 是合法的,尽管结果并无差别。我们可以使用关键词 all 替代 distinct 来说明保留重复元组,但是,既然 all 是默认的,就没必要这么做了。

3.7.2分组聚集

● 有时候我们不仅希望将聚集函数作用在单个元组集上,而且也希望将其作用到一组元组集上;在 SQL 中可用 group by 子句实现这个愿望。 group by 子句中给出的一个或多个属性是用来构造分组的。

● 在 group by 子句中的所有属性上取值相同的元组将被分在一个组中;

● 示例,考虑查询“找出每个系的平均工资”,该查询书写如下:

select dept name, avg (salary) as avg salary
from instructor
group by dept name;


Figure 3.14 Tuples of the instructor relation, grouped by the dept name attribute.
图 3.14 instructor关系的元组按照dept_name属性分组

● 图3-14给出了 instructor 关系中的元组按照 dept _ name 属性进行分组的情况,分组是计算查询结果的第一步。在每个分组上都要进行指定的聚集计算,查询结果如图3-15所示。

Figure 3.15 The result relation for the query “Find the average salary in each department”.
图 3.15 查询“找出每个系的平均工资”的结果关系

● 相反,考虑查询“找出所有教师的平均工资”。我们把此查询写做如下形式:

select avg (salary)
from instructor;

在这里省略了group by子句,因此整个关系当作是一个分组;

● 作为在元组分组上进行聚集操作的另一个例子,考虑查询“找出每个系在2010年春季学期讲授一门课程的教师人数”。有关每位教师在每个学期讲授每个课程段的信息在 teaches 关系中。但是,这些信息需要与来自 instructor 关系的信息进行连接,才能够得到每位教师所在的系名。这样,我们把此查询写做如下形式:

select dept name, count (distinct ID) as instr count
from instructor natural join teaches
where semester = ’Spring’ and year = 2010
group by dept name;

其结果如图3-16 所示;

Figure 3.16 The result relation for the query “Find the number of instructors in each department who teach a course in the Spring 2010 semester.”
图 3.16 查询“找出每个系在2010年春季学期讲授的一门课程的教师人数”的结果关系

● 当 SQL 查询使用分组时,一个很重要的事情是需要保证出现在 select 语句中但没有被聚集的属性只能是出现在 group by 子句中的那些属性。换句话说,任何没有出现在 group by 子句中的属性如果出现在 select 子句中的话,它只能出现在聚集函数内部,否则这样的查询就是错误的。例如,下述查询是错误的,因为 ID 没有出现在 group by 子句中,但它出现在了 select 子句中,而且没有被聚集:

/* erroneous query */
select dept name, ID, avg (salary)
from instructor
group by dept name;

在一个特定分组(通过dept_name定义)中的每位教师都有一个不同的ID,既然每个分组只输出一个元组,那么就无法确定选哪个ID作为输出;其结果为:SQL不允许这样的情况出现;

数据库系统概念-第六版 - charter 3 - 笔记相关推荐

  1. 数据库系统概念-第六版 - charter 1 - 笔记

    <1>数据库系统(Database System,DBS) 由一个相互关联的数据的集合和一组用以访问这些数据的程序组成:这个数据集合通常被称作数据库(Database): DBS的主要目标 ...

  2. 数据库系统概念第六版 第五章练习题 2 4

    数据库系统概念第六版 第五章练习题 2 4 5.2 写一个使用JDBC元数据特性的JAVA函数,该函数用ResultSet作为输入参数,并把结果输出为用合适的名字作为列名的表格形式. (对JAVA不太 ...

  3. 数据库系统概念第六版 第四章练习题 12 14 18

    数据库系统概念第六版 第四章练习题 12 14 18 4.12 对于图中的数据库,写出一个查询来找到那些没有经理的雇员.注意一个雇员可能只是没有列出其经理或者可能有null经理.使用外连接书写查询,然 ...

  4. 数据库系统概念第六版笔记

    Abraham Siberschatz Henry E Korth S.Sudarshan 杨冬青 李红艳 唐世渭等译. 华章一套书的标志性语言是: 文艺复兴以降,源远流长的科学精神和逐步形成的学术规 ...

  5. mysql图书管理数据库的三个关系模式_数据库 考虑如下关于图书馆的关系模式,用关系代数写出查询(数据库系统概念第六版6.14)...

    贝尔梅尔娜美 2019.03.15 采纳率:60%    等级:39 已帮助:91565人 数据库系统的基本概念 数据:实际上就是描述事物的符号记录. 数据的特点:有一定的结构,有型与值之分,如整型. ...

  6. 数据库系统概念 第六版 大学数据库代码

    官网 create table classroom(building varchar(15),room_number varchar(7),capacity numeric(4,0),primary ...

  7. 数据库系统概念第六版 第七章练习题 15 22

    数据库第七章理论习题 7.15 为医院构造一个包含一组病人和一组医生的E-R图.为每个病人关联一组不同的检查和化验记录. 说明:医生和病人间存在联系,同时病人有特有的检查和化验记录,病人有病床,医生属 ...

  8. C++Primer Plus (第六版)阅读笔记 + 源码分析【目录汇总】

    C++Primer Plus (第六版)阅读笔记 + 源码分析[第一章:预备知识] C++Primer Plus (第六版)阅读笔记 + 源码分析[第二章:开始学习C++] C++Primer Plu ...

  9. 【数据库系统概念第七版(Database System Concepts 7th)配套SQL文件如何获取】

    数据库系统概念第七版(Database System Concepts 7th)配套SQL文件如何获取 数据库大黑书获取配套SQL文件 最后在数据库运行SQL文件即可 数据库大黑书获取配套SQL文件 ...

  10. 软件工程第六版全书总结笔记

    软件工程 (!!!重在理解基本原理和概念,不要陷于工具的细节!!!) 第一章软件工程学概述 软件工程诞生的背景:为了更有效的开发与维护软件. 软件危机:计算机软件的开发和维护过程中所遇到的一系列严重问 ...

最新文章

  1. LiFi会将大数据和物联网带到新高度吗?
  2. C#设计模式(8)-Builder Pattern
  3. java公寓管理系统设计与实现_学生公寓(宿舍)管理系统的设计与实现(论文范文, jspjava).docx_蚂蚁文库...
  4. 异步编程Promise、Generator和Async
  5. linux 内核空间 缺页,Linux-3.14.12内存管理笔记【缺页异常处理(1)】
  6. sync.Once简介
  7. 兄dei,来封装一个自定义事件玩玩
  8. 毕设题目:Matlab优化选址
  9. vue-echarts渲染中国地图以及省份地图
  10. 使用MQTTX 建立连接报Error: Connection refused: Not authorized 未授权
  11. FCOS搭建环境bug
  12. 在服务器创建并进入虚拟环境
  13. 四十七、使用bootstrap中的选项卡制作产品特色页面
  14. Python 中隐藏的彩蛋们
  15. 宝塔linux面板时区不正确怎么修改
  16. 【Android应用】 苏州实时公交
  17. 推荐:六款强大的开源数据挖掘工具
  18. 计算机桌面怎么锁,电脑怎么上锁,详细教您怎么给电脑屏幕上锁
  19. C51 学习笔记03 | 8051单片机几大功能组成部件
  20. 同行者语音助手_车载语音搭载率接近八成,“后价格战”时代的错位竞争GGAI头条...

热门文章

  1. 计算机网络技术实训室管理制度,计算机实验室管理制度
  2. 一个程序员的成长之路
  3. MIUI12系统如何刷入开发版获得ROOT权限
  4. 拉绳位移传感器的原理
  5. java地铁最短距离_地铁线路最短路径
  6. 电子表格计算机时间格式,在Excel表格中输入日期的五种方法与技巧
  7. 信息系统项目管理师 第一章 信息化和信息系统 核心知识点总结
  8. 强化学习之Q函数的个人理解
  9. 计算机没桌面,电脑桌面的计算机图标没了怎么办
  10. Google快讯-UTStarcom