理论教育 SQL:关系数据库标准语言

SQL:关系数据库标准语言

时间:2023-08-18 理论教育 版权反馈
【摘要】:SQL已成为数据库领域中的主流语言。有人把确立SQL为关系数据库语言标准及其后的发展称为是一场革命。1986年10月,经美国国家标准局的数据库委员会X3H2批准,SQL被作为关系数据库语言的美国标准,同年公布了标准SQL文本。图1.5 SQL关系数据库三级模式结构以下是SQL的相关概念:①一个SQL数据库是表的汇集。

SQL:关系数据库标准语言

SQL(Structured Query Language),可读作“sequel”,即结构化查询语言,是国际化标准组织通过的关系数据库的标准语言,包括数据定义、查询、操纵和控制四种功能,SQL是一个通用的、功能极强的关系数据库语言。其功能不仅仅是查询。当前,几乎所有的关系数据库管理系统软件如Oracle、SQLServer、My SQL、Access等都支持SQL,许多软件运营商对SQL基本命令集还进行了不同程度的扩充和修改。SQL是实现数据库操作的一个最常用的途径,即使是在应用程序中,对数据库的操作也是通过嵌入到语句中的SQL语句完成的。因此,学好SQL语言是学好该课程的前提,也是本书的重点。

1.3.1 SQL概述

自SQL称为国际标准语言之后,世界各地的很多数据库运营商和厂家纷纷推出各自的SQL软件或与SQL的接口软件。这就使大多数数据库均采用SQL作为共同的数据存取语言和标准接口,使不同数据库系统之间的互操作有了共同的基础。SQL已成为数据库领域中的主流语言。这个意义十分重大。有人把确立SQL为关系数据库语言标准及其后的发展称为是一场革命。

1. SQL的产生与发展

1970年,美国IBM研究中心的E. F. Codd提出了关系模型,并连续发表了多篇论文,人们对关系数据库的研究也日渐深入。1972年IBM公司开始研制实验型关系数据库管理系统SYSTEMR,并为其配置了Square(Specifying Queries Relational Expression)查询语言。1974年,Boyce和Chamberlin在Square E语言的基础上进行了改进,产生了Sequel(Structured English Quary Language)语言,后来Sequel简称为SQL,即“结构式查询语言”。

SQL是一个综合的、功能极强的语言,且具有使用方便灵活、语言简洁、易学的优点,所以很快被业界接受。1986年10月,经美国国家标准局(American National Standard Insitute,ANSI)的数据库委员会X3H2批准,SQL被作为关系数据库语言的美国标准,同年公布了标准SQL文本(简称SQL-86)。1987年6月,国际标准化组织(International Organization For Standardization,ISO)也通过这一标准。随着数据库技术的不断发展,SQL标准也被不断的丰富和发展。ANSI在1989年10月又颁布了增强完整性特征的SQL89标准。1992年发布了SQL(1992)标准(被称为SQL2),1999年发布了SQL(1999)(称为SQL3)。

本书的讲解主要遵循SQL2标准,由于是教学用书,而不是SQL语言的使用手册,所以只能涵盖SQL最常用的一些特性,读者在实际使用中遇到的个别问题,可以查阅相关手册。

2. SQL的基本概念

SQL支持关系数据库三级模式结构。其中外模式对应于视图和部分基本表,模式对应于基本表,内模式对应于存储文件。但术语与传统关系模型术语不同,在SQL中,关系模式称为“基本表”,存储模式称为“存储文件”,子模式称为“视图”,元组称为“行”,属性称为“列”。如图1.5所示。

图1.5 SQL关系数据库三级模式结构

以下是SQL的相关概念:

①一个SQL数据库是表的汇集。

②一个SQL表由行集构成,行是列的序列,每列对应一个数据项

③表可以是基本表,也可以是视图。基本表是实际存储在数据库中的表。视图是从一个或几个基本表导出的表,它本身不独立存储在数据库中,即数据库中只存放视图的定义而不存放视图对应的数据,这些数据仍存放在导出视图的基本表中,因此视图是一个虚表。视图在概念上与基本表等同,用户可以在视图上再定义视图。

④一个基本表可以跨一个或多个存储文件,一个存储文件也可存放一个或多个基本表。另外,一个表可以带若干索引,索引也存放在存储文件中,存储文件与物理文件对应。

⑤用户可以用SQL语句对表进行操作,包括视图和基本表。基本表和视图一样,都是关系。

⑥SQL的用户可以是应用程序,也可以是终端用户。

3. SQL的特点

SQL之所以能够为广大用户和计算机工业界所接受,并成为国际标准,是因为它是一个综合的、功能极强同时又简捷易学的语言。SQL集数据查询、操纵、定义和控制功能于一体,主要特点包括以下几点:

①综合统一。SQL语言集数据定义语言、数据操纵语言和数据控制语言的功能于一体,语言风格统一,可以独立完成数据库生命周期中的全部活动,为数据库应用系统提供了良好的环境。用户在数据库系统投入运行后,还可以根据需要随时地、逐步地修改模式,且不影响数据库的运行,从而使系统具有良好的可扩展性

②高度非过程化。非关系数据模型的数据操纵语言是“面向过程”的,即是“过程化”的语言,用户不但要知道“做什么”,而且还应该知道“怎样做”,对于SQL,用户只需要提出“做什么”,无须具体指明“怎么做”,例如,存取位置、存取路径选择、具体处理操作过程均由系统自动完成。这种高度非过程化的特性大大减轻了用户的负担,使得用户更能集中精力考虑要“做什么”和所要得到的结果,并且存取路径对用户来说是透明的,有利于提高数据的独立性。

③面向集合的操作方式。在非关系数据模型中,采用的是面向记录的操作方式,即操作对象是一条记录。操作过程非常冗长复杂。而SQL语言采用的是面向集合的操作方式,且操作对象和操作结果都可以是元组的集合。

④统一的语法结构提供两种使用方式。SQL可用于所有用户,通过自含式语言和嵌入式语言两种方式对数据库进行访问,前者是用户直接通过键盘输入SQL命令,后者是将SQL语句嵌入到高级语言(如C、C++、VB、VC++、Java、C#等)程序中。这两种方式使用的是同一种语法结构。

⑤语言简洁,易学易用。尽管SQL的功能很强,但语言十分简洁,完成核心功能只需要使用9个动词: 数据查询(Select)、数据定义(创建Create,移除Drop,修改Alter)、数据操纵(插入Insert,更新Update,删除Delete)和数据控制(授权Grant,取消授权Revoke)。

1.3.2 数据定义

SQL语言的数据定义功能包括定义表、定义视图和定义索引,这里的定义实质上还包括对数据结构的定义、修改和删除。基本表和视图都是表,但基本表是实际存储在数据库中的表,视图是虚表,它是从基本表或其他视图中导出的表。

由于视图是基于基本表的虚表,索引又是依附于基本表的,大多数RDBMS支持的SQL语言,通常没有提供对视图和索引定义的修改操作,用户如果想修改这些对象的定义,只能先将其删除,然后再重建,但有些新的关系数据库管理系统软件提供了SQL语言修改视图的功能。在SQL中,一般将关系称为表(Table)。

1. 定义数据库

SQLServer中数据库的创建可用CREATE语句来实现,基本命令格式为:

CREATEDATABASE <数据库名>

例如建立学生信息数据库的命令为:

CREATEDATABASEStudent

当一个数据库及其所属的基本表、视图等都不需要时,可以用Drop语句删除这个数据库,其基本命令格式为:

DROPDATABASE <数据库名>

2. 定义基本表

建立表的第一步就是定义基本表的结构。SQL语言使用CREATETABLE语句定义基本表,其基本格式如下:

CREATETABLE <表名>(

A1 <数据类型>,

A2 <数据类型>,

A3 <数据类型>,

……

An <数据类型>;)

其中,CREATETABLE是关键字,表示我们将要定义一个新的关系模式; 圆括号里的A1,A2,A3,…,An是关系的属性名,每个属性名后面的数据类型就代表该属性对应的数据类型。系统执行了这条建表语句之后,就会在数据库中新建一个表,这个表里面还没有任何元组,直到系统执行了向表中插入元组的插入语句为止。

例: 以前的例子曾经描述过关系Student的模式: Student(Student No,Student Name, Age,Class)。对于学号Student No,我们决定把它说明为整数类型; Student Name说明为8个字符的定长字符串; 年龄Age也用整数类型; 班级Class用最多为20个字符的变长字符串类型。现在用如下语句将其建成SQL中的一个表:

CREATETABLEStudent(

Student No INT,

Student Name CHAR(8),

Age INT,

Class VARCHAR(20));

这只是一种最简单的描述,在实际应用中,我们通常要对某些属性做一定的约束,例如规定其不能为空,单值约束或者设定默认值等。这些约束写在相应属性数据类型的后面就可以了。NOTNULL表示某个属性的分量值不能为空。UNIQUE表示对某个属性进行单值约束; DEFAULT用来指定某个属性的分量的默认值。

例: 规定学号Student No不能为空,且对其进行单值约束; 对于年龄Age,默认值为18; 则建表语句变为:

CREATETABLEStudent(

Student No INTNOTNULLUNIQUE,

Student Name CHAR(8),

Age INTDEFAULT18,

Class VARCHAR(20));

执行完这条建表语句后,数据库中有了一个新表Student,此表暂时为空。

需要说明的是,不同的DBMS中支持的数据类型不完全相同。一个属性选用哪种数据类型要根据实际情况来决定,一般从取值范围和要做哪些运算两个方面来考虑,表1.23罗列了SQL2提供的主要数据类型。

表1.23 SQL2提供的主要数据类型

3. 修改基本表

基本表建成后,可以根据实际需要对基本表的结构进行修改,包括增加新的列或增加新的完整性约束条件、删除原有不再需要的列或删除旧的完整性约束条件。其基本命令格式为:

ALTERTABLE <表名>[ADD <新列名><数据类型>[完整性约束]][DROP<完整性约束名><完整性约束名>][MODIFY<列名><数据类型><数据类型>];

例: 向Student 表中增加一个“家庭住址”的 Home Address 的属性列,类型为VARCHAR型。

ALTERTABLEStudent ADDHome Address VARCHAR(100);

需要说明的是,这是笔者在可变长度VARCHAR型后加了长度为100的可变长度字符串。

在Student表中增加“年龄”的Age属性列,类型为SMALLINT型。

ALTERTABLEStudent ADDAge SMALLINT;

新增加的列不能定义为NOTNULL。基本表在增加一列后,原有元组在新增加的列上的值都被定义为空值(NULL)。

在Student表中删除“家庭住址”的Home Address的属性列。

ALTERTABLEStudent DROPCOLUMNHome Address;

在Student表中删除关于学号必须取唯一值的约束。

ALTERTABLEStudent DROPUNIQUE(Student No);

在Student表中将“学号”属性列的长度修改为20位。

ALTERTABLEStudent MODIFYStudent No CHAR(20);

4. 删除基本表

当某个基本表不再需要时,需要将其删除,以释放其所占的空间资源,删除基本表可以使用DROPTABLE语句实现,其格式如下:

DROPTABLE <表名>[RESTRICT CASCADE]

此处RESTRICT和CASCADE选项的使用与前面句法中的语义相同。需要注意的是,一旦对一个基本表执行了此删除操作后,该表中所有的数据也就丢失了,所以对于删除表的操作,用户一定要慎用。

例: 假设已经存在一个表,表名为“临时表”Temporary Tab,现将其删除,并将与该表相关的其他数据库对象一并删除。

DROPTABLETemporary Tab CASCADE;

前面曾经提到过,不同的数据库产品对于SQL语言的支持会有所不同,对于RESTRICT和CASCADE选项,目前居于主流的Oracle9i数据库只有CASCADE选项,而SQLServer数据库这两个选项都没有。

5. 建立索引

建立索引是加快查询速度的有效方法。索引实际上是根据关系(表)中某些字段的值建立一棵树型结构的文件。索引文件中存储的是按照某些字段的值排列的一组记录号,每个记录号指向一个待处理的记录,因此,索引实际上可以理解为根据某些字段的值进行逻辑排列的一组指针。在日常生活中,经常会遇到索引,如图书目录,工业词典索引等,通过索引可以大大提高查询的速度,但索引的功能仅限于查询。

目前,很多DBMS系统软件运营商和开发商直接使用主键的概念建立索引,方法是建立基本表时直接定义主键,即建立了主索引,一个表只能有一个主索引,同时用户还可以建立其他索引,不同的DBMS略有区别,如VFP有主索引、候选索引、普通索引和唯一索引4种类型的索引; Access中有重复索引和非重复索引; SQLServer中则是聚簇索引、非聚簇索引和唯一索引。

建立索引的基本命令格式为:

CREATE[UNIQUE][CLUSTER]INDEX <索引名> ON <表名>( <列名>[<次序>]

[,<列名>[<次序>]]…);

其中,<表名>是指要建立索引的基本表的名字,<索引名>是用户自己为建立索引起的名字。索引可以建立在该表的一列或多列上,各列名之间用逗号分隔,这种由两列或多列属性组成的索引称为复合索引(Composite Index)。每个列名后面还可以指定<次序>,即索引值的排列次序,可选ASC(升序)或DESC(降序),缺省值为ASC。UNIQUE表示每一个索引值对应唯一的数据记录。

例: 为Student表建立索引STU_INDEX_AGE,要求按年龄从小到大升序排列。

CREATEINDEXSTU_INDEX_AGEONStudent(Age);

6. 删除索引

索引建立后,系统会自动对其进行选择和维护,无需用户干预。如果数据频繁地增加、修改或删除,系统会花大量的时间来维护索引,不仅达不到建立索引减少查询时间的目的,反而降低了系统整体的效率。因此,用户可以根据实际需要删除一些不必要的索引。删除索引的基本命令格式为:

DROPINDEX <索引名>;

例如可以删除学生信息表中的年龄索引:

DROPINDEXSTU_INDEX_AGE;

需要注意的是,该命令不能删除由CREATETABLE或者ALTERTABLE命令创建的主键PRIMARY和唯一性约束UNIQUE索引,也不能删除系统表中的索引,这些约束条件必须用ALTERTABLE…DROP命令来完成。

1.3.3 数据查询

建立数据库的目的就是为了对数据库进行操作,以便能够从中提取有用的信息。在前面的内容中已经对数据库及其表结构进行了定义,从本节开始将开始介绍对数据库的操作,其中数据库查询是数据操作中的核心操作,SQL提供了SELECT语句对数据库进行查询操作。其标准语法是:

SELECT[ALL DISTINCT]<目标列表达式> [,<目标列表达式>]…

FROM <表名或视图名>[,<表名或视图名> ]…

[WHERE <条件表达式>]

[GROUPBY <列名1>[HAVING <条件表达式>]]

[ORDERBY <列名2>[ASC DESC]];

该语句的基本语义为,根据WHERE子句中的条件表达式,从FROM子句指定的基本表或视图中找出满足条件的元组,并按SELECT子句中指出的目标属性列,选出元组中的分量形成结果表。

实际上,语句中的SELECT子句的功能类似于关系代数中的投影运算,而WHERE子句的功能类似于关系代数中的选择运算。进行数据库查询时,并非上述语句中的每个子句都会用到,最简单的情况下,查询只需要一个SELECT和一个FROM子句。如果有GROUPBY子句选项,则将结果按<列名1>的值进行分组,该属性列值相等的元组为一个组,通常会在每组中使用聚集函数。如果GROUPBY子句带有HAVING短语,则结果只有满足指定条件的组。ORDERBY子句是将查询的结果进行排序显示,ASC表示升序,DESC表示降序,默认为升序排列。可选项[ALL DISTINCT]的含义是,如果没有指定DISTINCT短语,则缺省为ALL,即保留结果中取值重复的行,相反,如果指定了DISTINCT短语,则可消除重复的行。

列表达式可以是“列名1,列名2…”的形式。如果FROM子句指定了多个表,则列名应是“表名. 列名”的形式。

在本节内容中,需要引入图书管理系统中的3个表作为讲解的例子,这3个表分别为:

①M_STUDENT(借书证号,姓名,专业,性别,出生时间,借书数,照片,办证日期)

②M_BOOK(ISBN,书名,作者,出版社,价格,复本书,库存量)

③M_BORROW(借书证号,ISBN,借书时间,应还时间)

各种表中的数据分别如表1.24、表1.25和表1.26所示。

表1.24 M_STUDENT表

表1.25 M_BOOK表

表1.26 M_BORROW表

下面将查询分为简单查询、连接查询、嵌套查询以及集合查询等几类对该图书管理系统进行举例说明,通过这些例子可以看出查询语句的丰富功能和灵活的使用方式。

1. 单表查询

单表查询是指仅设计一个表的查询,一般只用到SELECT子句、FROM子句和WHERE子句。它们分别说明所查询列、查询的表或视图以及搜索条件等,这种查询通常也称为简单查询。

单表查询是最简单的SQL查询,指只涉及一个表的查询,可分为以下5种操作: 下面就这5种操作逐一进行详细的分析:

(1)选择表中的若干列

选择表中的若干列对应于关系代数中的投影运算,在SQL中利用SELECT子句来指定要投影的属性列。SELECT子句既可以指定表中所有的属性列,也可以指定个别读者感兴趣的属性列,还可以通过对列值进行算术运算得到表中不存在的信息。

①选择一个表中的指定列。使用SELECT子句选择其中一个表的某些列,各列名之间用逗号分隔。

例: 在M_STUDENT表中查询出所有借阅者的姓名、专业名和借书数。

SELECT 姓名,专业名,借书数

FROM M_STUDENT;

②查询全部列。使用SELECT子句查询一个表中的所有属性列且与表结构中的顺序相同时,可以使用通配符“* ”代替所有的列。

例: 在M_BORROW表中找出所有的借阅信息。

SELECT 借书证号,ISBN,借书时间,应还时间

FROM BORROW;

在该查询中,因为查询的是所有的属性列,且其顺序与表结构中的顺序相同,因此也可以表示为:

SELECT *

FROM BORROW;

上面的两个例子中的两个查询语句是等价的,查询结果相同。因此,如果要查询某个表的所有属性列,其查询语句有两种写法,一种是在SELECT子句中列出所有的属性列,一种是在SELECT子句中直接使用“* ”代替所有的属性列。但是使用第二种用法的前提是,用户所需要的属性列的顺序与数据库中的存储顺序相同。

③查询经过计算的值。使用SELECT进行查询时,不仅可以直接以列的原始值作为结果,而且还可以将对列值进行计算后所得的值作为查询结果,即在SELECT子句中可以使用表达式作为属性列。

例: 查询学生的姓名和年龄(目前的年份为2012年)。

由于表1.24M_STUDENT中没有年龄属性,所以不能直接列出年龄,但是SELECT子句中可以出现算术表达式,从而可以查询经过计算的值。

SELECT 姓名,2012-Year(出生时间)

FROM M_STUDENT;

其查询结果为:

该例中,通过运算得到的属性列系统都会自动地给它赋一个列名(Expr),用户看了之后不易理解,可以添加列的别名,以替换在结果中列出的默认列标题。则上述查询可以表示为:

SELECT 姓名,2012-Year(出生时间) AS ‘年龄’

FROM M_STUDENT;

其查询结果为:

SELECT子句中除了使用算术表达式外,还可以使用字符串常量、函数以及列别名,从而大大加强SQL查询的功能。

查询所有读者的姓名、还可借书籍的数量,要求两个字段之间用字符串“还可借书数:”进行连接,并为所计算的列指定列名“可借书数”(设每人最多可借书5本)。

SELECT 姓名,‘还可借书数:’,5—借书数AS‘可借书数’

FROM M_STUDENT;

其查询结果为:

SELECT语句使用ALL或DISTINCT选项来显示表中符合条件的所有行或删除其中重复的数据行,默认为ALL。使用DISTINCT选项时,对于所有重复的数据行在SELECT返回的结果集合中只保留一行。

(2)选择表中的若干元组

选择表中的若干元组对应于关系代数中的选择运算,使用FROM子句指定SELECT语句查询及查询相关的表或视图。

①消除结果集中的重复行。在关系数据库中,不允许出现完全相同的两个元组,但是当我们只选择表中的某些列时,就可能会出现重复的行。

例: 从M_BORROW表中找出所有借了书的读者的借书证号。

SELECT 借书证号

FROM M_BORROW;

本例中,可能某个读者借了5本书,则上述查询中就出现了“重号”,即一个借书证号出现了5次,在SELECT子句中用DISTINCT关键字可以消除结果集中的重复行,下述语句就去掉了查询结果中重复的元组。

SELECT DISTINCT借书证号

FROM M_BORROW;

②查询满足条件的元组。在SQL中,查询满足条件的元组,利用WHERE子句实现。WHERE子句常用的查询条件如表1.27所示。

表1.27 常用的查询条件

下面分别针对以上列出的查询条件,给出查询的实例:

Ⅰ. 比较运算

在表1.27中列出了一般的比较运算符,那么比较常用的运算符有: =(等于)、<>或! =(不等于)、>(大于)、<(小于)、>=(大于等于)、<=(小于等于)。

例: 查询M_STUDENT表中借书数在2本以上的学生情况。

SELECT*

FROMM_STUDENT

WHERE借书数 >=2;

Ⅱ. 指定范围

用于确定范围的关键字有BETWEEN…AND…和NOTBETWEEN…AND…,用来查找属性值在(或不在)指定范围内的元组,其中BETWEEN后是范围的下限(即低值),AND后是范围的上限(即高值)。

例: 查询M_BOOK表中价格介于20元到40元之间(包括20和40)的书籍的ISBN、书名、作者和出版社信息。

SELECT ISBN,书名,作者,出版社

FROM M_BOOK

WHERE价格BETWEEN20AND40;

相反的,如果要查询价格不在20元到40元之间的书籍信息,则可用NOTBETWEEN…AND…来表达:

SELECT ISBN,书名,作者,出版社

FROM M_BOOK

WHERE价格NOTBETWEEN20AND40;

Ⅲ. 确定集合

谓词IN可以用来查找属性值属于指定值表集合的元组,值表中列出所有可能的值,当IN前面的表达式与值表中的任何一个值匹配时,则返回True,否则返回False。

例: 查询STUDENT表中专业名为“地籍测量”、“摄影测量”的学生信息。

SELECT *

FROM M_STUDENT

WHERE专业名IN(‘地籍测量’,‘摄影测量’);

相反的,与IN相对的谓词是NOTIN,用于查找属性值不属于指定集合的元组。如查询除“工程测量”之外的所有专业的学生信息:

SELECT *

FROM M_STUDENT

WHERE专业名NOTIN(‘工程测量’);

Ⅳ. 字符匹配

谓词LIKE可以用来进行字符串的匹配,常用语模糊查找,它判断列值是否与指定的字符串格式相匹配。可用于char,varchar,text,ntext,datetime和smalldatetime等类型查询。

LIKE匹配中使用通配符的查询又称模糊查询。

其一般语法格式如下:

[NOT]LIKE‘<匹配符>’[ESCAPE‘<换码字符>’]

其含义是查找指定的属性列值与<匹配符>相匹配的元组。<匹配符>可以是一个完整的字符串,也可以含有通配符%和_。

百分号%可匹配单个任意字符,它常用来限制表达式的字符长度。

方括号[ ]指定一个字符、字符串或范围,要求所匹配对象为它们中的任一个。

[^]的取值和[ ]相同,但它要求所匹配对象为指定字符以外的任一个字符。

例如:

限制以“出版社”结尾,使用LIKE‘%%出版社’

限制以A开头,使用LIKE‘[A]%’

限制以非A开头,使用LIKE‘[^A]%’

例: 查询姓“张”作者所编著图书的书名、价格和出版社。

SELECT 作者,价格,出版社

FROM M_BOOK

WHERE作者LIKE‘张%’;

例: 查询姓“唐”且全名为3个汉字的作者编著的图书名、价格和出版社。

SELECT 作者,价格,出版社

FROM M_BOOK

WHERE作者LIKE‘唐____’;

这里需要注意的是,由于一个汉字占两个字符的位置,所以匹配串“唐”后需要跟4个_。

Ⅴ. 空值

在基本表中,如果哪一列中没有输入数据,则它的值就为空,空值用一个特殊的数据NULL来表示。如果要判断某一列是否为空,不能用“=NULL”或“<>NULL”来比较,只能用ISNULL或ISNOTNULL来运算。

例: 查询M_STUDENT表中专业名尚不确定的学生的详细信息。

SELECT *

FROMM_STUDENT

WHERE专业名IS NULL;

例: 查询所有借书证号不为空的学生信息。

SELECT 借书证号

FROM M_STUDENT

WHERE借书证号ISNOTNULL;

需要强调的是,这里的IS不能用等号( =)代替。

Ⅵ. 多重条件查询

逻辑运算符AND和OR可用来联结多个查询条件。如果这两个运算符同时出现在同一个WHERE条件子句中,则AND的优先级高于OR,但用户可以用括号改变优先级。

例: 查询BOOK表中价格在30元以下武汉大学出版社的图书和测绘出版社的图书的ISBN、书名、出版社和价格信息。

SELECT ISBN,书名,出版社,价格

FROM M_BOOK

WHERE出版社=‘武汉大学出版社’AND价格<30OR出版社=‘测绘出版社’;

例: 查询不是中国电力出版社也不是环境科学出版社出版的图书的书名和出版社。

SELECT 书名,出版社

FROM M_BOOK

WHERE出版社NOTIN(‘中国电力出版社’,‘环境科学出版社’);

在上面的列子中,IN谓词实际上是多个OR运算符的缩写,因此上面的查询语句也可以用OR运算符写成如下等价形式:

SELECT 书名,出版社

FROM M_BOOK

WHERE出版社=‘中国电力出版社’OR出版社=‘环境科学出版社’;

(3)使用聚集函数

为了进一步方便用户,增强检索功能,SQL提供了许多聚集函数,主要包括:

COUNT([DISTINCT ALL]* )统计元组个数

COUNT([DISTINCT ALL]<列名>)统计一列中值的个数

SUM([DISTINCT ALL]<列名>)计算一列值的总和(此列必须是数值型)

AVG([DISTINCT ALL]<列名>)计算一列值的平均值(此列必须是数值型)

MAX([DISTINCT ALL]<列名>)求一列值中的最大值

MIN([DISTINCT ALL]<列名>)求一列值中的最小值

例: 查询图书的种类。

SELECTCOUNT(* )

FROMM_BOOK;

例: 计算“测绘出版社”出版的图书的平均价格。

SELECTAVG(价格)

FROMM_BOOK

WHERE出版社=‘测绘出版社’;

例: 查询“武汉大学出版社”出版的单价最低的图书的价格。

SELECTMIN(价格)

FROMM_BOOK

WHERE出版社=‘武汉大学出版社’;

例: 查询借阅者总人数。

SELECTCOUNT(* )AS ‘总人数’

FROMM_STUDENT;

在该查询中,除了计算出M_STUDENT表中的总人数,还使用AS关键字为查询结果指定了新列名“总人数”。

(4)对查询结果分组

有时候我们需要的并不是某一列值的某种聚合,而是将这一列值根据其他某列或者某几列划分成组后每一组值的某种聚合。这时我们要在WHERE子句后面加上一个GROUP BY子句,关键字GROUPBY的后面给出分组属性列表。

需要说明的是,使用GROUPBY子句后,SELECT子句中的列表只能是GROUPBY子句中指定的列或在聚集函数中指定的列,否则系统会报错处理。

例: 查询STUDENT表中各个专业的学生数。

SELECT 专业名,COUNT(借书证号)AS‘学生数’

FROM M_STUDENT

GROUPBY专业名;

该语句对M_STUDENT表按专业名的取值进行分组,所有具有相同专业名的元组为一组,然后对每一组用聚集函数COUNT求得该组的学生人数,本查询中COUNT(借书证号)也可以换成COUNT(* )。

例: 查询BOOK表中各个出版社各有多少本图书。

SELECT 出版社,COUNT(* )AS‘图书数’

FROM M_BOOK

GROUPBY出版社;

如果分组后还需要按一定的条件对这些组进行筛选,最终只输出满足指定条件的组,则可以使用HAVING短语来指定筛选条件。

例: 查询出版的图书种类超过10种的出版社。

SELECT 出版社,COUNT(ISBN)

FROM M_BOOK

GROUPBY出版社

HAVINGCOUNT(ISBN) >10;

查询出版的图书种类超过10种的出版社,按出版社的取值进行分组,所有具有相同出版社的值的元组为一组,然后对每一组作用聚集函数COUNT以求得该出版社出版的图书种类。再用COUNT函数对每一组计数。如果某一组的元组数目大于10,则表示该出版社出版的图书超过10种,将出版社显示出来。HAVING短语制定选择组的条件,只有满足条件(即元组个数>10)的组才会被选出来。

WHERE子句与HAVING短语的根本区别在于作用对象不同。WHERE子句作用于基本表或视图,从中选择满足条件的元组; HAVING短语作用于组,从中选择满足条件的组。

(5)对查询结果排序

如果没有指定查询结果的显示顺序,DBMS将按其最方便的顺序(通常是元组在表中的先后顺序)输出查询结果。但在实际应用中,用户经常要对查询的结果排序输出, ORDERBY子句可用于对查询结果按照一个或多个属性列进行升序(ASC)或降序(DESC)排列,默认值为升序排列。

例: 查询M_BOOK表中机械工业出版社的图书,并按照价格进行由高向低排列。

SELECT *

FROM M_BOOK

WHERE出版社=‘机械工业出版社’

ORDERBY价格DESC;

例: 查询M_STUDENT表中所有学生的信息,查询结果按专业名升序排列,同一个专业的学生按照年龄升序排列(即按照出生年份降序排列)。

SELECT *

FROM M_STUDENT

ORDERBY专业名,出生年份DESC;

需要说明的是,使用ORDERBY子句对查询结果进行排序,当排序列含空值时,如果按升序排列,排序列为空值的元组最后显示; 如果按照降序排列,则排序列为空值的元组最先显示。

2. 连接查询

前面的查询都是针对单个表进行的。一个数据库中的多个表之间一般都存在某种内在联系,它们共同提供有用的信息。通过连接运算符可以实现多个表查询,连接是关系数据库模型的主要特点,也是区别于其他类型数据库管理系统的一个主要标志。

在关系数据库中,建立表时各个数据之间的关系不必确定,常把一个实体的所有信息存放在一个表中。当检索数据时,通过连接操作查询出存放在多个表中的不同实体的信息。连接操作给用户带来很大的灵活性,他们可以在任何时候增加新的数据类型,可以为不同实体创建新的表,然后通过连接进行查询。

一个查询同时涉及两个以上的表,则称之为连接查询。连接查询主要包括等值连接、非等值连接、自身连接查询、外连接查询、复合条件连接查询和集合运算查询。

(1)等值与非等值连接查询

WHERE子句中用来连接两个表的条件称为连接条件或连接谓词,其一般格式如下:

[<表名1>.]<列名1> <比较运算符>[<表名2>.]<列名2>

其中比较运算符主要有=、>、<、> =、< =、< >(或! =)等,当比较运算符为“=”时,称为等值连接,若在SELECT子句的目标列中去除相同的字段名,则为自然连接。使用其他的比较运算符称为非等值连接。

此外,连接谓词还可以使用下面形式:

[<表名1>.]<列名1> BETWEEN[<表名2>.]<列名2> AND[<表名2>.]<列名3>

需要说明的是,连接谓词中的列名称为连接字段。连接条件中的各连接字段类型必须是可比的,但名字不必相同。例如,可以都是字符型或都是日期型; 也可以一个是整型,另一个是实型,整型和实型都是数值型,因此是可比的。但若一个是字符型,另一个是整数型就不允许了,因为它们是不可比的类型。

例: 查询每个读者的基本信息及借书情况。

在图书借阅管理系统中,借阅者的基本信息存放在M_STUDENT表中,借阅者的借书情况存放在M_BORROW 表中,因此该查询实际上同时涉及了M_STUDENT和M_BORROW两个表中的数据。而这两个表之间的联系是通过两个表的共有属性“借书证号”实现的。完成本查询的SQL语句为: SELECT M_STUDENT.* ,M_BORROW.*FROM M_STUDENT,M_BORROWWHEREM_STUDENT. 借书证号= M_BORROW. 借书证号; 查询结果如下:

续表

该查询是等值连接查询,相同的属性列出现了两次。其执行过程是: 首先在M_STUDENT表中找到第一个元组,然后从头开始顺序扫描或按索引扫描表M_BORROW,查找M_BORROW表中借书证号与M_STUDENT表中第一个元组的借书证号相等的元组,每找到一个元组,就将M_STUDENT表中的第一个元组与该元组拼接起来,形成结果表中一个元组。M_BORROW表全部扫描完毕后,再到表M_STUDENT中找第二个元组,然后再从头开始顺序扫描或按索引扫描M_BORROW表,查找满足连接条件的元组,每找到一个元组,就将M_STUDENT表中的第二个元组与该元组拼接起来,形成结果表中一个元组。重复上述操作,直到M_STUDENT表全部元组都处理完毕为止。

需要说明的是,如果一个属性列属于两个或多个表,那么在使用时一定要在列名前加前缀“表名”,否则系统无法判断。比如在本例中,两个表中都有属性列“借书证号”,那么当查询语句中出现“借书证号”字段时,一定要注明“表名”前缀。

连接运算中有两种特殊情况,一种称为笛卡儿积连接,另一种称为自然连接。

笛卡儿积连接: 是不带连接谓词的连接。两个表的笛卡儿积即是两表中元组的交叉乘积,也是其中一表中的每一元组都要与另一表中的每一元组作拼接,因此结果表往往很大,并且查询结果会出现许多无意义的行,所以这种运算对于数据库管理员等计算机工作者很少使用。

自然连接: 如果是按照两个表中的相同属性进行等值连接,且目标列中去掉了重复的属性列,但保留了所有不重复的属性列,则称之为自然连接。

例: 对上述例子用自然连接完成。

SELECTM_STUDENT. 借书证号,姓名,专业名,性别,借书数,出生年份,办证日期,ISBN,借书时间,应还时间

FROM M_STUDENT,M_BORROW

WHEREM_STUDENT. 借书证号=M_BORROW. 借书证号;

查询结果如下:

在本查询中,由于姓名、专业名、性别、ISBN等属性列在两表中是唯一的,因此引用时可以去掉表名前缀,而借书证号在两个表都出现了,因此引用时必须加上表名前缀。该查询的执行结果不再出现借阅表的借书证号属性列M_BORROW. 借书证号。

(2)自身连接查询

连接操作不仅可以在不同的表上进行,而且在同一个表内可以进行自身连接,即将同一个表的不同行连接起来,所以自身连接又称为自连接。自身连接可以看做一个表的两个副本之间的连接。在自身连接中,必须为表指定两个别名,且对所有的引用均要用到别名限定,使之在逻辑上成为两张表。

例: 在M_BOOK表中查询同一作者及其所著的图书数名。

SELECTa1. 作者,a1. 书名

FROMM_BOOKa1JOINM_BOOKa2ona1. 作者=a2. 作者

WHEREa1.ISBN<>a2.ISBN;

查询结果如下:

(3)外连接查询

在通常的连接操作中,只有满足查询条件(WHERE搜索条件或HAVING条件)和连接条件的元组才能作为结果输出,这样的连接称作内连接。如在等值连接和非等值连接查询的例子中结果表中没有关于2009020145张丽萍、2009020312宋晨霖和2009021108薛晓娟这三位借阅者的信息,原因在于她们没有借阅图书,在借阅表M_BORROW中没有相应的元组,从而造成这位借阅者的信息被舍弃了。但是,有时可能需要以学生表M_STUDENT为主体列出每个学生的基本情况及其借阅图书情况,若某个学生没有借阅图书,则只输出其基本信息,其借阅信息为空值即可,也就是说,如果在实际应用中,也想同时输出那些不满足连接条件的元组,这时就需要使用外连接(Outer Join)。

外连接分为左外连接和右外连接。左外连接列出连接语句左边关系中所有的元组,使用运算符“* =”,如果连接语句右边关系中没有与之相匹配的元组,则在相应的属性上填空值(NULL),而右外连接是列出右边关系中所有的元组,使用运算符“=* ”,连接语句左边关系中没有与之相匹配的元组,则在相应的属性上填空值(NULL)。

例: 查询所有学生的信息及其借阅图书的ISBN和应还时间,如果没有借阅图书则直接列出其基本信息。

SELECTM_STUDENT. 借书证号,姓名,专业名,借书数,办证日期,ISBN,应还时间

FROM M_STUDENT LEFT OUT JOIN M_BORROW ON(M_STUDENT. 借书证号=

M_BORROW. 借书证号);

查询结果如下:

在该例中,属于左外连接,列出了连接语句左边关系M_STUDENT中的所有元组,若该学生没有借阅图书,则M_BORROW表中相应字段赋值为NULL; 同样可以用RIGHT OUTJOIN表示右外连接。

(4)复合条件查询

上面各个连接查询中,WHERE子句中只有一个条件,即一个连接谓词。WHERE子句中有多个条件的连接操作,称为复合条件连接。

例: 查询借阅了书名中含有“数据库”的图书的学生的借书证号、姓名、专业名、所借图书的ISBN、书名和应还书时间。

SELECTM_STUDENT. 借书证号,姓名,专业名,BORROW.ISBN,书名,应还时间

FROM M_STUDENT,M_BORROW,M_BOOK

WHERE M_STUDENT. 借书证号 = M_BORROW. 借书证号 AND M_BOOK.ISBN=

M_BORROW.ISBN AND 书名LIKE‘%数据库%’;

例: 查询工程测量专业学生借阅图书的信息,包括学生的姓名、借书数、所借书的书名、应还时间。

SELECT 姓名,借书数,书名,应还时间

FROM M_STUDENT,M_BORROW,M_BOOK

WHERE M_STUDENT. 借书证号 = M_BORROW. 借书证号 AND M_BOOK.ISBN=

M_BORROW.ISBNAND专业名=‘计算机’;

(5)集合运算查询

在连接查询中,还有一种比较特殊的查询,即集合查询。众所周知,简单查询的结果是元组的集合,那么多个简单查询的结果就可以进行集合的操作,在关系代数中,对于集合的基本操作主要包括并、交、差等运算,在SQL语言中,也提供了相应的运算符,分别为UNION(∪)、INTERSECT(∩)、EXCEPT(—)。

需要强调的是,参加集合操作的各查询结果的列数必须相同,对应项的数据类型也必须相同。另外,不同的DBMS所支持的集合操作不尽相同,如在Access、SQLServer2000数据库中仅支持并运算,目前的SQLServer2005就可以支持这三种集合运算。

例: 查询同时借阅了ISBN为9787503017193和9787503017481的图书的读者的借书证号。

(SELECT 借书证号

FROM M_BORROW

WHERE ISBN=‘9787503017193’)

INTERSECT

(SELECT 借书证号

FROM M_BORROW

WHERE ISBN=‘9787503017481’);

3. 嵌套查询

在SQL语言中,一个SELECT-FROM-WHERE语句称为一个查询块,在WHERE子句或HAVING短语所表示的条件中,可以使用一个查询块作为条件的一部分,这种将一个查询块嵌套在另一个查询块的WHERE子句或HAVING短语条件中的查询称为嵌套查询,又称为子查询。嵌套查询命令在执行时,每个子查询在上一级查询处理之前求解,即由里向外查,子查询的结果用于建立其父查询的查找条件。子查询是SQL语句的扩展,其语句形式如下:

SELECT <目标表达式1>[,…]

FROM <表或视图名1>(www.daowen.com)

WHERE[表达式](SELECT <目标表达式2>[,…]

FROM <表或视图名2>)

[GROUPBY <分组条件>

HAVING[<表达式> 比较运算符](SELECT <目标表达式2>[,…]

FROM <表或视图名2>)]

例如:

SELECT 书名

FROM M_BOOK

WHERE ISBN IN

(SELECTISBN

FROMM_BORROW

WHERE借书时间=‘2009-09-10’);

说明: 在上例中,下层查询块“SELECTISBNFROMM_BORROWWHERE借书时间=‘2009-09-10’”是嵌套在上层查询块“SELECT书名FROMM_BOOKWHEREISBNIN”的WHERE条件中的。上层的查询块又称为外层查询或父查询或主查询,下层查询块又称为内层查询或子查询。SQL语言允许多层嵌套查询,即一个子查询中还可以嵌套其他子查询,用来表示复杂的查询。嵌套查询可以用一系列简单查询构成复杂的查询,明显地增强了SQL的查询能力。以层层嵌套的方式构造程序是SQL中“结构化”的含义所在。

需要特别指出的是,子查询的SELECT语句中不能使用ORDERBY子句,ORDERBY子句永远只能对最终查询结果排序。

嵌套查询一般的求解方法是由里向外处理。即每个子查询在其上一级查询处理之前求解,子查询的结果用于建立其父查询的查找条件。嵌套查询主要包括以下4类,下面就这4种操作逐一进行详细的分析。

(1)带有IN谓词的子查询

带有IN谓词的子查询是指父查询与子查询之间用IN进行连接,用于判断某个属性列值是否在子查询的结果中。在嵌套查询中,由于子查询的结果往往是一个集合,所以谓词IN是嵌套查询中最常使用的谓词。

例: 查询与“邵明杰”同一个专业学生的借书证号、姓名、性别和借书数。

查询与“邵明杰”同专业学习的学生,可以首先确定“邵明杰”的专业名,然后再查找所有该专业的学生。所以可以先分步完成此查询,然后再构造嵌套查询。

首先确定“邵明杰”所在系专业名。

SELECT 专业名

FROM M_STUDENT

WHERE姓名=‘邵明杰’;

查询结果为: 工程测量

其次,查找所有工程测量专业的学生基本信息。

SELECT 借书证号,姓名,性别,借书数

FROM M_STUDENT

WHERE专业名IN(‘工程测量’);

将第一步嵌入到第二步的条件中,构造嵌套查询的形式,表示为:

SELECT 借书证号,姓名,性别,借书数

FROM M_STUDENT

WHERE专业名IN

(SELECT 专业名

FROM M_STUDENT

WHERE姓名=‘邵明杰’);

该查询还可以用自连接查询来实现:

SELECTS1. 借书证号,S1. 姓名,S1. 性别,S1. 借书数

FROM M_STUDENTS1,M_STUDENTS2

WHERE S1. 专业名=S2. 专业名ANDS2. 姓名=‘邵明杰’;

IN谓词用于判断某个属性列值是否在子查询的结果中,同样道理,NOTIN则可以用来判断某个属性列是否不在子查询的结果中。

(2)带有比较运算符的子查询

带有比较运算符的子查询是指父查询与子查询之间用比较运算符进行连接,使用带有比较运算符的子查询时,子查询一定要跟在比较运算符之后,当用户能确切知道内层查询返回的是单值时,可以用=,>,<,>=,<=,<>或! =等比较运算符。

例: 查询“武汉大学出版社”出版的并且单价小于所有图书平均价格的图书。

由于所有图书的平均价格的结果是唯一的,因此该查询也可以用比较运算符来实现,其SQL语句如下:

SELECT 书名

FROMM_BOOK

WHERE出版社=‘武汉大学出版社’AND价格<(SELECTAVG(价格)

FROMM_BOOK);

需要强调的是,子查询一定要跟在比较运算符之后,下列的写法是错误的。

SELECT 书名

FROMM_BOOK

WHERE出版社=‘武汉大学出版社’AND(SELECTAVG(价格)

FROMM_BOOK) >价格;

(3)带有ANY或ALL谓词的子查询

子查询返回单值时可以用比较运算符,而使用ANY或ALL谓词时则必须同时使用比较运算符。其语义如表1.28所示。

表1.28 比较运算符

例: 查询其他出版社中比“测绘出版社”任一图书单价都低的图书的书名和价格。

查询比“测绘出版社”任一图书单价都低的图书,实际上就是找出低于“测绘出版社”出版的图书中最高单价的图书。

SELECT 书名,价格

FROMM_BOOK

WHERE出版社 <>‘测绘出版社’AND价格<ANY(SELECT价格

FROMM_BOOK

WHERE出版社=‘测绘出版社’);

以上查询实际上也可以用集函数实现。先利用求最大值MAX函数找出“测绘出版社”出版的图书中最高单价,然后在父查询中查找出所有单价比前面的最高单价都低并且不是“测绘出版社”出版的图书,显示其书名、单价。

SELECT 书名,价格

FROMM_BOOK

WHERE出版社 <>‘测绘出版社’AND价格<(SELECT MAX(价格)

FROMM_BOOK

WHERE出版社=‘测绘出版社’);

例: 查询其他出版社中比“测绘出版社”所有图书单价都低的图书的书名和价格。

查询比“测绘出版社”所有图书单价都低的图书,实际上就是找出低于“测绘出版社”出版的图书中最低价格的图书。

SELECT 书名,价格

FROMM_BOOK

WHERE出版社 <>‘测绘出版社’AND价格<ALL(SELECT价格

FROMM_BOOK

WHERE出版社=‘测绘出版社’);

以上查询同样可以利用集函数实现。先利用求最小MIN函数找出“测绘出版社”出版的图书中最低价格,然后在父查询中查找出所有价格比前面的最低价格都低并且不是“测绘出版社”出版的图书,显示其书名和价格。

SELECT 书名,价格

FROMM_BOOK

WHERE出版社 <>‘测绘出版社’AND价格<ALL(SELECTMIN(价格)

FROMM_BOOK

WHERE出版社=‘测绘出版社’);

事实上,用集函数实现子查询通常比直接用ANY或ALL查询效率要高。ANY与ALL与集函数的对应关系如表1.29所示。

表1.29 ANY,ALL谓词与集函数及IN谓词的等价转换关系

(4)带有EXISTS谓词的子查询

EXISTS代表存在量词∃。带有EXISTS谓词的子查询不返回任何实际数据,它只产生逻辑真值“True”或逻辑假值“False”。

例: 查询借阅ISBN为9787503017193的图书的学生姓名、性别、专业名。

思路分析: 本查询涉及M_STUDENT和M_BORROW关系。在M_STUDENT中依次取每个元组的“借书证号”值,用此值去检查M_BORROW关系,若M_BORROW中存在这样的元组,其“借书证号”值等于此“M_STUDENT. 借书证号”值,并且其 ISBN =‘9787503017193’,则取此M_STUDENT的关系姓名、性别、专业名送入结果关系。

SELECT 姓名,性别,专业名

FROM M_STUDENT

WHERE EXISTS

(SELECT *

FROM M_BORROW

WHERE借书证号=M_STUDENT. 借书证号ANDISBN=‘9787503017193’);

通过以上思路分析以及所给定的查询语句可知,本例中子查询的查询条件需要依赖于父查询,因此该查询属于相关子查询,故其执行过程遵守相关子查询的执行算法,但是读者通过分析可以发现,对于外层查询而言,每次取一个元组的“借书证号”值后,子查询只要执行到了有结果值满足查询条件,则可停止执行子查询,返回逻辑真值给父查询,然后继续取外层查询的第二个元组……

此外,读者可以发现,在本例中,子查询没有指定具体的属性列,而是用“* ”代替,这是带有EXISTS谓词的子查询的一个特点,因为父查询只关心子查询是否有值返回,而不考虑返回的是什么值。因此子查询给出的列名无实际意义。

本例中的查询也可以用连接运算来实现,表示如下:

SELECT 姓名,性别,专业名

FROM M_STUDENT,M_BORROW

WHEREM_STUDENT. 借书证=M_BORROW. 借书证号ANDISBN=‘9787503017193’;

与EXISTS谓词相对应的是NOTEXISTS谓词,使用NOTEXISTS谓词后,若内层查询结果为空,则外层的WHERE子句返回TRUE,相反,若内层查询结果非空,则外层的WHERE子句返回FALSE。

例: 查询当前没有被借阅的图书的书名和出版社。

SELECT 书名,出版社

FROMM_BOOK

WHERENOTEXISTS(SELECT*

FROMM_BORROW

WHEREM_BORROW.ISBN=M_BOOK.ISBN);

4. 集合查询

每一个SELECT语句的查询结果都是由一个或多个元组构成的集合,若要把多个SELECT语句的结构完全相同的结果合并为一个结果,可用集合操作来完成,这种查询称为集合查询。标准SQL集合操作只要并操作UNION。

使用UNION将多个查询结果合并起来,形成一个完整的查询结果时,系统可以去掉重复的元组。需要强调的是,进行UNION操作的各结果表的列数必须相同,对应项的数据类型也必须相同。

使用UNION运算符时,应保证每个联合查询语句的选择列表中有相同数量的表达式,并且每个查询选择表达式应具有相同的数据类型,或是可以自动将它们转换为相同的数据类型。在自动转换时,对于数值类型,系统将低精度的数据类型转换为高精度的数据类型。

例: 查询地理信息专业的学生及年龄不大于20岁的学生。

SELECT *

FROMM_STUDENT

WHERE专业=‘地理信息’

UNION

SELECT *

FROMM_STUDENT

WHERE年龄<20;

本例中的查询是求地理信息专业的所有学生与年龄不大于20岁的学生的并集。使用UNION将多个查询结果合并起来,系统会自动去掉重复元组。

1.3.4 数据更新

前面的内容主要是对SQL的查询语句作了由浅入深的讨论,这些查询语句都不改变数据库中的数据,而是把数据库中的某些信息反馈给用户。然而,一个数据库能否保持信息的正确性、及时性,很大程度上依赖于数据库的更新功能。数据库的更新包括插入、删除和修改3种操作。插入语句是往关系表中插入元组; 删除语句是从关系表中删除某些元组; 修改语句则是修改关系表中已经存在的元组的某些分量的值。

1. 插入数据

数据库的信息时常需要改变,用户需要添加数据,INSERT语句提供了此功能。INSERT语句通常有两种形式,一种是插入一个元组,插入的数据以常量形式给出; 另一种是插入子查询的结果,即将查询的结果插入到表中,可以一次插入多个元组。

(1)插入单个元组

使用INSERT语句实现插入单个元组的基本格式如下:

INSERT

INTO<表名> [( <属性列1>[,<属性2>…)]

VALUES( <常量1>[,<常量2>]…);

其功能是将新元组插入指定表中。其中新记录属性列1的值为常量1,属性列2的值为常量2……INTO子句中没有出现的属性列,新记录在这些列上将取空值。

但必须注意的是,在表定义时说明了NOTNULL的属性列不能取空值。否则会出错。

如果INTO子句中没有指明任何列名,则新插入的记录必须在每个属性列上均有值。

例: 将一个新学生记录(借书证号: 2009020309; 姓名: 张朝阳; 专业名: 地理信息;性别: 男; 借书数: 1; 出生年份: 1989-11; 办证日期: 2009-10)插入到M_STUDENT表中。

INSERT

INTO M_STUDENT(借书证号,姓名,专业名,性别,借书数,出生年份,办证日期)

VALUES(‘2009020309’,‘张朝阳’,‘地理信息’,‘男’,‘1’,‘1989-11’,‘2009-10’);

本例中,属性列表的顺序与表结构中顺序相同,且为每个属性列都指定了值,此时可省略属性列表,表示为:

INSERT

INTO M_STUDENT

VALUES(‘2009020309’,‘张朝阳’,‘地理信息’,‘男’,‘1’,‘1989-11’,‘2009-10’);

但是,如果属性列表的顺序与表结构中的顺序不一致,因此不能省略INTO子句中的属性列表。

例: 插入一条图书记录(ISBN: 7560826903; 书名: 遥感影像的数字摄影测量; 作者: 陈鹰; 出版社: 同济大学出版社)插入到M_BOOK表中。

INSERT

INTO M_BOOK(ISBN,书名,作者,出版社)

VALUES(‘7560826903’,‘遥感影像的数字摄影测量’,‘陈鹰’,‘同济大学出版社’);

新插入的记录将在本属性列上赋默认值0,在复本和库存列上自动赋空值,本例也可以表示为:

INSERT

INTO M_BOOK

VALUES(‘7560826903’,‘遥感影像的数字摄影测量’,‘陈鹰’,‘同济大学出版社’, 0,NULL);

如果SQL拒绝了所插入的一列值,语句中其他各列的值也不会写入,这是因为SQL提供对事务的支持。一次事务将数据库从一种一致性转移到另一种一致性。如果事务的某一部分失败,则整个事务都会失败,系统将会自动回滚到此前事务之前的状态。因此必须注意几点: 首先在SQLServer2000插入数据时在VALUES子句中必须将所有列值写出,否则会出现“插入错误: 列名或所提供值的数目与表定义不匹配”错误。其次,所有的整型十进制数都不需要用单引号引起来,而字符串和日期类型的值都要用单引号来区别。同样输入文字值时要使用单引号。双引号用来封装限界标识符。最后,对于日期类型,必须使用SQL标准日期格式(yyyy-mm-dd)。但是在系统中可以进行定义,以接受其他的格式。

(2)插入子查询结果

子查询不仅可以嵌套在SELECT语句中,用以构造父查询的条件,也可以嵌套在INSERT语句中,用以生成要插入的批量数据。插入数据时,除了插入单个元组外,还可以将子查询嵌套在INSERT语句中,从而插入子查询的结果。插入子查询结果的INSERT语句格式如下:

INSERT

INTO <表名> [( <属性列1>[,<属性列2>…)]

子查询;

该语句中,子查询用以生成要插入的数据,整个语句的功能是批量插入,一次将子查询的结果全部插入指定表中。子查询中SELECT子句目标列不管是值的个数还是值的类型必须与INTO子句匹配。

例: 在数据库新建一个表,存放M_STUDENT表中各专业的学生人数。

首先在数据库中新建一张表,存放各专业的名称及学生人数。

CREATETABLEMajor_num

(专业名char(12),人数int);

然后求得各专业的人数并插入新建的表中。

INSERT

INTOMajor_num

SELECT 专业名,count(借书证号)

FROMM_STUDENT

GROUPBY专业名;

注意: DBMS在执行插入语句时会检查所插元组是否破坏表上已定义的完整性规则,包括实体完整性、参照完整性和用户定义的完整性。

2. 修改数据

修改操作又称为更新操作,语句的一般格式如下:

UPDATE<表名>

SET<列名>=<表达式>[,<列名>=<表达式>]…

[WHERE<条件>];

该语句的功能是修改指定表中满足WHERE子句条件的元组。其中SET子句用于指定修改方式、要修改的列和修改后的取值,即用<表达式>的值取代相应的属性列值。WHERE子句指定要修改的元组,如果省略WHERE子句,则表示要修改表中的所有元组。

注意: DBMS在执行修改语句时会检查所修改元组是否破坏表上已定义的完整性规则,包括实体完整性(一些DBMS规定主码不允许修改)、参照完整性和用户定义的完整性。

更新包括如下几种操作:

(1)更新表中全部的数据

例: 将所有学生的借书数清为0。

UPDATE M_STUDENT

SET借书数=0;

(2)更新表中某些元组的数据

例: 将武汉大学出版社的图书的复本数和库存量加10。

UPDATE M_BOOK

SET复本数=复本数+10,库存量=库存量+10

WHERE出版社=‘武汉大学出版社’;

(3)带子查询的修改

例: 将地理信息专业的所有学生的应还书日期改为2012-01-01。

UPDATE M_BORROW

SET应还时间=‘2012-01-01’

WHERE借书证号IN

(SELECT借书证号

FROM M_STUDENT

WHERE专业名=‘地理信息’);

或表示为:

UPDATE M_BORROW

SET应还时间=‘2012-01-01’

WHERE ‘地理信息’=

(SELECT专业名

FROM M_STUDENT

WHERE借书证号=M_BORROW. 借书证号);

3. 删除数据

删除语句的一般格式如下:

DELETE

FROM<表名>

[WHERE<条件>];

DELETE语句的功能是从指定表中删除满足WHERE子句条件的所有元组。如果省略WHERE子句,表示删除表中全部元组,但表的定义仍在字典中。也就是说,DELETE语句删除的是表中的数据,而不是关于表的定义。

(1)删除某个(某些)元组的值

例: 删除书号ISBN为9787503018039的图书。

DELETE

FROMM_BOOK

WHEREISBN=‘9787503018039’;

DELETE操作也是一次只能操作一个表,因此同样会遇到UPDATE操作中提到的数据不一致问题。比如9787503018039图书在M_BOOK表中被删除后,有关它的借阅信息也应同时删除,而这必须用一条独立的DELETE语句完成。

例: 2009级的学生毕业了,删除M_STUDENT表中2009级学生的记录(2009级学生的借书证号是以2009开头的)。

DELETE

FROM M_STUDENT

WHERE借书证号like‘2009%’;

(2)删除全部元组的值

例: 清空借阅记录表。

DELETE

FROM M_BORROW;

(3)带子查询的删除语句

例: 删除工程测量专业的所有学生的借阅记录。

DELETE

FROM M_BORROW

WHERE借书证号IN

(SELECT借书证号

FROM M_STUDENT

WHERE专业名=‘工程测量’);

或表示为:

DELETE

FROM M_BORROW

WHERE‘工程测量’=

(SELECT专业名

FROM M_STUDENT

WHERE借书证号=M_BORROW. 借书证号);

注意: DBMS在执行删除语句时,会检查所删元组是否破坏表上已定义的参照完整性规则,检查是否不允许删除或是需要级联删除。

1.3.5 视图

前面所讨论的关系,都是实际存在于数据库中的,它们不仅在逻辑上是一张表,在物理上也以表的形式存储于实际的存储介质上。这些表一般都是通过CREATETABLE语句建立的。

本节将讨论另一种SQL“关系”,它们实际上并不存在,只是在逻辑上可以看做是一张表,我们称之为“视图”(view)。我们可以把视图当成是普通的关系一样予以建立、查询、修改或者删除。

视图通常是按不同的应用领域或不同的用户群体进行定义,从而使用户从数据库中浩如烟海的数据中超脱出来,只关注自己需要的数据,透过它可以看到数据库中用户感兴趣的数据及其变化。另一方面,视图也是对数据库中的数据进行安全保护的一种机制,使数据库中一些保密的数据对无关人员成为不可见的,从而不能随意查询。

1. 定义视图

在SQL语言中,定义视图的基本语句如下:

CREATEVIEW<视图名>[( <列名>[,<列名>]…)]

AS<子查询>

[WITH CHECKOPTION];

该语句中,子查询可以是任意复杂的SELECT语句,可以来自一个表,也可以来自多个表,还可以来自一个或多个视图。但一般来说,SELECT语句中不允许含有ORDERBY子句和DISTINCT短语。

WITHCHECKOPTION选项指出在视图上进行UPDATE、INSERT、DELETE操作时要符合子查询中条件表达式所指定的限制条件。

例: 建立地理信息专业学生借阅图书的视图GISTUDENT(GI是地理信息Geographic Information的缩写),包括学生的借书证号、姓名、性别、所借书的ISBN和借书时间,且要保证对该视图进行修改和插入操作时都是地理信息专业的学生。

CREATEVIEWGISTUDENT

ASSELECTM_STUDENT. 借书证号,姓名,性别,ISBN,借书时间

FROMM_STUDENT,M_BORROW

WHEREM_STUDENT. 借书证号=M_BORROW. 借书证号AND专业名=‘地理信息’

WITHCHECKOPTION;

定义了这个视图GISTUDENT之后,我们就可以把GISTUDENT看做是一个虚拟的表。为了与这种虚拟表相区别,又把实际存在的表称为基本表。

例: 创建一个视图GISTUDENT_20121010,该视图中定义的是地理信息专业学生2012年10月10日前的借阅图书情况。

分析: 该例可以直接对表进行查询,建立视图,也可以对视图进行查询建立视图。

CREATEVIEWGISTUDENT_20121010

ASSELECT *

FROM GISTUDENT

WHERE借书时间<=‘20121010’;

可以看出,视图的内容就是子查询的查询结果,但要注意,视图的内容并不另外存放在数据库中,事实上,数据库中只存放视图的定义,实际的数据仍然放在原基本表的物理存放位置上。

定义基本表时,为了减少数据库中的冗余数据,表中只存放基本数据,由基本数据经过各种计算派生出的数据一般是不存储的。由于视图中的数据并不实际存储,所以定义视图时可以根据应用的需要设置一些派生属性列。这些派生属性由于在基本表中并不实际存在,所以有时也称它们为虚拟列,带虚拟列的视图我们称为带表达式的视图。

如果我们想为定义的视图重新设置属性名,则定义视图时在视图后面紧跟着属性名列表就可以了。

2. 查询视图

视图定义后,用户就可以像查询基本表一样查询视图了。DBMS在执行对视图的查询时,首先进行有效性检查,检查查询涉及的表、视图等是否在数据库中存在,如果存在,则从数据字典中取出查询涉及的视图的定义,把定义中的子查询和用户对视图的查询结合起来,转换成等价的对基本表的查询,然后再执行转换以后的查询。将对视图的查询转换为对基本表的查询过程称为视图的消解(View Resolution)。

例: 查询摄影测量专业2011年12月30日借书的学生的借书证号、姓名和ISBN。

SELECT借书证号,姓名,ISBN

FROM GISTUDENT

WHERE借书时间=‘2011-12-30’;

DBMS在执行此查询时,首先进行有效性检查,然后从数据字典中取出GISTUDENT视图的定义:

CREATEVIEWGISTUDENT

ASSELECT M_STUDENT. 借书证号,姓名,性别,ISBN,借书时间

FROM M_STUDENT,M_BORROW

WHEREM_STUDENT. 借书证号=M_BORROW. 借书证号AND专业名=‘摄影测量’

WITHCHECKOPTION;

再将二者进行合并消解,转换为对基本表的查询:

SELECT M_STUDENT. 借书证号,姓名,ISBN

FROM M_STUDENT,M_BORROW

WHEREM_STUDENT. 借书证号=M_BORROW. 借书证号

AND专业名=‘摄影测量’

AND借书时间=‘2011-12-30’;

一般来说,DBMS都可以将对视图的查询正确转换为对基本表的视图,但是,当对有些视图进行查询时,可能会出现语法错误。

例: 查询学生所借图书的总价值超过200元的学生的借书证号、姓名和总价值。

SELECT借书证号,姓名,总价值

FROM TOTAL_PRICE

WHERE总价值>200;

将该查询与对视图TOTAL_PRICE的定义结合起来,消解得到的查询语句为:

SELECT M_BORROW. 借书证号,姓名,SUM(价格)

FROMM_STUDENT,M_BOOK,M_BORROW

WHEREM_STUDENT. 借书证号=M_BORROW. 借书证号

ANDM_BOOROW.ISBN=M_BOOK.ISBNANDSUM(价格) >200

GROUPBYM_BORROW. 借书证号;

显然,转换为对基本表的查询语句是错误的,因为WHERE子句中不能用聚集函数作为条件表达式,正确的查询语句应该为:

SELECT M_BORROW. 借书证号,姓名,SUM(价格)

FROM M_STUDENT,M_BOOK,M_BORROW

WHERE M_STUDENT. 借书证号=M_BORROW. 借书证号ANDM_BOOROW.ISBN=M_BOOK.ISBN

GROUPBYM_BORROW. 借书证号HAVINGSUM(价格) >200;

因此,当视图的定义中出现了聚集函数所生成的属性列时,如果要对该视图进行有条件限制的查询,应该直接对基本表进行查询。

3. 更新视图

视图的查询可以像对基本表进行查询一样,但对视图的元组的更新就与基本表的数据更新不同,因为视图是不实际存储数据的虚表,因此对视图的更新,最终要转换为对基本表的更新。更新视图包括插入(INSERT)、删除(DELETE)和修改(UPDATE)3种操作。

为防止用户通过视图对数据进行增删改时,无意或故意操作不属于视图范围内的基本表数据,可在定义视图时加上WITHCHECKOPTION子句,这样在视图上增删改数据时, DBMS会进一步检查视图定义中的条件,若不满足条件,则拒绝执行该操作。此外,还有其他的一些视图也是允许更新的,但是确切的哪些视图可以更新是有待进一步研究的课题。

例: 将视图GISTUDENT中借书证号为“2009020523”的学生所借图书的ISBN改为9787802093461。

UPDATE GISTUDENT

SETISBN=‘9787802093461’

WHERE借书证号=‘2009020523’;

本例中该更新语句是可以执行的,且执行时也是转换为对基本表的更新。但是对于视图GISTUDENT和TOTAL_PRICE的更新是不允许的。

4. 删除视图

撤销视图的语句格式如下:

DROPVIEW<视图名>

一个视图被删除后,由此视图导出的其他视图也将失效,用户应该使用DROPVIEW语句将它们一一删除。

5. 视图的特点

通过前面的讲解,可能有的读者会问,对视图的操作最终要转换为对基本表的操作,而且对其进行更新还有很多的限制,既然如此,为什么还要使用视图呢?

合理的使用视图有如下几个优点:

①视图能够简化用户观点。在使用数据库的过程中,可能有部分数据是用户集中关心的数据,而且此数据要经过多次投影和连接操作才可获得,视图机制正好适应了用户的需要,用户所做的只是对一个虚表的简单查询,这个虚表是如何得来的、数据库是如何实现该查询的,用户不必关心,从而更加清晰地表达查询。

②视图在一定程度上保证了数据的逻辑独立性。本书开头曾经介绍过,数据的逻辑独立性是指用户的应用程序与数据的逻辑结构是相互独立的,即数据的逻辑结构改变了,用户程序也可以不变。因为视图来自于基本表,因此如果基本表的结构发生了改变,则只需要修改定义视图的子查询,一般不需要修改基于视图的操作或应用程序,从而在一定程度上保证了数据的逻辑独立性。

③视图在一定程度上提高了数据的安全性。有了视图机制就可以为不同的用户定义不同的视图,把数据对象限制在一定的范围内,也就是说,不同的用户只能看到与自己有关的数据,自动地对数据提供一定的安全保护。例如,在M_STUDENT表的基础上建立几个视图,分别包括各个专业的学生数据,这样就可以把学生的数据按照专业限制在不同的范围内,只有测绘工程专业的老师才可以查看本专业的学生,而无权去查看其他学院或者系专业的学生信息。

④视图使用户能以多种角度看待同一数据

视图机制能使不同的用户以不同的方式看待同一数据,当许多不同种类的用户使用同一个数据库时,这种灵活性是非常重要的。

1.3.6 数据控制

SQL语言提供了数据控制功能。数据控制也称为数据保护,包括数据的安全性控制、完整性控制、并发控制和恢复,能够在一定程度上保证数据库中数据的安全性、完整性,并提供了一定的并发控制及恢复能力。

并发控制指的是当多个用户并发地对数据库进行访问或操作时,对他们加以控制、协调,以保证并发操作正确执行,并保持数据库的一致性。恢复指的是当发生各种类型的故障,使数据库处于不一致状态时,将数据库恢复到一致状态的功能。

数据库的安全性是指保护数据库,防止不合法的使用所造成的数据泄露和破坏。这里所说的是SQL语言的安全性控制。SQL语言所提供的数据的安全性控制主要包括两个方面,一是对用户或者角色授予操作权限,二是收回对某用户或者角色的权限,所使用的语句是GRANT和REVOKE语句。

1. 授权

SQL语言用GRANT语句向用户授予操作权限,GRANT语句的一般格式如下:

GRANT<权限>[,<权限>]…

[ON<对象类型> <对象名>]

[TO<用户>[,<用户>]…

[WITHGRANTOPTION];

其语义为: 将对指定操作对象的指定操作权限授予指定的用户或角色。

接受权限的用户可以是一个或多个具体用户,也可以是PUBLIC即全体用户。如果指定了WITHGRANTOPTION子句,则获得某种权限的用户还可以把这种权限再授予别的用户。如果没有指定WITHGRANTOPTION子句,则获得某种权限的用户只能使用该权限,但不能传播该权限。

2. 回收权限

授予的权限可以由DBA或其他授权者用REVOKE语句收回,REVOKE语句的一般格式如下:

REVOKE<权限>[,<权限>]…

[ON<对象类型> <对象名>]

[FROM<用户>[,<用户>]…

【本章小结】

本章第一节首先阐述了数据库的基本概念,介绍了数据库与数据模型、数据库系统领域中的常用术语以及数据库系统的组成与结构,其次介绍了组成数据模型的三个要素、概念和三种主要的数据库模型(即层次模型、网状模型和关系模型)。学习本节应把注意力放在掌握基本概念和基本知识方面,为进一步学习后面的章节打好基础。

第二节是本章的重点。这是因为关系数据库系统是目前使用最广泛的数据库系统,本书所讲解的空间数据库也是基于关系的。

第三节系统而详尽地讲解了SQL语言。SQL是关系数据库语言的工业标准。各个数据库运营商所支持的SQL语言在遵循标准的基础上常常会作不同的扩充或修改。本节介绍的是标准SQL,因此,本章的绝大部分例子应能在不同的系统如Oracle、Sybase、DB2、My SQL、Foxpro、Access和SQLServer等众多系统上运行,同时也有几个例子列举了不同系统上运行时的区别。

在讲解SQL语言的同时进一步讲解了关系数据库系统的基本概念,使这些概念更加具体和丰富。

SQL语言可以分为数据定义、数据查询、数据更新和数据控制四大部分,有时人们把数据更新称为数据操纵,或把数据查询与数据更新合称数据操纵。SQL语言的数据查询功能是最丰富也是最复杂的,读者应加强练习。

【练习与思考题】

1. 试述数据、数据库、数据库管理系统、数据库系统的概念。

2. 使用数据库系统有什么好处?

3. 试述数据库系统的特点。

4. 数据库管理系统的主要功能有哪些?

5. 常用的数据模型有哪几种? 各有什么特点? 它们之间有什么联系?

6. 简述关系的完整性。

7. 试述关系数据语言的特点和分类。

8. 试述SQL语言的特点。

9. 简述SQL的定义功能。

10. 什么是基本表? 什么是视图? 两者的区别和联系是什么?

11. 简述视图的优点。

12. 所有的视图是否都可以更新? 为什么?

13. 用SQL语句建立一个学生基本信息表,要求列出学号、姓名、性别、出生日期、家庭住址,建成表格后,自行输入假定数据,完成对于属性列的查询和删除功能。

免责声明:以上内容源自网络,版权归原作者所有,如有侵犯您的原创版权请告知,我们将尽快删除相关内容。

我要反馈