SQL(structured query language)即结构化查询语言,是关系数据库的标准语言,具有数据查询(data query)、数据操作(data manipulation)、数据定义(data definition)和数据控制(data control)等强大功能。本章首先介绍SQL的特点,然后分别介绍SQL的数据定义、数据查询、数据更新以及视图的定义形式和使用方法。
SQL于1974年由Boyce和Chamberlin提出,并在IBM公司研制的关系数据库管理系统原型System R上实现。由于SQL简单易学、功能丰富、深受用户及计算机业界人士的欢迎,因此被数据库厂商采用。
经各公司不断地修改、扩充和完善,SQL已经成为集数据查询、数据操作、数据定义和数据控制于一体的关系数据库标准语言。该语言具有如下特点:
在非关系数据库中,通常使用模式数据定义语言(schema data definition language,模式DDL)、外模式数据定义语言(subschema data definition language,外模式DDL或子模式DDL)、数据存储描述语言(data storage description language,DSDL)和数据操作语言(data manipulation language,DML)分别定义模式、外模式、内模式和数据的存取与处理操作。
而SQL集数据定义语言、数据操作语言、数据控制语言的功能于一体,语言风格统一,可以独立完成数据库生命周期中的全部活动,包括定义关系模式、插入数据、建立数据库,查询和更新数据库中的数据,重构和维护数据库,控制数据库安全性、完整性等一系列操作。
SQL的这些功能为数据库应用系统的开发提供了良好的环境。在数据库系统投入运行后,用户还可以根据需要随时、逐步地修改模式,并且不会影响数据库的运行,从而使系统具有良好的可扩展性。
另外,在关系模型中,实体和实体间的联系均用关系表示,这种数据结构的单一性带来了数据操作符的统一性,查找、插入、删除、更新等每一种操作都只需一种操作符,从而克服了非关系系统因信息表示方式的多样性而带来的操作复杂性。
非关系数据模型的数据操作语言是“面向过程”的语言,用“过程化”语言完成某项请求,必须指定存取路径。而用SQL进行数据操作只需提出“做什么”,不用指明“怎么做”,因此无须了解存取路径,存取路径的选择和SQL的操作过程由系统自动完成。这不但大大减轻了用户的负担,而且有利于提高数据的独立性。
非关系数据模型采用的是面向记录的操作方式,操作对象是一条记录。例如,查询所有平均成绩在80分以上的学生姓名,用户必须一条一条地把满足条件的学生记录找出来。而SQL采用的是集合操作方式,不仅操作对象、查找结果可以是元组的集合,而且一次插入、删除、更新操作的对象也可以是元组的集合。
SQL既是独立的语言,又是嵌入式语言。作为独立的语言,它能够独立地用于联机交互,用户可以在终端键盘上直接输入SQL命令对数据库进行操作;作为嵌入式语言,SQL语句能够嵌入高级语言(如C、C++、Java)程序中,供程序员设计程序时使用。而在两种不同的使用方法下,SQL语言的语法结构基本上是一致的。这种以统一的语法结构提供两种不同的使用方法的做法,为用户提供了极大的灵活性与方便性。
SQL不但功能强大,而且设计巧妙、语言十分简洁,只用9个动词就可以完成核心功能,这些动词如表3.1所示。SQL接近英语口语,因此容易学习,易于使用。
表3.1 SQL的动词
支持SQL的RDBMS(relational database management system,关系数据库管理系统)同样支持关系数据库三级模式结构,如图3.1所示。其中,外模式对应于视图(view)和部分基本表(base table),模式对应于基本表,内模式对应于存储文件(stored file)。
图3.1 SQL对关系数据库模式的支持
用户可以用SQL对基本表和视图进行查询或其他操作,基本表和视图一样,都是关系。基本表是本身独立存在的表,在SQL中一个关系就对应一张基本表。一张(或多张)基本表对应一个存储文件,一张表可以带若干索引,索引也存放在存储文件中。
存储文件的逻辑结构组成了关系数据库的内模式。存储文件的物理结构是任意的,对用户是透明的。
视图是从一张或几张基本表导出的表。它本身不独立存储在数据库中,即数据库中只存放视图的定义而不存放视图对应的数据。这些数据仍存放在导出视图的基本表中。因此,视图是一张虚表。视图在概念上与基本表等同,用户可以在视图上再定义视图。
下面将逐一介绍各SQL语句的功能和格式,为了突出基本概念和基本功能,略去了许多语法细节。各个RDBMS产品在实现标准SQL时有所差别,与SQL标准的符合程度也不相同,一般在85%以上。因此,具体使用某个RDRMS产品时,还应参阅系统提供的有关手册。
关系数据库系统支持模式、外模式和内模式三级模式结构,而模式、外模式和内模式中包含的基本对象有表、视图和索引。因此,SQL的数据定义功能包括模式定义、表定义、视图和索引的定义,如表3.2所示。
表3.2 SQL的数据定义语句
SQL通常不提供修改模式定义、视图定义和索引定义的操作。用户如果想修改这些文件,只能先将它们删除掉,然后重新建立。
本节主要介绍如何定义模式、基本表和索引,视图的概念及其定义方法将在3.5节中讨论。
在SQL中,定义模式实际上是定义一个数据库的命名空间,用这个命名空间存放该数据库中所有的基本表、视图或索引等对象的名称。模式的定义有如下3种形式:
(1)CREATE SCHEMA <模式名> AUTHORIZATIDN <用户名>
(2)CREATE SCHEMA AUTHORIZATIDN <用户名>
(3)CREATE SCHEMA <模式名>AUTHORIZATIDN <用户名>[<表定义子句>|<视图定义子句>|<授权定义子句>]
· 形式(1)表示在定义模式时,给用户与模式起不相同的名字。
· 形式(2)表示在定义模式时,模式名隐含为用户名。
· 形式(3)表示在定义模式时,又定义了包含在该模式下的基本表、视图或索引。
在模式定义中,符号“<>”表示其中的内容由用户来定义;“[]”表示其中的内容是可选项;“|”表示两边的参数二选一。
只有拥有DBA权限或获得DBA授予的创建模式命令权限的用户才能调用创建模式命令。
【例3.1】 若要为用户WANG创建一个模式名为S-T的模式,则可定义为如下形式:
CREATE SCHEMA S-T AUTHORIZATION WANG;
若将上述语句改写为CREATE SCHEMA AUTHDRIZATIDN WANG,则表示为用户WANG创建一个模式名也为WANG的模式。
【例3.2】 若要为用户ZHANG创建一个名为TEST的模式,同时其中定义一个名为TAB1的表,则定义形式如下:
在SQL中,删除模式的语句如下:
DROP SCHEMA <模式名> <CASCADE | RESTRICT>
其中,CASCADE(级联)和RESTRICT(限制)两者必选其一。当模式中定义了表或视图等数据库对象时,只能选择DROP SCHEMA <模式名> CASCADE语句,表示在删除模式的同时把该模式中所有数据库对象也全部删除,而拒绝执行DROP SCHEMA <模式名>RESTRICT语句。只有当模式中不包含任何对象时,才能执行DROP SCHEMA <模式名>RESTRICT语句。
如果要删除【例3.2】创建的模式TEST,就只能使用DROP SCHEMA TEST CASCADE;语句。该语句表示在删除模式TEST的同时,也删除该模式中定义的TAB1表。
在创建一个模式、确定一个数据库的命名空间后,首先要在这个空间中定义的是该模式包含的数据库的基本表。
SQL语言使用CREATE TABLE语句定义基本表,基本格式如下:
通常,在建表的同时还可以定义与该表有关的完整性约束条件。这些完整性约束条件被存入系统的数据字典中,当用户操作表中的数据时,由RDBMS自动检查该操作是否违背这些完整性约束条件。如果完整性约束条件涉及该表的多个属性列,就必须定义在表级上;否则,既可以定义在列级上,又可以定义在表级上。
在关系模式中,每个属性都对应一个域,它的取值必须是域中的值。在SQL中,域用数据类型来表示。在定义表的各个属性时,需要指明其数据类型和长度。SQL提供了一些主要数据类型,如表3.3所示。注意,不同的RDBMS中支持的数据类型不完全相同。
表3.3 SQL数据类型
一个属性选用何种数据类型要根据实际情况决定,一般从两方面考虑:一方面是取值范围,另一方面是要做哪些运算。例如,对于年龄(Sage)属性,可以采用CHAR(3)作为数据类型,但考虑到要对年龄做算术运算(如求平均年龄),所以采用整数作为数据类型,因为CHAR(3)数据类型不能进行算术运算。而整数又有长整数和短整数两种,因为一个人的年龄在百岁左右,所以选用短整数作为年龄的数据类型。
【例3.3】 建立一个属性包含学生学号(Sno)、姓名(Sname)、性别(Ssex)、年龄(Sage)和所在系(Sdept)的学生表Student,SQL语句如下:
其中,学生学号、姓名、性别和所在系的属性值都是固定不变的,所以根据名字的长度选择CHAR数据类型,而年龄需要选择SMALLINT类型。
系统在执行上面的CREATE TABLE语句后,在数据库中建立一个新的空表Student,并将有关Student表的定义和约束条件存放在数据字典中。
【例3.4】 建立一张属性包含课程号(Cno)、课程名(Cname)、先修课(Cpno)和课程学分(Ccredit)的课程表Course,SQL语句如下:
本例说明参照表和被参照表可以是同一张表。
【例3.5】 建立属性包含学生学号(Sno)、课程号(Cno)和学生成绩(Crade)的学生选课表SC,SQL语句如下:
由于每一张基本表都必须属于某个关系模式,因此在定义基本表的同时,必须定义所属的关系模式。定义基本表所属关系模式的方法有3种。
方法1 定义模式的同时定义基本表,如3.2.1节中形式(3)的方法。
方法2 在已知某个模式存在的条件下,希望定义的基本表属于该模式,则采用如下方式:
CREATE TABLE <已经存在的模式名>. <表名>(...);
【例3.6】 在【例3.1】中定义了一个学生-课程模式S-T,现在要定义Student、Course、SC基本表属于S-T模式,则采用的语句形式如下:
方法3 若用户创建基本表(其他数据库对象也一样)时没有指定模式,则系统根据搜索路径确定该对象所属的模式。搜索路径包含一组模式列表,RDHMS会使用模式列表中第一个存在的模式作为数据库对象的模式名。若搜索路径中的模式名都不存在,系统将给出错误。使用下面的语句可以显示当前的搜索路径:
SHOW search_path;
搜索路径的当前默认值是$user,PUBLIC。其含义是首先搜索与用户名相同的模式名,如果该模式名不存在,就使用PUBLIC模式。DBA用户也可以设置搜索路径,如SET search-path TO"S-T",PUBLIC;。然后定义基本表,如CREATE TABLE Student(...);。
实际结果是建立了S-T. Student基本表,因为RDBMS发现搜索路径中第一个模式名S-T存在,所以把该模式作为基本表Student所属的模式。
随着应用环境和应用需求的变化,有时需要对已建好的基本表进行修改,SQL用ALTER TABLE语句修改基本表,一般格式如下:
其中:
· <表名>是要修改的基本表。
· ADD子句用于增加新属性和新的完整性约束条件。
· DROP子句用于删除指定的完整性约束条件。
· ALTER COLUMN子句用于修改原有属性的定义,包括修改属性名和数据类型。
【例3.7】 向Student表增加“入学时间”属性,数据类型为日期型,SQL语句如下:
ALTER TABLE Student ADD S_entrance DATE;
无论基本表中原来是否已有数据,新增加的属性一律为空值。
【例3.8】 将Student表中年龄的数值类型改为字符型,SQL语句如下:
ALTER TABLE Student ALTER COLUMN Sage CHAR(3);
【例3.9】 为Course表增加课程名称,必须取唯一值的约束条件,SQL语句如下:
ALTER TAHLE Course ADD UNIQUE(Cname);
当某张基本表不再被需要时,可以使用DROP TARLE语句删除它。其一般格式为:
DROP TAHLE<表名>[RESTRICT | CASCADE];
若选择RESTRICT,则该表的删除是有限制条件的:欲删除的基本表不能被其他表的约束所引用(如CHECK、FOREIGN KEY等约束),不能有视图,不能有触发器,不能有存储过程或函数等。如果存在依赖该表的对象,此表就不能被删除。
若选择CASCADE,则该表的删除没有限制条件。在删除基本表的同时,相关的依赖对象(如视图)都将被一起删除。默认选择RESTRICT。
【例3.10】 删除Student表,SQL语句如下:
DROP TABLE Student CASCADE;
基本表定义一旦被删除,不仅表中的数据和此表的定义将被删除,而且此表上建立的索引、视图、触发器等有关对象也都将被删除。因此,在执行删除基本表的操作时一定要格外小心。
【例3.11】 若表上建有视图,则选择RESTRICT时不能删除表;选择CASCADE时可以删除表,且视图也自动被删除。
注意,虽然不同的数据库产品都遵循SQL标准,但在具体实现细节和处理策略上还是与SQL标准存在一定差别。
建立索引是提高查询速度的有效方法。用户可以根据应用环境的需要在基本表上建立一个或多个索引,以提供多种存取路径,提高查询速度。
通常,建立与删除索引由数据库管理员或建表的人负责完成。系统在存取数据时会自动选择合适的索引作为存取路径,用户不必也不能显式地选择索引。
在SQL语言中,建立索引使用CREATE INDEX语句,一般格式为:
C REATE [UNIQUE][CLUSTER] INDEX<索引名> ON <表名>(<属性名1>[<次序>][,<属性名2>[<次序>]]...);
其中:
· <表名>是要建索引的基本表的名字。索引可以建立在该表的一个或多个属性上,各个属性名之间用逗号分隔。每个<属性名>后面还可以用<次序>指定索引值的排列次序,可选择ASC(升序)或DESC(降序),默认值为ASC。
· UNIQUE表明此索引的每一个索引值只对应唯一的数据记录。
· CLUSTER表示要建立的索引是聚簇索引。所谓聚簇索引,是指索引项的顺序与表中记录的物理顺序一致的索引组织。
用户使用CREATE INDEX语句定义索引时,可以定义索引是唯一索引、非唯一索引或聚簇索引。
【例3.12】 执行CREATE INDEX语句:
CREATE CLUSTER INDEX Stusname ON Student(Sname);
将在Student表的Sname(姓名)属性上建立一个聚簇索引,而且Student表中的记录将按Sname值的升序存放。
用户可以在最常查询的属性上建立聚簇索引,以提高查询效率。显然,一张基本表最多只能建立一个聚簇索引。建立聚簇索引后,更新该索引属性上的数据往往会导致表中记录的物理顺序发生变更,代价较大,因此对于经常需要更新的属性,不宜建立聚簇索引。
【例3.13】 为学生-课程数据库中的Student、Course、SC三张表建立索引。其中,Student表按学号升序建唯一索引,Course表按课程号升序建唯一索引,SC表按学号升序和课程号降序建唯一索引。SQL语句如下:
CREATE UNIQUE INDEX Stusno ON Student(Sno); CREATE UNIQUE INDEX Coucon ON Course(Cno); CREATE UNIQUE INDEX SCno ON SC(Sno ASC,Cno DESC);
索引一经建立,就由系统使用和维护,不需要用户干预。建立索引是为了减少查询操作的时间,但是如果需要对数据不断地进行增加、删除和更改操作,系统就会花费许多时间来维护索引,导致查询效率降低。这时可以删除一些不必要的索引。
在SQL中,删除索引使用DRDR INDEX语句,一般格式为:
DROP INDEX(索引名);
【例3.14】 删除Student表的Stusname索引,SQL语句如下:
DROP INDEX Stusname;
删除索引时,系统会同时从数据字典中删除有关该索引的描述。
在数据库中,数据查询操作是核心操作。SQL中的查询操作由SELECT语句完成,该语句具有使用灵活和功能丰富的特点。其一般格式为:
SELECT[All | DISTINICT]<属性表达式1>[别名1][,<属性表达式2>[别名2]]... FROM <表名或视图名> [,<表名或视图名>]... [WHERE<条件表达式>] [GROUP BY<属性1>[HAVING<条件表达式>]] [ORDER BY <属性2>[ASC | DESC]];
其中:
· SELECT子句中的“属性表达式”指出要查找的属性或属性的表达式。查询结果按照属性表达式的顺序输出,通过别名的方式可以更改查询结果的属性名。参数ALL表示输出结果允许有相同的元组。DISTINICT表示在输出结果中,若有相同的元组,则只保留1个。默认值为ALL。
· FROM子句中的“表名或视图名”指出要查找的表或视图。
· WHERE子句中的“条件表达式”指出要查找的元组应满足的条件。
· GROUP BY子句的功能是将输出结果按“属性名1”进行分组,把该属性值相等的元组分为一个组。HAVING<条件表达式>短语的作用是仅输出满足该条件表达式的组。
· ORDER BY子句指出输出结果可以按“属性2”的值升序(ASC)或降序(DESC)排序。
SELECT语句可以进行单表查询,也可以对多表进行连接查询和嵌套查询。本节以学生-课程数据库为例,分别介绍SELCET语句对单表和多表的查询方法。
用SELCET-FROM对单表进行查询时,语句可以简化为如下形式:
SELECT[All | DISTINICT]<属性表达式1>[别名1][,<属性表达式2>[别名2]]... FROM <表名或视图名>;
【例3.15】 对【例3.3】中的Student表进行如下查询:
· 查询全体学生的学号与姓名。
· 查询全体学生的姓名、学号、所在的院系。
· 查询全体学生的详细记录。
· 查询全体学生的姓名和出生年份。
· 查询全体学生的姓名、出生年份和所在的院系,并用小写字母表示所有系名。
要完成上述查询功能,可以分别使用如下SQL语句:
(1)查询全体学生的学号与姓名:
SELECT Sno,Sname FROM Student;
查询结果为:
该语句按Student表中的属性顺序输出Sno和Sname两个属性的值。语句执行过程为:首先从Student表中取出一个元组,然后取出该元组在属性Sno和Sname上的分量,形成一个新的元组作为输出结果。对Student表中的所有元组做相同的操作,最后形成一个查询结果关系作为输出。
(2)查询全体学生的姓名、学号、所在的院系:
SELECT Sname,Sno,Sdept FROM Student;
查询结果为:
查询结果按照SELECT语句中的属性顺序进行输出,即先输出姓名,再输出学号和所在系。
(3)查询全体学生的详细记录:
SELECT * FROM Student;
如果要原封不动地输出基本表,可以用*代替全部属性。
(4)查询全体学生的姓名和出生年份:
SELECT Sname,2017-Sage /*查询结果的第2列是一个算术表达式*/ FROM Student;
查询结果为:
基本表只有年龄信息,没有出生年份信息,但用当前的年份减去年龄就能得到出生年份。此例说明,SELECT语句在对某一属性进行查询的同时,也可以对该属性进行运算,并输出运算后的结果,此时属性名为属性表达式。例如,2017-Sage为属性表达式,每个人的出生年份为它的值。
(5)查询全体学生的姓名、出生年份和所在的院系,并用小写字母表示所有系名:
SELECT Sname NAME,2017-Sage BIRTHDAY,LOWER(Sdept) DEPARTMENT FROM Student;
查询结果为:
根据用户需求修改输出属性名的方法LOWER(属性名)是一个函数,它将该属性值中的所有大写字母都转换为小写字母。
当要查询满足某些条件的元组时,需要用SELECT-FROM-WHERE语句。若用SELECT-FROM-WHERE语句对单表进行查询,则语句可简化为如下形式:
SELECT[All | DISTINICT]<属性表达式1>[别名1][,<属性表达式2>[别名2]]... FROM <表名或视图名> WHERE<条件表达式>;
其中,WHERE子句中的“条件表达式”可以是单个条件,也可以是多个条件。该语句的作用是:首先查找满足WHERE子句中“条件表达式”的元组,然后查找这些元组在SELECT子句中的“属性表达式”的分量,最后输出由这些分量组成的元组所构成的表。
WHERE子句常用的查询谓词如表3.4所示。
表3.4 常用的查询谓词
下面介绍由谓词所表达的查询条件。
当要查询的元组的属性分量为与某个值进行比较时,可选择比较运算符表达查询条件。
【例3.16】 对【例3.6】中的Student和SC表进行如下查询:
· 查询计算机科学系全体学生的名单。
· 查询年龄在20岁以下的所有学生的姓名及其年龄。
· 查询考试成绩不及格的学生的学号。
要完成上述查询功能,可以分别使用如下SQL语句:
(1)查询计算机科学系全体学生的名单:
查询结果为:
实际上此例是查询属性Sdept分量为CS的元组。
(2)查询年龄在20岁以下的所有学生的姓名及其年龄:
查询结果为:
实际上此例是查询属性Sage分量为小于20的元组。
(3)查询考试成绩不及格的学生的学号:
查询结果为:
在此例中,由于使用了DISTINCT短语,因此尽管20160001号的学生有两科成绩不及格,但在查询结果中只显示1个20160001的学号。
当要查询元组的属性分量介于(或不介于)两个值之间时,可以使用谓词BETWEEN(或NOT BETWEEN)...AND表达查询条件。其中,BETWEEN后面的值是查询范围的低值,AND后的值是查询范围的高值。
【例3.17】 对【例3.6】中的Student表进行如下查询:
· 查询年龄为19~23岁(包括19岁和23岁)的学生的姓名、系别和年龄。
· 查询年龄不是19~23岁的学生的姓名、系别和年龄。
要完成上述查询功能,可以分别使用如下SQL语句:
(1)查询年龄为19~23岁(包括19岁和23岁)的学生的姓名、系别和年龄:
查询结果为:
(2)查询年龄不是19~23岁的学生的姓名、系别和年龄:
查询结果为:
当要查询元组的属性分量取(不取)域中的某几个值时,可以用谓词IN(NOT IN)或逻辑运算符表达查询条件。
【例3.18】 对【例3.6】中的Student表进行如下查询:
· 查询计算机科学系(CS)和数学系(MA)的学生的姓名和性别。
· 查询既不是计算机科学系又不是数学系的学生的姓名和性别。
要完成上述查询功能,可以分别使用如下SQL语句:
(1)查询计算机科学系(CS)和数学系(MA)的学生的姓名和性别:
等价形式为:
SELECT Sname,Ssex FROM Student WHERE Sdept='CS' OR Sdept=' MA';
查询结果为:
当查询条件为同一属性取几个不同的值时,可用谓词IN或逻辑运算符OR表示查询条件。
(2)查询既不是计算机科学系又不是数学系的学生的姓名和性别:
查询结果为:
当要查询元组的属性分量为空(NULL)或不空(NOT NULL)时,可选择谓词IS NULL或IS NOT NULL表达查询条件。
【例3.19】 对【例3.6】中的SC表进行如下查询:
· 查询所有缺考学生的学号和相应的课程号。
· 查询所有参加考试的学生的学号和相应的课程号。
要完成上述查询功能,可以分别使用如下SQL语句:
(1)查找所有缺考学生的学号和相应的课程号:
查询结果为:
此例中,缺考的学生在SC选修课表中,成绩(Grade)的属性分量为NULL,此时只能用谓词IS NULL表达查询条件,而不能用“=”代替。
(2)查找所有参加考试的学生的学号和相应的课程号:
查询结果为:
此例中,参加考试的学生的成绩一定不为空,可以用谓词IS NOT NULL表述查询条件。
当WHERE子句的查询条件为多个时,可以用逻辑运算符AND和OR将它们连接起来。AND的优先级高于OR,但用户可以用圆括号改变优先级的次序。
【例3.20】 使用【例3.6】中的Student表查询信息系年龄在20岁以下的学生的姓名,SQL语句如下:
SELECT Sname FROM Student WHERE Sdept= 'IS ' AND Sage <20;
查询结果为:
此例中,查询的元组需要同时满足两个条件:一个是“信息系”,另一个是“年龄小于20岁”。此时用逻辑运算符AND将这两个属性不同的查询条件连接起来,用以表示多个查询条件。
当要查询元组的属性分量包含(不包含)某一字符串时,可选择LIKE(NOT LIKE)谓词表达查询条件。其一般语法格式如下:
WHERE <属性>[NOT] LIKE '<字符串>' [ESCAPE '<换码字符>']
该查询语句的含义是查找属性分量包含“字符串”的元组。其中,“字符串”可以包含通配符“%”和“_”。若查询的属性分量本身含有通配符“%”或“_”,则使用ESCAPE '<换码字符>'短语对通配符进行转义。“%”代表任意长度的字符串,“_”代表任意单个字符。例如,a% b表示以a开头,以b结尾的任意长度的字符串;a_b表示以a开头,以b结尾的长度为3的任意字符串。
【例3.21】 对【例3.6】中的Student表进行如下查询:
· 查询所有姓刘的学生的姓名、学号和性别。
· 查询所有不姓刘的学生的姓名、学号和性别。
· 查询姓“张”且全名为两个汉字的学生的姓名。
· 查询名字中第2个字为“阳”的学生的姓名和学号。
要完成上述查询功能,可以分别使用如下SQL语句:
(1)查询所有姓刘的学生的姓名、学号和性别:
查询结果为:
(2)查询所有不姓刘的学生的姓名、学号和性别:
查询结果为:
(3)查询姓“张”且全名为两个汉字的学生的姓名:
查询结果为:
(4)查询名字中第2个字为“阳”的学生的姓名和学号:
查询结果为:
【例3.22】 对【例3.6】中的Course表进行如下查询:
· 查询程序设计语言_C的课程号和学分。
· 查询以“程序设计语言”开头的课程的详细情况。
要完成上述查询功能,可以分别使用如下SQL语句:
(1)查询程序设计语言_C的课程号和学分:
此例中,ESCAPE'\'表示“\”为换码字符。在换码字符“\”后面的字符“_”不再具有通配符的含义,而转义为普通的“_”字符。
查询结果为:
(2)查询以“程序设计语言”开头的课程的详细情况:
查询结果为:
此例中,在字符串“程序设计语言\_%”中,“_”前面使用了换码字符“\”,所以“_”被转义为普通的“_”字符;“%”前面没有换码字符,所以它还表示通用字符。
在介绍GROUP BY子句的查询功能之前,先介绍与之相关的聚集函数。在SQL中,提供聚集函数的目的是增强检索功能。常用的聚集函数有如下6个:
· 统计元组个数:COUNT([DISTINCT|ALL]*)。
· 统计一列中值的个数:COUNT([DISTINCT|ALL]<属性名>)。
· 计算一列数值型分量的总和:SUM([DISTINCT|ALL]<属性名>)。
· 计算一列数值型分量的平均值:AVG([DISTINCT|ALL]<属性名>)。
· 求一列值中的最大值:MAX([DISTINCT|ALL]<属性名>)。
· 求一列值中的最小值:MIN([DISTINCT|ALL] <属性名>)。
【例3.23】 对【例3.6】中的Student和SC表进行如下查询:
· 查询学生总人数。
· 查询选修了课程的学生人数。
· 计算选修了3号课程的学生的平均成绩。
· 查询选修了3号课程的学生的最高分数。
要完成上述查询功能,可以分别使用如下SQL语句:
(1)查询学生总人数:
查询结果为:
(2)查询选修了课程的学生人数:
查询结果为:
此例中,学生每选修一门课,在SC中都有一条相应的记录。一个学生可能选修了多门课程,为避免重复计算学生人数,必须在COUNT函数中用DISTINCT短语。
(3)计算选修了3号课程的学生的平均成绩:
查询结果为:
(4)查询选修了3号课程的学生的最高分数:
查询结果为:
GROUP BY子句将查询结果按某一属性或多个属性的分量进行分组,分量相等的为一组。对查询结果分组是为了细化聚集函数的作用对象。如果未对查询结果分组,聚集函数就会作用于整个查询结果。分组后,聚集函数将作用于每一个组,即每一组都有一个函数值。
当聚集函数遇到空值时,除COUNT(*)外,都跳过空值而只处理非空值。
【例3.24】 对【例3.6】中的Student和SC表进行如下查询:
· 求各个课程号及相关的选课人数。
· 查询选修了两门以上课程的学生的学号。
要完成上述查询功能,可以分别使用如下SQL语句:
(1)求各个课程号及相关的选课人数:
查询结果为:
该语句对查询结果按Cno的值分组,所有具有相同Cno值的元组为一组,然后对每一组使用聚集函数COUNT进行计算,以求得该组的学生人数。
(2)查询选修了两门以上课程的学生的学号:
查询结果为:
此例先用CROUP BY子句按Sno进行分组,再用聚集函数COUNT求每一组中元组的个数。HAVING短语给出了选择组的条件,即只有满足元组个数大于2的组才能被选择出来。
WHERE子句与HAVING短语的区别在于作用对象不同:WHERE子句作用于基本表或视图,从中选择满足条件的元组;HAVING短语作用于组,从中选择满足条件的组。
ORDER BY子句的作用是将查询结果按照ORDER BY子句中“属性1”的分量进行升序(ASC)或降序(DESC)排列。当“属性1”分量相同时,可按照“属性2”的分量进行升序(ASC)或降序(DESC)排列,以此类推。默认值为升序。
【例3.25】 对【例3.6】中的Student和SC表进行如下查询:
· 查询选修了3号课程的学生的学号及其成绩,查询结果按分数降序排列。
· 查询全体学生的情况,查询结果按所在系的系号升序排列,同一系中的学生按年龄降序排列。
要完成上述查询功能,可以分别使用如下SQL语句:
(1)查询选修了3号课程的学生的学号及其成绩,查询结果按分数降序排列:
查询结果为:
对于空值,排序时显示的次序是由具体系统实现来决定的。例如,若按升序排序,则含空值的元组最后显示;若按降序排序,则含空值的元组最先显示。各个系统的实现可以不同,只要保持一致就行。
(2)查询全体学生的情况,查询结果按所在系的系号升序排列,同一系中的学生按年龄降序排列:
查询结果为:
以上介绍了常用谓词的简单查询表达条件。有时一个查询的表达条件是另一个查询的结果,此时需要用嵌套结构进行查询。
在SQL中,一个SELECT-FROM-WHERE语句称为一个查询块。将一个查询块嵌套在另一个查询块的WHERE子句或HAVING短语的条件中的查询,称为嵌套查询。
嵌套查询是用多个简单查询构成的复杂查询。上层的查询块称为外层查询或父查询,下层的查询块称为内层查询或子查询。SQL允许多层嵌套查询,即一个子查询中还可以嵌套其他子查询。在此仅给出嵌套查询在单表中的一个应用实例,更多的嵌套查询内容将在3.3.2节中介绍。
【例3.26】 从【例3.6】的Student表中查询与“刘晨”在同一个系学习的学生。
嵌套查询语句为:
查询结果为:
此实例中,下层查询块SELECT-FROM-WHERE Sname='刘晨'嵌套在上层查询块SELECT Sno,Sname,Sdept- FHOM Student-WHERE Sdept IN的WHERE条件中。首先查询“刘晨”所在的系,查询结果为IS;然后查询所有在IS系学习的学生,即将第一步查询的结果IS作为第二步查询的条件,故可将第一步查询嵌入第二步查询的条件中,构成嵌套查询。
多表查询指一个查询同时涉及两张以上的表。多表查询通常由连接查询和嵌套查询来实现。
连接查询指由连接运算符表达连接条件的查询,是关系数据库中最主要的查询。按照连接运算符的分类,可以将连接查询分为等值连接查询和非等值连接查询。等值连接查询又可分为自然连接查询和外连接查询。连接运算符可以连接两张或两张以上的表,连接的这些表可以相同,也可以不同。下面通过示例来讲解连接查询的应用。
【例3.27】 对【例3.6】中的Student、Course和SC表进行如下查询:
· 查询每个学生及其选修课程的情况。
· 查询每一门课程的间接先修课(即先修课的先修课)。
· 查询选修了2号课程且成绩在90分以上的所有学生。
· 查询每个学生的学号、姓名、选修的课程名及成绩。
要完成上述查询功能,可以分别使用如下SQL语句:
(1)查询每个学生及其选修课程的情况:
由于学生的基本信息存放在Student表中,学生选课信息存放在SC表中,因此查询每个学生及其选修课程的情况实际上涉及Student与SC两张表。又因为这两张表都有共同的学号(Sno)属性,所以可以直接进行连接查询。这是两张不同表的连接查询,可用的查询方法有等值连接查询、自然连接查询和外连接查询。使用不同的查询方法所得到的查询结果也会略微不同。
等值连接的查询语句为:
SELECT Student. *,SC .* FROM Student,SC WHERE Student.Sno=SC.Sno;
查询结果为:
自然连接的查询语句为:
SELECT Stuent.Sno,Sname,Ssex,Sage,Sdept,Cno,Grade FROM Studen,SC WHERE Student.Sno=SC.Sno
查询结果为:
外连接的查询语句为:
SELECT Student.Sno,Sname,Ssex,Sage,Sdept,Cno,Grade FROM Student LEFT OUT JOFN SC ON(Student. Sno=SC.Sno);
查询结果为:
从以上示例中可以看出,等值连接和自然连接的查询结果只包含Student表中已选课程的学生信息,外连接的查询结果为Student表中所有学生的信息。同时,自然连接和外连接的SELECT子句消除了重复的属性。
需要说明的是,当一个属性同时在多张表中存在时,引用该属性就必须在属性前加上表名前缀,以避免产生混淆。例如,属性Sno在Student表与SC表中都存在,因此引用时必须加上表名前缀,即Student.Sno和SC.Sno,以区分Sno是哪张表的属性。当属性只在1张表中,不需要区分时,就不用加表名前缀。例如,本例中的属性Sname、Sage、Ssex、Sdept、Cno和Grade只在Student表或SC表中,因此可以直接引用。
(2)查询一门课程的间接先修课,实际上是查找这门课程先修课的先修课。课程的先修课信息存放在Course表中,而Course表中只存放每门课程的直接先修课程信息,而没有先修课的先修课。要得到这个信息,必须先对一门课程找到其先修课,再按此先修课的课程号,查找它的先修课程。这就要将Course表与其自身进行连接。为此,要为Course表取两个别名:一个是FIRST,如表3.5所示;另一个是SECOND,如表3.6所示。
表3.5 FIRST表(Course表)
表3.6 SECOND表(Course表)
完成该查询的语句为:
SELECT FIRST .Cno,SECOND.Cpno FROM Course FIRST,Course SECOND WHERE FIRST.Cpno=SECOND.Cno;
查询结果为:
(3)查询选修了2门课程且成绩在90分以上的所有学生:
查询结果为:
此例中,WHERE子句包含3个查询条件,即一个等值连接和两个比较运算,这种由多个查询条件构成的连接称为复合条件连接。
该查询的一种优化(高效)的执行过程是先从SC中挑选出Cno=' 2'并且Crade > 90的元组,形成一个中间关系,再和Student中满足连接条件的元组进行连接,得到最终的结果关系。
(4)查询每个学生的学号、姓名、选修的课程名及成绩:
由于学生的学号、姓名、选修的课程名及成绩分别在Student表、Course表和SC表中,因此查询每个学生的学号、姓名、选修的课程名及成绩是3张表的查询。具体查询语句为:
SELECT Student.Sno,Sname,Cname,Grade FROM student,SC,Course WHERE Student. Sno=SC. Sno AND SC. Cno=Course.Cno;
从上述的示例可知,连接操作可以是两表连接、一张表的自身连接,还可以是两张以上表的连接。通常把两张以上表的连接称为多表连接。
在单表查询中简单介绍了嵌套查询的基本方法,下面重点介绍嵌套查询在多表查询中的应用。在嵌套查询中,如果子查询的查询条件不依赖于父查询,那么这类子查询就称为不相关子查询。如果子查询的查询条件依赖于父查询,那么这类子查询就称为相关子查询,整个查询语句称为相关嵌套查询语句。不相关子查询是较简单的一类子查询,相关子查询则是相对复杂的查询。
当子查询的结果是一个集合时,外查询的谓词通常用IN;当子查询的结果是一个值时,外查询的谓词通常用“比较运算符”或“比较运算符”+ANY(ALL,SOME)等。下面给出各种查询的示例。
【例3.28】 对【例3.6】中的Student、Course和SC表进行如下查询:
· 查询选修了课程名为“信息系统”的学生的学号和姓名。
· 查询与“刘晨”在同一个系学习的学生。
· 找出每个学生超过其选修课程平均成绩的课程号。
要完成上述功能,分别使用下面的查询语句:
(1)查询选修了课程名为“信息系统”的学生的学号和姓名:
本查询涉及学号、姓名和课程名3个属性。学号和姓名存放在Student表中,课程名存放在Course表中,但Student与Course之间没有直接联系,必须通过SC表建立它们二者之间的联系。所以本查询实际上涉及3个关系,查询语句如下:
查询结果为:
查询过程为:首先在Course关系中找出“信息系统”的课程号,结果为3号;然后在SC关系中找出选修了3号课程的学生学号;最后在Student关系中取出Sno和Sname。该查询使用IN子查询语句。
本查询还可以用连接查询实现,具体语句如下:
从此例可以看到,当查询涉及多个关系时,用嵌套查询逐步求解层次清楚、易于构造,具有结构化程序设计的优点。该嵌套查询为不相关子查询,且可以用连接查询代替。对于可以用连接查询代替嵌套查询的查询,用户可以根据自己的习惯确定采用哪种方法。当然,不是所有嵌套查询都可以用连接代替。
(2)查询与“刘晨”在同一个系学习的学生:
查询结果为:
在此例中,由于一个学生只可能在一个系学习,也就是说内查询的结果只能是一个值,因此可以用“=”取代IN。
另外,本查询还可以用连接查询实现,具体语句如下:
SELECT S1.Sno,S1.Sname,S1.Sdept FROM Student S1,Student S2 WHERE S1.Sdept=S2.Sdept AND S2.Sname='刘晨';
(3)找出每个学生超过其选修课程平均成绩的课程号:
本查询是带有比较运算符“>”的相关子查询,具体语句如下:
查询结果为:
其中,x是表SC的别名,又称为元组变量,可以用来表示SC的一个元组。内层查询是求一个学生所有选修课程的平均成绩,值唯一。至于是哪个学生的平均成绩,要看参数x.Sno的值,而该值与父查询的值相关,因此是带有比较运算符“>”的相关子查询。
示例语句的一种可能的执行过程是:
从外层查询中取出SC的一个元组x,将元组x的Sno值(20160001)传递给内层查询:
执行内层查询,得到查询结果65,用该值代替内层查询,得到外层查询:
执行这个查询,得到查询结果(20160001,2)和(20160001,3)。
然后外层查询取出下一个元组,重复
,直到外层的SC元组全部处理完毕为止。
求解相关子查询不能像求解不相关子查询那样一次就将子查询求解出来,然后求解父查询,由于内层查询与外层查询有关,因此必须反复求值。
【例3.29】 对【例3.6】中的Student、Course和SC表进行如下查询:
· 查询其他系中比信息系某一学生年龄小的学生的姓名和年龄。
· 查询其他系中比信息系所有学生年龄都小的学生的姓名和年龄。
要完成上述功能,可以分别使用下面的查询语句:
(1)查询其他系中比信息系某一学生年龄小的学生的姓名和年龄:
查询结果如下:
RDRMS执行此查询时,首先处理子查询,找出IS系中所有学生的年龄,构成一个集合(20,19);然后处理父查询,找出所有不是IS系且年龄小于20或19岁的学生。
本查询也可以用聚集函数实现:首先用子查询找出IS系中最大的年龄(20),然后在父查询中查所有非IS系且年龄小于20岁的学生。具体语句如下:
(2)查询其他系中比信息系所有学生年龄都小的学生的姓名和年龄:
RDBMS执行此查询时,首先处理子查询,找出IS系中所有学生的年龄,构成一个集合(20,19);然后处理父查询,找所有不是IS系且年龄小于19岁的学生。查询结果为:
本查询同样可以用聚集函数实现,具体语句如下:
事实上,用聚集函数实现的子查询通常比直接用ANY或ALL的查询效率要高。ANY、ALL与聚集函数的对应关系如表3.7所示。
表3.7 ANY、ALL与聚集函数的对应关系
在表3.7中,=ANY等价于IN谓词,<ANY等价于<MAX,< >ALL等价于NOT IN谓词,<ALL等价于<MIN等。
SELECT语句的查询结果是元组的集合,所以多个SELECT语句的结果可进行集合操作。集合操作主要包括并操作(UNION)、交操作(INTERSECT)和差操作(EXCEPT)。
参加集合操作的各查询结果的列数必须相同,对应项的数据类型也必须相同。
【例3.30】 对【例3.6】中的Student、Course和SC表进行如下查询:
· 查询计算机科学系的学生及年龄不大于19岁的学生。
· 查询计算机科学系的学生与年龄不大于19岁的学生的交集。
· 查询计算机科学系的学生与年龄不大于19岁的学生的差集。
· 查询选修了课程1或选修了课程2的学生。
· 查询既选修了课程1又选修了课程2的学生。
要完成上述功能,可以分别使用下面的查询语句。
(1)查询计算机科学系的学生及年龄不大于19岁的学生:
查询结果为:
本查询实际上是求计算机科学系的所有学生与年龄不大于19岁的学生的并集。使用UNION将多个查询结果合并起来时,系统会自动去掉重复元组。如果要保留重复元组,就用UNION ALL操作符。
(2)查询计算机科学系的学生与年龄不大于19岁的学生的交集:
查询结果为:
这实际上就是查询计算机科学系中年龄不大于19岁的学生。也可以写成如下查询语句:
(3)查询计算机科学系的学生与年龄不大于19岁的学生的差集:
也就是查询计算机科学系中年龄大于19岁的学生,也可以写成如下查询语句:
查询结果为:空集。
(4)查询选修了课程1或选修了课程2的学生,就是查询选修了课程l的学生集合与选修了课程2的学生集合的并集:
查询结果为:
(5)查询既选修了课程1又选修了课程2的学生,就是查询选修了课程1的学生集合与选修了课程2的学生集合的交集:
也可以表示为:
数据更新有3种操作,即向表中插入若干行数据、修改表中的数据和删除表中若干行数据。在SQL中有对应的3类语句,即INSERT语句、UPDATE语句和DELETE语句。下面一一进行介绍。
SQL的数据插入语句INSERT通常有两种形式,一种是插入一个元组,另一种是插入子查询结果。后者可以一次性插入多个元组。
插入元组的INSERT语句的格式如下:
INSERT INTO <表名>[(<属性列l>[,<属比列2>]...)] VALUES(<常量1>[,<常量2>]...);
其功能是将新元组插入指定表中。其中,新元组的属性列1的值为常量1,属性列2的值为常量2…对于INTO子句中没有出现的属性列,新元组在这些列上将取空值。注意,在定义表时必须说明NOT NULL的属性列不能取空值,否则会出错。
如果INTO子句中没有指明任何属性列名,那么新插入的元组就必须在每个属性列上都有值。
【例3.31】 将一个新学生元组(学号:20160008;姓名:陈冬;性别:男;所在系:IS;年龄:18岁)插入【例3.6】的Student表中,SQL语句如下:
INSERT INTO Student(Sno,Sname,Ssex,Sdept,Sage) VALUES('20160008','陈冬','男',' IS',18);
在INTO子句中指出了表名Student,还指出了新增加的元组在哪些属性上要赋值,属性的顺序可以与CREATE TABLE中的顺序不一样。VALUES子句对新元组的各个属性赋值,字符串常量要用单引号(英文符号)引起来。
这个插入语句也可以写成如下形式:
在这种表示法中,INTO子句中只指出了表名,没有指出属性名,表示新元组要在表的所有属性列上都指定值,属性列的次序与CREATE TABLE中的次序相同。VALUES子句对新元组的各个属性列赋值时,一定要注意值与属性列要一一对应,否则将会出错。
【例3.32】 在【例3.6】的SC表中插入一条选课记录('200215128','1'),SQL语句如下:
RDBMS将在新插入记录的Grade列上自动地赋空值。也可以写成如下形式:
因为没有指出SC的属性名,所以在Grade列上要明确给出空值。
子查询不仅可以嵌套在SELECT语句中用以构造父查询的条件,还可以嵌套在INSERT语句中用以生成要插入的批量数据。
插入子查询结果的INSERT语句的格式如下:
【例3.33】 在【例3.6】中Student表中,对每一个系求学生的平均年龄,并把结果存入数据库。
首先,在数据库中建立一张新表,其中一列存放系名,另一列存放相应的学生平均年龄。
然后,对Student表按系分组求平均年龄,再把系名和平均年龄存入新表中,具体语句如下:
修改数据的操作又称为更新操作,语句的一般格式如下:
UPDATE语句的功能是修改指定表中满足WHERE子句条件的元组。其中,SET子句给出<表达式>的值,用于取代相应的属性列值。如果省略WHERE子句,就表示要修改表中的所有元组。 UPDATE语句可以修改一个值、多个值或子查询语句的值。
【例3.34】 对【例3.6】的Student表进行如下修改:
· 把学号为20160001的学生的年龄改为22岁。
· 把表中所有学生的年龄增加1岁。
· 把表中计算机科学系全体学生的成绩置零。
要完成上述功能,可以分别使用下面的修改语句。
(1)把学号为20160001的学生的年龄改为22岁:
此例修改了一个元组的值。
(2)把表中所有学生的年龄增加1岁:
此例修改了表中所有的元组值。
(3)把表中计算机科学系全体学生的成绩置零:
此例子查询嵌套在UPDATE语句中,用以构造修改的条件。
删除数据语句的一般格式如下:
DELETE FROM<表名> [WHERE<条件>];
DELETE语句的功能是从指定表中删除满足WHERE子句条件的所有元组。如果省略WHERE子句,就表示删除表中全部元组,但表的定义仍在字典中。也就是说,DELETE语句删除的是表中的数据,而不是表的定义。DELETE可以删除一个或多个元组的值,也可以删除带子查询语句的值。
【例3.35】 对【例3.6】的Student表和SC表做如下删除操作:
· 删除学号为20160008的学生信息。
· 删除SC表中所有学生的选课信息。
· 删除计算机科学系所有学生的选课信息。
要完成上述功能,可以分别使用下面的删除语句。
(1)删除学号为20160008的学生信息:
此例只删除了一个元组的值。
(2)删除SC表中所有学生的选课信息:
此例删除了SC表中所有元组的值,导致SC表成为空表。
(3)删除计算机科学系所有学生的选课信息:
此例表示子查询同样可以嵌套在DELETE语句中,用以构造执行删除操作的条件。
视图是从一张或几张基本表(或视图)导出的虚表。视图之所以称为虚表,是因为数据库只存放视图的定义,而不存放视图对应的数据,视图中的数据仍存放在原来的基本表中。当基本表中的数据发生变化时,从视图中查询出的数据也随之变化。从这个意义上讲,视图就像一个窗口,透过它可以看到数据库中的数据及其变化。
视图具有与基本表一样的功能,可以对它进行查询、删除操作,也可以从一个视图导出新的视图,但对视图的更新(增、删、改)操作有一定的限制。本节主要讨论视图的定义和对视图的操作。
在SQL中,用CREATE VIEW命令建立视图,一般格式为:
其中:
· 视图的属性名要么全部省略,要么全部指定。当视图的属性名全部省略时,隐含视图的属性名与子查询SELECT语句的属性名相同。但若是下列3种情况,则必须明确指定视图的所有属性名。
◆ SELECT语句的某个属性名是聚集函数或列表达式。
◆ 多表连接时选出了几个同名属性作为视图的属性。
◆ 需要在视图中为某个属性定义新的更合适的名字。
· AS子查询语句可以是任意SELECT语句,但通常不允许含有ORDER BY子句和DISTINCT短语。
· WITH CHECK OPTION表示对视图进行更新、插入或删除操作时,满足视图定义中的子查询条件表达式。
视图可以从一张或多张基本表导出,也可以从视图中导出。下面给出一些具体示例。
【例3.36】 对【例3.6】的Student表建立如下视图:
· 建立信息系学生的视图。
· 建立信息系学生的视图,并要求对视图进行修改和插入操作时仍保证该视图只有信息系的学生。
要完成上述功能,可以使用下面的SQL语句。
(1)建立信息系学生的视图:
此例中,视图IS_ Student省略了属性名,隐含的属性由子查询中SELECT子句中的3个属性组成。
RDBMS执行CREATE VIEW语句的结果,只是把视图的定义存入数据字典,并不执行其中的SELECT语句。只有在对视图进行查询时,才按视图的定义从基本表中查出数据。
(2)建立信息系学生的视图,并要求对视图进行修改和插入操作时仍保证该视图只有信息系的学生:
此例中,在定义IS_ Student视图时,带有WITH CHECK OPTION子句,表示以后对该视图进行插入、修改和删除操作时,RDRMS会自动加上Sdept='IS'的条件。
如果一个视图从单张基本表中导出,并且只是去掉基本表中的某些行和不是主码的某些列,就称这类视图为行列子集视图。IS_ Student视图就是一个行列子集视图。
【例3.37】 对【例3.6】的Student表和SC表建立如下视图:
· 建立信息系中选修了l号课程的学生的视图。
· 建立信息系中选修了1号课程且成绩在90分以上的学生的视图。
要完成上述功能,可以使用下面的SQL语句。
(1)建立信息系中选修了l号课程的学生的视图:
此例视图IS_S1从Student和SC两张基本表中导出。由于视图IS_S1的属性中包含Student表与SC表同名的属性Sno,因此必须在视图名后定义视图的各个属性。
(2)建立信息系中选修了1号课程且成绩在90分以上的学生的视图:
此例的视图IS _S2是从视图IS_S1中导出的。
【例3.38】 对【例3.6】的Student表建立一个反映学生出生年份的视图,SQL语句如下:
在BT_S视图中,元组在出生年份属性Sbirth的分量是通过计算表达式2016-Sage得到的,此时称Sbirth属性为派生属性。
定义基本表时,为了减少数据库中的冗余数据,表中只存放基本数据,由基本数据经过各种计算派生出的数据一般不进行存储。但由于视图中的数据也不实际存储,因此定义视图时可以根据应用的需要设置一些派生属性。由于这些派生属性在基本表中并不实际存在,因此也称它们为虚拟列。带虚拟列的视图也称为带表达式的视图,BT_ S视图就是带表达式的视图。还可以用带有聚集函数和GROUP BY子句的查询定义视图,这种视图称为分组视图。
【例3.39】 在【例3.6】的SC表中,将学生的学号及平均成绩定义为一个视图,SQL语句如下:
由于AS子句中SELECT语句的属性列平均成绩是通过作用聚集函数得到的,因此CREATE VIEW中必须明确定义组成S_C视图的各个属性名,S_G是一个分组视图。
【例3.40】 将【例3.6】的Student表中所有女生的记录定义为一个视图,SQL语句如下:
此例视图F_Student由子查询SELECT*建立。F_Student视图的属性与Student表的属性一一对应。如果修改了基本表Student的结构,那么Student表与F_Student视图的映像关系也会发生改变,此时该视图不能正常工作。为了避免出现这类问题,最好在修改基本表结构之后删除以前从该基本表导出的所有视图,然后在修改后的基本表上重新建立视图。
删除视图的语句格式如下:
DROP VIEW <视图名>[CASGADE];
其中,CASGADE表示在删除该视图的同时也删除所有在该视图上定义的视图。
注意,删除视图实际上是在数据字典中删除视图的定义。删除基本表虽然并不能删除在该基本表上定义的视图,但是这些视图均无法使用。
【例3.41】 删除视图BT_S:
DROP VIEW BT_S;
删除视图IS_S1:
DROP VIEW IS_S1;
执行此语句时,由于IS _S1视图上还定义了IS _S2视图,因此该语句被拒绝执行。如果要删除视图IS _S1,就需要使用级联删除语句:
DROP VIEW IS _S1 GASOAUE; /*删除视图IS_S1和由它导出的所有视图*/
视图被定义后,用户就可以像对基本表一样对视图进行查询。
【例3.42】 在信息系学生的视图IS_Student中找出年龄小于20岁的学生,SQL语句如下:
RDRMS执行对视图的查询时,首先进行有效性检查。检查查询中涉及的表、视图等是否存在。如果存在,就从数据字典中取出视图的定义,把定义中的子查询和用户的查询结合起来,转换成等价的对基本表的查询,然后执行修正查询。这一转换过程称为视图消解。
本例转换后的查询语句为:
【例3.43】 查询选修了1号课程的信息系学生,SQL语句如下:
此例查询涉及视图IS_Student和基本表SC,通过这两张表的连接完成查询任务。
一般情况下,视图查询的转换可以直接进行。但在有些情况下这种转换不能直接进行,此时查询就会出现问题。
【例3.44】 在【例3.39】定义的S_G视图中查询平均成绩在90分以上的学生的学号及其平均成绩,SQL语句如下:
在【例3.39】中定义的S_G视图的子查询为:
将本例中的查询语句与定义S_G视图的子查询结合,形成下列查询语句:
因为在WHERE子句中不能用聚集函数作为条件表达式,所以执行此修正后的查询将会出现语法错误。正确转换的查询语句应该是:
目前,多数关系数据库系统对行列子集视图的查询均能进行正确转换。但对非行列子集视图的查询就不一定能转换了,因此这类查询应该直接对基本表进行查询。
更新视图是指通过视图来插入(INSERT)、删除(DELETE)和修改(UPDATE)数据。
由于视图是不实际存储数据的虚表,因此对视图的更新最终要转换为对基本表的更新。像查询视图那样,更新视图的操作也是通过视图消解转换为更新基本表的操作。
为防止用户在通过视图对数据进行增加、删除、修改时,有意无意地对不属于视图范围内的基本表数据进行操作,可以在定义视图时加上WITH CHECK OPTION子句。这样,在视图上增、删、改数据时,RDBMS会检查视图定义中的条件,若不满足条件,则拒绝执行该操作。
【例3.45】 将信息系学生视图IS_Student中学号为20160002的学生姓名改为“刘辰”,SQL语句如下:
转换后的更新语句为:
【例3.46】 向信息系学生视图IS_Student中插入一个新学生的记录,其中学号为20160006,姓名为赵新,年龄为20岁,SQL语句如下:
转换为对基本表的更新:
这里系统自动将系名'IS'放入VALUES子句中。
【例3.47】 删除信息系学生视图IS_ Student中学号为20160004的信息,SQL语句如下:
转换为对基本表的更新:
在关系数据库中,并不是所有的视图都是可更新的,因为有些视图的更新不能唯一且有意义地转换成对相应基本表的更新。
例如,【例3.39】中定义的视图S_G由学号和平均成绩两个属性组成,其中平均成绩一项由在Student表中对元组分组后计算平均值得来:
如果想把视图S_G中学号为20160001的学生的平均成绩改成90分,SQL语句如下:
但对这个视图的更新无法转换成对基本表SC的更新,因为系统无法修改各科成绩,以使平均成绩成为90,所以S_G视图不可更新。
一般情况下,行列子集视图可以更新。除此之外,还有一些理论上可以更新的视图,但是它们确切的特征还是尚待研究的课题。另外就是一些理论上就不可更新的视图。
目前,各个关系数据库系统一般只允许对行列子集视图进行更新,而且各个系统对视图的更新还有更进一步的规定。由于各个系统实现方法上的差异,这些规定也不尽相同。
视图是从基本表导出的虚表,对视图的所有操作实际上都转换为对基本表的操作。虽然对于非行列子集视图进行查询或更新时有可能出现问题,但是合理使用视图能够带来许多好处。
视图机制使用户可以将注意力集中在所关心的数据上,如果这些数据不是直接来自基本表,就可以通过定义视图使数据库看起来结构简单、清晰,并且可以简化用户的数据查询操作。例如,那些定义了若干张表连接的视图,就将表与表之间的连接操作对用户隐藏起来了。换句话说,用户需要做的只是对一张虚表的简单查询,而这张虚表是怎样得来的,用户无须了解。
视图机制能使不同的用户以不同的方式看待同一个数据。当许多不同种类的用户共享同一个数据库时,这种灵活性是非常重要的。
数据的逻辑独立性是指当数据库重构(如增加新的关系或对原有关系增加新的属性等)时,用户的应用程序不会受影响。
在关系数据库中,数据库的重构往往是不可避免的。重构数据库最常见的做法是将一张基本表“垂直”地分成多张基本表。例如,将学生关系Student(Sno,Sname,Ssex,Sage,Sdept)分解为SX(Sno,Sname,Sage)和SY(Sno,Ssex,Sdept)两个关系,这时原表Student为SX表和SY表自然连接的结果。
如果建立一个视图Student:
这样虽然数据库的逻辑结构改变了(变为SX和SY两张表),但应用程序不必修改,因为新建立的视图定义为用户原来的关系,用户的外模式保持不变,用户的应用程序通过视图仍然能够查找数据。
当然,视图只能在一定程度上提供数据的逻辑独立性。由于对视图的更新是有条件的,因此应用程序中修改数据的语句仍可能会因基本表结构的改变而改变。
有了视图机制,就可以在设计数据库应用系统时对不同的用户定义不同的视图,使机密数据不出现在不应看到这些数据的用户视图上。这样视图机制就自动提供了对机密数据的安全保护功能。例如,Student表涉及全校15个院系的学生数据,可以在表中定义15个视图,每个视图只包含一个院系的学生数据,并且只允许每个院系的主任查询和修改自己系的学生视图。
例如,如果经常需要执行这样的查询:“找出每个同学获得最高成绩的课程号”。可以先定义一个视图,求出每个同学获得的最高成绩:
然后用如下查询语句完成查询: