Skip to content

第3章 关系数据库标准语言SQL

3.1 SQL概述⚓

3.1.2 SQL的特点⚓

结构化查询语言(Structured Query Language,SQL)是关系数据库的标准语言。

目前,没有一个数据库系统能够支持 SQL 标准的所有概念和特性。

SQL 集数据查询、数据操纵、数据定义和数据控制功能于一体,主要特点包括:

  1. 综合统一

    非关系模型(层次模型、网状模型)的数据语言一般都分为:

    • 模式数据定义语言, DDL
    • 外模式数据定义语言, 外模式 DDL
    • 数据存储有关的描述语言,DSDL
    • 数据操作语言,DML

    它们分别用于定义模式、外模式、内模式和进行数据的存取与处置。但是SQL 可以独立完成数据库生命周期的全部活动。

  2. 高度非过程化

    非关系数据模型的数据操纵语言是“面向过程”的语言,用“过程化”语言完成某项请求必须指定存取路径。

    而 SQL进行数据操作时,只要提出“做什么”,而无须指明“怎么做”,因此无须了解存取路径。存取路径的选择以及 SQL 的操作过程由系统自动完成。

  3. 面向集合的操作方式

  4. 以同一种语法结构提供多种使用方式

    既是独立的语言又是嵌入式语言(可以嵌入到高级语言程序中,比如 Java)。

  5. 语言简洁,易学易用

3.1.3 SQL 的基本概念⚓

支持 SQL 的关系数据库管理系统同样支持关系数据库三级模式结构。如图3.1所示,其中外模式包括若干视图(view)和部分基本表(base table),数据库模式包括若干基本表,内模式包括若干存储文件(stored file)。

存储文件的逻辑结构组成了关系数据库的内模式。

视图是一个虚表。

3.3 数据定义⚓

SQL 的数据定义功能包括 模式定义、表定义、视图和索引的定义(或定义数据库、定义基本表、定义视图和定义索引)。

一个关系数据库管理系统的实例中可以建立多个数据库,一个数据库中可以建立多个模式,一个模式下通常包括多个表、视图和索引等数据库对象。

3.3.1 模式的定义与删除⚓

pgsql 中的数据模式

定义:

CREATE SCHEMA <模式名> AUTHORIZATION <用户名>;

删除:

DROP SCHEMA <模式名> <CASCADE | RESTRICT>;
  • CASCADE,级联: 自动删除包含在该模式中的对象(表、函数等),然后删除所有 依赖于那些对象的对象。
  • RESTRICT,限制: 如果该模式含有任何对象,则拒绝删除它。

3.3.2 基本表的定义、删除与修改⚓

如果完整性约束涉及该表的多个属性列,则必须定义在表级上。

修改:

删除:

DROP TABLE <表名> [RESTRICT | CASCADE];

3.3.3 索引的建立与删除⚓

数据库索引有多种类型,常见索引包括:

  • 顺序文件上的索引 是针对按指定属性值升序或降序存储的关系,在该属性上建立一个顺序索引文件,索引文件由属性值和相应的元组指针组成。
  • B+树索引 是将索引属性组织成 B+树形式,B+树的叶结点为属性值和相应的元组指针。B+树索引具有动态平衡的优点。
  • 散列索引 是建立若干个桶,将索引属性按照其散列函数值映射到相应桶中,桶中存放索引属性值和相应的元组指针。散列索引具有查找速度快的特点。
  • 位图索引 是用位向量记录索引属性中可能出现的值,每个位向量对应一个可能值。

索引虽然能够加速数据库查询,但需要占用一定的存储空间,当基本表更新时,索引要进行相应的维护,这些都会增加数据库的负担,因此要根据实际应用的需要有选择地创建索引。

用户不必也不能显式地选择索引。索引属于内模式的范畴。

建立索引:

在SQL 语言中,建立索引使用 CREATE INDEX 语句,其一般格式为

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

UNIQUE 表明此索引的每一个索引值只对应唯一的数据记录。

CLUSTER 表示要建立的索引是聚簇索引。有关聚簇索引的概念在第7章7.5.2小节关系模式存取方法选择中介绍。

修改索引:

ALTER INDEX <旧索引名> RENAME TO <新索引名>;

删除索引:

DROP INDEX <索引名>;

3.3.4 数据字典⚓

数据字典(DD)是关系数据库系统内部的一组系统表,它记录了数据库中所有的定义信息,包括关系模式定义、视图定义、索引定义、完整性约束定义、各类用户对数据库的操作权限、统计信息等。

3.4 数据查询⚓

一般格式为:

SELECT [ALL | DISTINCT] <目标列表达式> [,<目标列表达式>] ···
FROM <表名或视图名>[,表名或视图名···]| (<SELECT 语句>)[AS]<别名>
[WHERE <条件表达式>]
[GROUP BY <列名1>[HAVING<条件表达式>]]
[ORDER BY <列名2> [ASC|DESC]];

3.4.1 单表查询⚓

<目标列表达式> 不仅可以是表中的属性列,也可以是表达、字符串常量、函数等。

聚集函数只能用于 select 子句和 group by 中的 having 子句。

分组后的聚集函数将作用于每一个分组,即每一组都有应该函数值。having 短语给出了选择分组的条件,满足条件的分组才会被选出来。

where 子句中不能使用聚集函数作为条件表达式。

3.4.2 连接查询⚓

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

  1. 等值与非等值连接查询

连接查询的 where 子句中用来连接两个表的条件称为连接条件或连接谓词,一般格式为

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

此外还可以使用下面形式:

[<表名1>.]<列名1> between [<表名2>.]<列名2> and [<表名2>.]<列名3>

当连接运算符为=时称为等值连接,其他的称为非等值连接。

连接谓词中的列名称为连接字段。连接条件中的各连接字段类型必须是可比的。

  1. 外连接

左外连接

3.4.3 嵌套查询⚓

一个SELECT-FROM-WHERE语句称为一个查询块。将一个查询块嵌套在另一个查询块的 where 子句或 having 短语的条件中的查询称为嵌套查询。

上层的查询块称为外层查询或父查询,下层查询块称为内层查询或子查询。

SQL 语言允许多层嵌套。但是子查询的 select 语句不能使用 order by 子句,该子句只能对最终查询结果排序。有些嵌套可以用连接运算符替代。

子查询条件不依赖于父查询,称为不相关子查询。否则称为相关子查询,整个查询语句称为相关嵌套查询。

嵌套查询的分类:

  • 带有 IN 谓词的子查询
  • 带有比较运算符的子查询。父查询和子查询之间用比较运算符进行连接。当用户能确切知道内层查询返回的是单个值时,可以用比较运算符连接。

    例3-57 相关子查询

  • 带有 ANY(SOME)或 ALL 谓词的子查询。返回多值时要用 ANY(SOME)或 ALL 谓词修饰符修饰比较运算符。

    ANY 修饰符

  • 带有 EXISTS / NOT EXISTS 谓词的子查询。带有 EXISTS 谓词的子查询不返回任何数据,只返回逻辑真值true或false。由 EXISTS 引出的子查询,其目标列表达式通常都用*,因为带 EXISTS的子查询只返回真值或假值,给出列名无实际意义。

    例 3-60

    例 3-62

3.4.4 集合查询⚓

多个 select 语句的结果可进行集合操作,主要包括 并操作 UNION、交操作 INTERSECT 和差操作 EXCEPT。

使用 UNION 时系统会自动去掉重复元组,若要保留则使用 UNION ALL。

3.4.5 基于派生表的查询⚓

子查询还可以出现在FROM子句中,此时子查询生成的临时表叫做派生表。必须为派生表指定一个别名。

3.4.6 SELECT语句的一般格式⚓

select 语句的一般格式

其中,MAX 中的列名可以是字符、字符串、日期和数值类型等,不限制数据类型。

SELECT 语句执行的结果是一个临时表,也叫做派生表。

3.5 数据更新⚓

3.5.1 插入数据⚓

子查询结果也可插入:

INSERT INTO <表名> [(<属性列 1> [,<属性列 2>]...)] 子查询;

3.5.2 修改数据⚓

子查询也可嵌套在 update 语句的 where 子句中。

3.5.3 删除数据⚓

子查询也可嵌套在 delete 语句的 where 子句中。

3.6 空值的处理⚓

外连接会产生空值,空值的关系运算也会产生空值。

用IS NULL或IS NOT NULL来判断空值。

属性定义(或域定义)中有NOT NULL约束条件的结果为空值,空值与另一个值(包括空值)的算术运算结果是空值,空值与另一个值(包括空值)的比较运算结果为UNKNOWN。

逻辑运算符真值表

  • T: TRUE
  • F: FALSE
  • U: UNKNOWN

3.7 视图⚓

视图是从一个或几个基本表(或视图)导出的表。与基本表不同,是一个虚表。数据库中只存放视图的定义,不存放视图对应的数据。

3.7.1 定义视图⚓

CREATE VIEW <视图名> [(<列名> [,<列名>]...)]
AS <子查询>
[WITH CHECK OPTION];

其中,子查询可以是任意的 select 语句。

WITH CHECK OPTION 表示对视图进行 update、insert、delete 操作时要保证更新、插入或删除的行满足视图定义中的谓词条件(即子查询中的条件表达式),关系数据库管理系统会自动加上谓词条件。

组成视图的属性列名要么全部省略要么全部指定。但下列三种情况下必须指定组成视图的所有列名:

  1. 某个目标列不是单纯的属性名,而是聚集函数或列表达式
  2. 多表连接时选择了几个同名列作为视图的字段
  3. 需要在视图中为某个列启用新的更合适的名字

若一个视图是从单个基本表导出的,并且只是去掉了基本表的某些行和某些列,但保留了主码,则称这类视图为行列子集视图。

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

用带有聚集函数和 group by 子句的查询来定义视图,称为分组视图。

删除视图:

DROP VIEW <视图名> [CASCADE];

CASCADE 级联删除语句可以使视图删除后将导出的其它视图也一起删除。

但是基本表删除后,需要显式地使用 DROP VIEW 删除。

3.7.2 查询视图⚓

关系数据库管理系统执行对视图的查询时,首先进行有效性检查,检查查询中涉及的表、视图等是否存在。如果存在,则从数据字典中取出视图的定义,把定义中的子查询和用户的查询结合起来,转换成等价的对基本表的查询,然后再执行修正了的查询。这一转换过程称为视图消解(view resolution)。

但定义视图并查询视图与基于派生表(临时表)的查询是有区别的。视图一旦定义,其定义将永久保存在数据字典中,之后的所有查询都可以直接引用该视图。而派生表只是在语句执行时临时定义,语句执行后该定义即被删除。

3.7.3 更新视图⚓

对视图的更新最终要转换为对基本表的更新。

但并不是所有的视图都是可更新的。因为不一定能唯一地有意义地转换成对相应基本表的更新。目前,各个关系数据库管理系统一般都只允许对行列子集视图进行更新,而且各个系统

由于各系统实现方法上的差异,这些规定也不尽相同。 例如,DB2 规定:

  1. 若视图是由两个以上基本表导出的,则此视图不允许更新。
  2. 若视图的字段来自字段表达式或常数,则不允许对此视图执行 INSERT 和 UPDATE 操作,但允许执行 DELETE 操作。
  3. 若视图的字段来自聚集函数,则此视图不允许更新。
  4. 若视图定义中含有 GROUP BY 子句,则此视图不允许更新。
  5. 若视图定义中含有 DISTINCT 短语,则此视图不允许更新。
  6. 若视图定义中有嵌套查询,并且内层查询的FROM 子句中涉及的表也是导出该视图的基本表,则此视图不允许更新。
  7. 一个不允许更新的视图上定义的视图也不允许更新。

3.7.4 视图的作用⚓

  1. 能够简化用户的操作
  2. 使用户能以多种角度看待同一数据
  3. 视图对重构数据库提供了一定程度的逻辑独立性
  4. 能够对机密数据提供安全保护
  5. 适当利用视图可以更清晰地表达查询