文章目录
1. 数据库语言
数据结构化语言 (Structured Query Language,SQL),在关系数据库中最普遍使用的语言,是一种通用的、功能强大的关系数据库标准语言。
主要功能:数据查询 (Data Query)、数据操纵 (Data Manipulation)、数据定义 (Data Definition)、数据控制 (Data Control)
基本表:实际存储在数据库中的表;
视图:由若干个基本表或其它视图导出的表,是一个虚表;
SQL 用户:可以是应用程序,也可以是终端用户;
任何一个数据库系统都应向用户提供一种数据库语言,其中包括数据定义语言和数据操纵语言;SQL 语言是集数据定义和数据操纵为一体的典型数据库语言。数据库语言与数据模型密切相关,基于不同的数据模型,数据库语言也不同。
数据定义语言 (Data Definition Language,DDL) :用来定义数据库模式,包括数据库模式定义、数据存储结构和存取方法定义,数据库模式的修改和删除功能。
数据操纵语言 (Data Manipulation Language,DML) :用来表示用户对数据库的操作请求,通常数据操纵语言 DML 能进行的操纵有插入、删除、修改、查询数据库中的信息,简称增删改查。
SQL 可以作为独立语言在终端以交互的方式使用,也可以作为程序设计的子语言使用,即嵌入到高级语言中使用,这种方式下使用的 SQL 称为嵌入式 SQL ,嵌入式 SQL 的高级语言称为宿主语言。
在 DBMS 中,对宿主型数据库语言 SQL 采用两种方法处理:第一种方法是采用预编译;第二种方法是修改和扩充主语言,使之能处理 SQL 语句。
2. SQL 概述
2.1 SQL 的特点
- 综合统一。非关系模型的数据语言分为模式定义语言和数据操纵语言,其缺点是当要修改模式时,必须停止现有数据库的运行,转储数据,修改模式编译后再重装数据库。SQL 集数据定义、数据操纵和数据控制功能于一体,语言风格统一,可独立完成数据库生命周期的所有活动。
- 高度非过程化。当进行数据操作时,只要指出“做什么”,无须指出“怎么做”,存储路径对用户来说是透明的,提高了数据的独立性。
- 面向集合的操纵方式。非关系数据模型采用的是面向记录的操作方式,操作对象是一条记录。而 SQL 语言采用面向集合的操作方式,其操作对象、查找结果可以是元组的集合。
- 两种使用方式。第一种方式,用户可以在终端输入 SQL 命令,对数据库进行运行操作,称为自含式语言;第二种方式,将 SQL 语言嵌入到高级程序中 (Python等),称为嵌入式语言。
- 语言简洁、易学易用。
- 数据查询:
SELECT
,该动词是 SQL 中用得最多的动词。 - 数据定义:
CREATE
、DROP
、ALTER
,用于创建新表、修改表和删除表。 - 数据操作:
INSERT
、UODATE
、DELETE
,用于数据的插入、修改和删除。 - 数据控制:
GRANT
、REVORK
,用于数据库对象访问的权限授予和回收。
- 数据查询:
2.2 SQL 语言支持三级模式结构
SQL 语言支持关系数据库的三级模式结构 (外模式、模式、内模式),其中,视图对应外模式、基本表对应模式、存储文件对应内模式。
2.3 SQL 的基本组成
- 数据定义语言。SQL DDL 提供定义关系模式和视图、删除关系和视图、修改关系模式的命令。
- 交互式数据操作语言。SQL DML 提供查询、插入、删除和修改的命令。
- 事务控制 (Transaction Control)。SQL 提供定义事务开始和结束的命令。
- 嵌入式 SQL 和动态 SQL ( Embedded SQL and Dynamic SQL )。用于嵌入到某种通用的高级语言 (C、C++、Java、Python、PL/I、COBOL 和 VB 等) 中混合编程。其中,SQL 负责操作数据库,高级语言负责控制程序流程。
- 完整性 ( Integrity )。SQL DDL 包括定义数据库中的数据必须满足的完整性约束条件的命令,对于破坏完整性约束条件的更新将被禁止。
- 权限管理 ( Authorization )。SQL DDL 中包括说明关系和视图的访问权限。
3. 数据定义
3.1 数据类型
SQL 支持的内部基本类型如下:
3.2 创建表
语句格式:
CREATE TABLE <表名>
(
<列名> <数据类型> [列级完整性约束条件]
[,<列名> <数据类型> [列级完整性约束条件]
...
[,<表级完整性约束条件>]
);
列级完整性约束有 NULL (空) 和 UNIQUE (取值唯一) ,如 NOT NULL UNIQUE 表示取值唯一,不能取空值。
示例1. 建立一个供应商、零件数据库。其中关系供应商 S(Sno,Sname,Status,City) 属性名分别表示供应商代码、供应商名、供应商状态和供应商所在城市;关系零件 P(Pno,Pname,Color,Weight,City) 属性名分别表示零件号、零件名、颜色、重量及产地。该数据库要满足如下要求:
(1)供应商代码不能为空,且值是唯一的,供应商的名也是唯一的。
(2)零件号不能为空,且值是唯一的;零件名不能为空。
(3)一个供应商可以供应多个零件,而一个零件可以由多个供应商供应。
分析:(1)表示 Sno 非空唯一约束,Sname 唯一约束;(2)表示 Pno 非空唯一约束,Pname 非空约束;(3)表示供应商和零件之间是多对多的联系,在关系数据库中,多对多联系必须生成一个关系模式,而该模式的码是该联系两端实体的码加上联系的属性构成的,若该联系名为SP,那么关系模式为SP(Sno, Pno,Qty),另外供应商和零件分别要建立一个关系模式。
CREATE TABLE S
(
Sno CHAR(5) NOT NULL UNIQUE
,Sname CHAR(30) UNIQUE
,Status CHAR(8)
,City CHAR(20)
,PRIMARY KEY(Sno)
);
CREATE TABLE P
(
Pno CHAR(6)
,Pname CHAR(30) NOT NULL
,Color CHAR(8)
,Weight NUMERIC(6,2)
,City CHAR(20)
,PRIMARY KEY(Pno)
);
CREATE TABLE SP
(
Sno CHAR(5) NOT NULL UNIQUE
,Pno CHAR(6) NOT NULL UNIQUE
,Qty NUMERIC(9)
,PRIMARY KEY(Sno,Pno)
,FOREIGN KEY(Sno) REFERENCES S(Sno)
,FOREIGN KEY(Pno) REFERENCES P(Pno)
);
PRIMARY KEY(Pno) 已经定义了 Pno 为主码,所以 Pno CHAR(6) NOT NULL UNIQUE 语句中的 NOT NULL UNIQUE 可以省略。
3.3 修改和删除表
修改表语法格式:
ALTER TABLE <表名>
[ADD COLUMN <新列名> <数据类型> [完整性约束条件]
[DROP COLUMN <列名>]
[DROP [CONSTRAINT]<完整性约束名>]
[MODIFY <列名> <数据类型>];
删除表语法格式:
DROP TABLE <表名>;
示例2. 向“ 供应商” 表S 增加Zap “ 邮政编码”;将 Status 字段改为整型;删除供应商表。
# 向“ 供应商” 表S 增加Zap “ 邮政编码”
ALTER TABLE S ADD Zap CHAR(6);
# 将 Status 字段改为整型
ALTER TABLE S MODIFY Status INT;
# 删除供应商表
DROP TABLE S;
3.4 创建和删除索引
数据库中的索引与书籍中的目录页面类似,利用目录页面可以快速查找信息,在数据库中,索引使数据库程序无须对整个表进行扫描,就可以在其中找到所需数据。数据库中的索引是某个表中一列或者若干列的集合和相应的指向表中物理标识这些值的数据页的逻辑指针清单。
索引作用如下:
- 通过创建唯一索引,可以保证数据记录的唯一性。
- 可以大大加快数据检索速度。
- 可以加快表与表之间的连接,这一点在实现数据的参照完整性方面有特别的意义。
- 在使用 ORDER BY 和 GROUP BY 子句中进行检索数据时,可以显著减少查询中分组和排序的时间。
- 使用索引可以在检索数据的过程中使用优化隐藏器,提高系统性能。
索引分为聚集索引和非聚集索引。聚集索引时指索引项的顺序与表中记录的物理顺序一致的索引。
创建索引语法格式:
CREATE [UNIQUE] [CLUSTER|NONCLUSTER]
INDEX <索引名>
ON <表名>(<列名>[<次序>][,<列名>[<次序>]);
参数说明如下:
- 次序:可选择 ASC (升序) 或 DESC (降序) ,默认值为 ASC。
- UNIQUE:表明此索引的每一个索引值只对应唯一的数据记录。
- CLUSTER:表明要建立的索引是聚集索引,意为索引项的顺序是与表中记录的物理顺序一致的索引组织,默认。
- NONCLUSTER:表示建立的索引是非聚集索引。
删除索引语法格式:
DROP INDEX <索引名> ON <表名>;
示例3. 假设供应销售数据库中有供应商S、零件P、工程项目J、供销情况SPJ 关系,希望建立4个索引。其中,供应商S中Sno按升序建立索引;零件P中Pno按升序建立索引;工程项目J中Jno按升序建立索引;供销情况SPJ中Sno按升序,Pno按降序,Jno按升序建立索引。删除其中一个索引。
CREATE UNIQUE INDEX SNS-O ON S(Sno);
CREATE UNIQUE INDEX P-PNO NO P(Pno);
CREATE UNIQUE INDEX J-JNO ON J(Jno);
CREATE UNIQUE INDEX SPJ-NO ON SPJ(Sno ASC, Pno DESC, JNO ASC);
DROP INDEX SNS-O ON SNS-O;
3.5 创建和删除视图
视图是从一个或者多个基本表或视图中导出的表,其结构和数据是建立在对表的查询基础上的;视图不是真实存在的基本表,而是一个虚拟表,视图所对应的数据并不实际地以视图结构存储在数据库中,而是存储在视图所引用的表中。
视图的优点和作用如下:
- 可以使视图集中数据、简化和定制不同用户对数据库的不同数据要求。
- 可以屏蔽数据的复杂性,用户不必了解数据库的结构,就可以方便地使用和管理数据,简化数据权限管理和重新组织数据以便输出到其他应用程序中。
- 可以使用户只关心他感兴趣的某些特定数据和所负责的特定任务,而那些不需要的或者无用的数据则不在视图中显示。
- 大大地简化了用户对数据的操作。
- 可以让不同的用户以不同的方式看到不同或者相同的数据集。
- 在某些情况下,由于表中数据量太大,因此在表的设计时常将表进行水平或者垂直分割,但表的结构的变化对应用程序产生不良的影响。
- 提供了一个简单而有效的安全机制。
创建视图语法格式:
CREATE VIEW 视图名(列表名)
AS SELECT 查询子句
[WITH CHECK OPTION];
视图的创建,必须遵循如下规定:
(1)子查询可以是任意复杂的 SELECT 语句,但通常不允许含有 ORDER BY 子句和 DISTINCT 短语。
(2)WITH CHECK OPTION 表示对 UPDATE,INSTER,DELETE 操作时保证更新、插入或删除的行满足视图定义中的谓词条件(即子查询中的条件表达式)。
(3)组成视图的属性列名或者全部省略或者全部指定。如果省略属性列名,则隐含该视图由 SELECT 子查询目标列的主属性组成。
删除视图语法格式:
DROP VIEW 视图名;
示例4. 若学生关系模式为 Student(Sno,Sname,Sage,Sex,SD,Email,Tel),建立“计算机系”(CS表示计算机系)学生的视图,并要求进行修改、插入操作时保证该视图只有计算机系的学生。
CREATE VIEW CS_STUDENT
AS SELECT Sno,Sname,Sage,Sex
FROM Student
WHERE SD = 'CS'
WITH CHECK OPTION;
DROP VIEW CS_STUDENT;
4. 完整性约束
数据库的完整性是指数据库正确性和相容性,是防止合法用户使用数据库时向数据库加入不符合语义的数据。保证数据库中数据是正确的,避免非法的更新。数据库完整性重点需要掌握的内容有:完整性约束条件的分类、完整性控制应具备的功能。
完整性约束类别大概分为实体完整性约束、参照完整性约束、自定义完整性约束三类。
- 实体完整性约束:非空值约束(NOT NULL)、默认值(DEFAULT)、唯一性约束(UNIQUE、UNIQUE(列名))、主键约束(PRIMARY KEY 、PRIMARY KEY(列名))。
- 参照完整性约束:外键约束(REFERENCES 表名(列)、FOREIGN KEY(列名) REFERENCES 表名(列))。
- 自定义完整性约束:约束表达式(CHECK)。
其中,非空值约束和默认值只有列级完整性约束;唯一性约束、主键约束、外键约束、CHECK 有表级和列级完整性约束。
4.1 主键 ( Primary Key ) 约束
完整性约束条件作用的对象有关系、元祖、列三种,又分静态和动态,共分为六类:
在关系中只能有一个主键。声明主键有两种方法:
- 将 PRIMARY KEY 保留字加在属性类型之后(列级完整性约束)。
- 在属性列表中引入新元素,该元素包含保留字 PRMARY KEY和利用圆括号括起形成该键的属性或属性组列表(表级完整性约束)。
当主键有多个属性时必须用方法2,表级完整性约束。
示例5. 学生关系Students(Sno,Sname,Sex,Sdept,Sage)的主键是Sno,在创建学生关系时可使用PRIMARYKEY进行实体完整性约束。创建学生表的SQL语句如下:
CREATE TABLE Students
(
Sno CHAR(8)
,Sname CHAR(10)
,Sex CHAR(1)
,Sdept CHAR(20)
,Sage NUMBER(3)
,PRIMARY KEY(Sno)
);
4.2 外键(Foreign Key)约束
参照完整性定义语法如下:
FOREIGN KEY (属性名) REFERENCES 表名(属性名)
[ON DELETE [CASCADE|SET NULL]
关键字说明:FOREIGN KEY 定义哪些列为外码;REFERENCES 指明外码对应于哪个表的主码;ON DELETE CASCADE
指明删除被参照关系的元组时,同时删除参照关系中的元组;SET NULL 表示置为空值方式。
示例6. 对于示例5学生选课关系SC(Sno, Cno, Grade)中,学号Sno 参照关系Students, 课程号Cno 参照关系C。因此完整的语句为:
CREATE TABLE SC
(
Sno CHAR(8)
,Cno CHAR(4)
,Grade NUMBER(3)
,PRIMARY KEY(Sno)
,PRIMARY KEY(Cno)
,FOREIGN KEY Sno REFERENCES Students(Sno)
,FOREIGN KEY Cno REFERENCES C(Cno)
);
4.3 属性值上的约束
属性值上的约束可以通过 NOT NULL、UNIQUE 和 CHECK 进行:
- NOT NULL:在 SQL 中,NULL 值是所有域的成员,也是每个属性默认的合法值。但是,根据用户要求有些属性不允许取空值,此时可用“NOT NULL”进行约束。例如,银行的账户关系Account(Account-no, branch-name, balance)不允许余额balance取空值,此时可用“balance numeric(12,2) not null” 进行约束,即禁止在该属性上插入一个空值。
- UNIQUE:唯一标识数据库表中的每条记录。
- CHECK:CHECK 子句可用于保证属性值满足指定的条件,条件与 where 类似。例如,银行关系 Branch (branch-name,branch-city,assets) 要求资产 assets 不能为负值 ,此时可用“CHECK (assets >=0)”进行约束。
示例7. 学生关系Students(Sno,Sname,Sex,Sdept,Sage),假设用户要求学生姓名不能为空,男生的年龄为1525岁,女生的年龄为1523岁。那么可使用如下语句创建表:
CREATE TABLE Students
(
Sno CHAR(8)
,Sname CHAR(10) NOT NULL
,Sex CHAR(1)
,Sdept CHAR(20)
,Sage NUMBER(3)
,PRIMARY KEY(Sno)
,CHECK (Sage >= 15
AND ((Sex = 'M' AND Sage <= 25)
OR (Sex = 'F' AND Sage <= 23))
);
4.4 全局约束
全局约束是指一些比较复杂的完整性约束,这些约束涉及多个属性间的联系或多个不同关系间的联系。
基于元组的检查子句和断言两种情况:
(1)基于元组的检查子句:这种约束是对单个关系的元组值加以约束。方法是在关系定义中的任何地方加上关键字 CHECK 和约束条件。
例如,年龄在16至20岁之间,可用 CHECK(Sage>=16 AND Sage<=20) 检测。
(2)基于断言的语法格式
格式
CREATE ASSERTION <断言名> CHECK(<条件>)
示例8. 教学数据库的模式Students、SC、C中创建一个约束ASSE-SC1:不允许男同学选修“张勇”老师的课。
CREATE ASSERTION ASSE_SC1
CHECK (NOT EXISTS
(SELECT *
FROM SC
WHERE Cno IN (SELECT Cno
FROM C
WHERE TEACHER = '张勇')
AND Sno IN (SELECT Sno
FROM Students
WHERE SEX = 'M')));
示例9. 教学数据库的模式Students、SC、C中创建 一个约束ASSE_SC2;每门课最多允许50名男同学选修。
CREATE ASSERTION ASSE_SC2
CHECK (50>=ALL(SELECT COUNT(SC.Sno)
FROM Students,SC
WHERE Students.Sno = SC.Sno
AND SEX = 'M'
GROUP BY Cno));
5. 数据操作
SQL 的数据操作包括 SELECT(查询)、INSERT(插入)、DELETE(删除)和 UPDATE(修改)四条语句。
5.1 SELECT 基本结构
数据库查询是数据库的核心操作,语法格式如下:
SELECT [ALL|DISTINCT] <目标列表达式>[,<目标列表达式>]...
FROM <表名或视图名>[,<表名或视图名>]
[WHERE <条件表达式>]
[GROUP BY <列名> [HAVING<条件表达式>]]
[ORDER BY <列名>[ASC|DESC]...]
SQL 查询中的子句顺序为SELECT、FROM、WHERE、GROUP BY、HAVING 和 ORDER BY。其中,SELECT、FROM 是必须的,HAVING 条件子句只能与 GROUP BY 搭配起来使用。
(1)SELECT 子句对应的是关系代数中的投影运算,用来列出查询结果中的属性。其输出可以是列名、表达式、函数集(AVG、COUNT、MAX、MIN、SUM),DISTINCT 选项可以保证查询的结果集不存在重复元组。
(2)FROM 子句对应的是关系代数中的笛卡尔积,它列出的表达式求值过程中需扫描的关系,即在 FROM 子句中出现多个基本表或视图时,系统首先执行笛卡尔积操作。
(3)WHERE 子句对应的是关系代数中的选择谓词。WHERE 子句的条件表达式可以使用的运算符如下:
5.2 简单查询
SQL 最简单的查询是找出关系中满足特定条件的元组,这些查询与关系代数中的选择操作类似,通常只需要使用三个关键字 SELECT、FROM 和 WHERE。
示例10. 查询所有员工信息;查询员工号为1088的员工的姓名和参加工作时间。
SELECT *
FROM 员工;
SELECT 姓名,参加工作时间
FROM 员工
WHERE 员工号 = 1088;
5.3 连接查询
如果查询涉及两个以上的表,则称为连接查询。
示例11. 关系模式:员工(员工号,姓名,性别,参加工作时间,部门号);部门(部门号,名称,电话,负责人);
# 查询部门名为“企业信息部”,且在2010年1月1日后入职的员工工号、姓名和性别
SELECT 员工号,姓名,性别
FROM 员工,部门
WHERE 员工.部门号 = 部门.部门号
AND 部门.名称 = '企业信息部'
AND 参加工作时间 > '2010-01-01';
连接查询相同字段需要带上表名点前缀,字符类型,日期类型的数据需要使用单引号。
5.4 子查询
子查询也称嵌套查询。嵌套查询是指一个 SELECT-FROM-WHERE 查询块可以嵌入另一个查询块之中。在 SQL 中允许多重嵌套。
示例12. 查询部门名为“企业信息部和行政部”,所有员工的工号、姓名和性别。
SELECT 员工号,姓名,性别
FROM 员工
WHERE 部门号 IN (SELECT 部门号
FROM 部门
WHERE 名称 IN ('企业信息部','行政部'));
5.5 聚集函数和更名操作
聚集函数是一个值的集合输入,返回单个值的函数。
SQL提供了5个预定义集函数:平均值AVG、最小值MIN、最大值MAX、求和SUM以及计数COUNT;
使用ANY和ALL谓词必须同时使用比较运算符,其含义及等价的转换关系如下:
使用聚合函数实现子查询比直接用 ALL 或 ANY 查询效率要高。
更名操作:SQL提供可为关系和属性重新命名的机制,可以使用 AS 来操作。as 子句可以出现在SELECT 子句中,也可出现在FROM子句中
示例13. 查询课程C1的最高分和最低分以及高低分之间的差距。
SELECT MAX(Grade) AS MAX_Grade
,MIN(Grade) AS MIN_Grade
,MAX(Grade)-MIN(Grade) AS GAP
FROM SC AS X
WHERE Cno='C1';
示例14. 查询其他系比计算机系CS所有学生年龄都要小的学生姓名及年龄。
## 方法一
SELECT Sname,Sage
FROM S
WHERE Sage < ALL(SELECT Sage
FROM S
WHERE SD='CS')
AND SD <> 'CS';
## 方法二
SELECT Sname,Sage
FROM S
WHERE Sage < (SELECT MIN(Sage)
FROM S
WHERE SD = 'CS')
AND SD <> 'CS';
5.6 分组查询
在 WHERE 子句后面加上 GROUP BY 子句可以对元组进行分组,关键字 GROUP BY 后面跟着一个分组属性列表。如元组在分组后需要过滤,可以在后面加 HAVING 子句即可。
当元组含有空值时,应该记住以下两点:
(1)空值在任何聚集操作中都会被忽视。它对求和、求平均值和计数都没有影响。它也不能是某列的最大值或最小值。例如,COUNT(*)
是某个关系中所有元组数目之和,但 COUNT(A) 却是A属性非空的元组个数之和。
(2)NULL 值又可以在分组属性中看作是一个一般的值。例如,SELECT A,AVG(B) FORM R 中,当A的属性值为空时,就会统计A=NULL的所有元组中B的均值。
示例15. 供应商数据库中的S、P、J、SPJ关系,查询某工程至少用了三家供应商(包含三家)供应的零件的平均数量,并按工程号的降序排列
SELECT Jno,AVG(QTY)
FROM SPJ
GROUP BY Jno
HAVING COUNT(DISTINCT(Sno)) > 2
ORDER BY Jno DESC
5.7 字符串操作
谓词 LIKE 可以用来进行字符串匹配,通常也称模糊查询,语法格式如下:
[NOT] LIKE '<匹配串>'[ESCAPE'<换码字符>']
可以使用通配符%
和_
,其中:%
匹配任意字符串;_
匹配任意一个字符。例如,_ _
匹配只含两个字符的字符串;_ _ %
匹配至少包含两个字符的字符串。
NULL 操作,通常使用 IS NULL、IS NOT NULL。
示例16. 学生关系模式为(Sno,Sname,Sex,SD,Sage,Addr),其中,Sno为学号,Same为姓名,Sex为性别,SD为所在系,Sage为年龄,Addr为家庭地址。请查询:
(1) 家庭地址包含“科技路”的学生姓名。
(2)名字为“晓军”的学生姓名、年龄和所在系。
# (1)
SELECT Sname FROM S WHERE Addr LIKE '%科技路%';
# (2)
SELECT Sname,Sage,SD FROM S WHERE Sname LIKE '_ _晓军';
为了使模式中包含特殊模式字符(即%和_),在SQL中允许使用 ESCAPE 关键词来定义转义符。转义字符紧靠着特殊字符,并放在它的前面,表示该特殊字符被当成普通字符。例如,在LIKE比较中使用ESCAPE关键词来定义转义符,例如使用反斜杠\
作转义符。
LIKE 'ab\%cd%'ESCAPE '\',匹配所有以阿宝%cd开头的字符串。
LIKE 'ab\\cd%'ESCAPE '\',匹配所有以ab\cd开头的字符串。
5.8 集合操作
在关系代数中可以用集合的并、交和差来组合关系。SQL也提供了对应的操作,但是查询的结果必须具有相同的属性和类型列表。保留字UNION、INTERSECT和EXCEPT分别对应 ∪ 、 ∩ \cup、\cap ∪、∩和—。保留字用于两个查询时,应该将每个查询分别用括号括起来。
UNION 运算
示例17. 假设查询所有客户的集合的语句1为:SELECT Customer-no FROM depositor;查询有贷款客户的集合的语句2为:SELECT Customer-no FROM borrower。查询在银行有账户、有贷款或两者都有的所有客户身份证号。
# 1和2 取并集,使用 UNION 运算会自动去除重复
SELECT Customer-no FROM depositor
UNION
SELECT Customer-no FROM borrower;
# 1和2 取并集,使用 UNION ALL 运算会保留重复
SELECT Customer-no FROM depositor
UNION ALL
SELECT Customer-no FROM borrower;
INTERSECT 运算
示例18. 学生关系模式为Students (Name, Sno, SEX, SD, Type, Address),教师关系模式为Teachers (Name, Eno, SEX, Salary, Address),查询既是女研究生,又是教师且工资大于等于2600元的名字和地址。
(SELECT Name, Address
FROM Students
WHERE SEX='女'
AND rype='研究生')
INTERSECT
(SELECT Name, Address
FROM Teachers
WHERE Salary>=2600);
EXCEPT 运算
示例19.查询不是教师的学生姓名。
(SELECT Name, Address FROM Students)
ЕХСЕРТ
(SELECT Name, Address FROM Teachers);
5.9 视图查询和更新
查询视图表时,系统先从数据字典中取出该视图的定义,然后将定义中的查询语句和对该视图的查询语句结合起来,形成一个修正的查询语句。
# 创建视图
CREATE VIEW V_ITEmp
AS SELECT 员工号,姓名,性别
FROM 员工,部门
WHERE 员工.部门号 = 部门.部门号
AND 部门.名称 = 'IT部';
# 查询IT部性别为‘女’员工的姓名工号
SELECT 员工号,姓名
FROm V_ITEmp
WHERE 性别 = '女';
视图更新
SQL 对视图更新必须遵循以下规则:
(1) 从多个基本表通过连结操作导出的视图不允许更新。
(2) 对使用了分组、聚集函数操作的视图则不允许进行更新操作。
(3) 如果视图是从单个基本表通过投影、选取操作导出的则允许进行更新操作,且语法同基本表。
5.10 WITH 子句
With 子句是在SQL99中引入的,目前只有部分数据库支持这一子句。如果我们将一个复杂查询分解成一些小视图,然后将它们组合起来,就像将一个程序按其任务分解成一些过程一样,使得复杂查询的编写和理解都会简单得多。
示例20. 假定教师关系模式为Teachers(TName,Eno,Tdept,SEX,Salary,Address),利用With子句查询工资最高的教师姓名。此时,如果具有同样工资最高的教师有多个,他们都会被选择。
with max-Salary (value) AS (SELECT max (Salary)
FROM Teachers)
SELECT Tname
FROM Teachers, max-Salary
WHERE Teachers.Salary = max-Salary.value;
5.11 其他语句
插入语句语法如下:
INSERT INTO 表名(列名[,...n])
VALUES (常量[,...n]);
INSERT INTO 表名(列名[,...n])
SELECT 查询语句;
删除语句语法如下:
DELETE
FROM 表名
WHERE 条件表达式;
更新语句语法如下:
UPDATE 表名
SET 列名=值表达式(,列名=值表达式)
[WHERE 条件表达式];
6. 数据控制
数据控制是控制用户对数据的操作和存储权力,是由 DBA 来决定的。DBMS 数据控制应具有如下功能:
(1)通过 GRANT 和 REVOKE 将授权通知系统,并存入数据字典。
(2)当用户提出请求时,根据授权情况检查是否执行操作请求。
SQL标准包括 DELETE、INSERT、SELECT 和 UPDATE 权限。SELECT 权限对应于 READ 权限,SQL 还包括了 REFERENCES 权限,用来限制用户在创建关系时定义外码的能力。
6.1 授权(GRANT)
授权语法格式:
GRANT <权限>[,<权限>]...
[ON<对象类型><对象名>]
TO <用户>[,<用户>]...
[WITH GRANT OPTION];
注意:若指定了 WITH GRANT OPTION
子句,那么获得了权限的用户还可以将权限赋给其他用户;接受权限的用户可以是单个或多个具体的用户,PUBLIC 参数可将权限赋给全体用户。不同类型的操作对象有不同的操作权限,常见的操作权限如下所示:
示例21. 如果用户要求把数据库SPJ中供应商S、零件P、项目J表赋予各种权限。各种授权要求如下:
(1) 将对供应商S、零件P、项目J的所有操作权限赋给用户 USER1 及 USER2。
(2) 将对供应商S的插入权限赋给用户 USER1,并允许将此权限赋给其他用户。
(3) DBA 把数据库 SPJ 中建立表的权限赋给用户 USER1。
参考答案:
(1)GRANT ALL PRIVILEGES ON TABLE S,P,J TO USER1,USER2;
(2)GRANT INSERT ON TABLE S TO USER1 WITH GRANT OPTION;
(3)GRANT CREATE TABLE ON DATABASE SPJ TO USER1;
6.2 销权(REVOKE)
销权语法格式:
REVOKE <权限>[,<权限>]...
[ON<对象类型><对象名>]
FROM <用户>[,<用户>]...
[RESTRICT|CASCADE];
其中:RESTRICT 表示只收回指定用户的权限,默认;CASCADE 表示收回指定用户及其授予的其他用户的该权限。
示例22. 要求回收用户对数据库SPJ中供应商S、零件P、 项目J表的操作权限。各种收回权限的要求如下:
(1) 将用户USER1 及USER2对供应商S、零件P、项目J的所有操作权限收回。
(2) 将所有用户对供应商S的所有查询权限收回 。
(3)将USER1 用户对供应商S的供应商编号Sno的修改权限收回。
参考答案:
(1) REVOKE AL PRIVILEGES ON TABLE S,P,J FROM USER1, USER2;
(2) REVOKE SELECT ON TABLE S FROM PUBLIC;
(3) REVOKE UPDATE(Sno) ON TABLE S FROM USER1;
示例23. 收回用户LI对表EMPLOYEE的查询权限,同时级联收回LI授予其他用户的该权限,SQL语句为: (1) SELECT ON TABLE EMPLOYEE FROM LI (2) ;
(1) A.GRANT B.GIVE C.CALL BACK D. REVOKE
(2) A.RESTRICT B.CASCADE C.WITH GRANT OPTION D. WITH CHECK OPTION
答案:(1)的正确选项为D,(2)的正确选项为B。
7. 存储过程和触发器
存储过程,类似于高阶语言的函数或者方法,包含SQL语句序列,是可复用的语句,保存在数据库中,在服务器中执行。
特点:复用,提高了效率,安全性。
触发器是一种特殊类型的存储过程,是通过事件触发而执行的,而存储过程可以通过存储过程名称而被直接调用。
触发器主要有如下特点:
(1) 当数据库程序员声明的事件发生时,触发器被激活。事件可以是对某个特定关系的插入(INSERT)、删除(DELETE)或更新(UPDATE)。
(2)当触发器被事件激活时,不是立即执行,而是首先由触发器测试触发条件,若条件不成立,响应该事件的触发器什么事情都不做。
(3)如果触发器声明的条件满足,则与该触发器相连的动作由DBMS执行。动作可以阻止事件发生,可以撤销事件。
触发器数据库对象,当创建一个触发器时必须指定:1> 名称;2> 在其上定义触发器的表;3> 触发器将何时激发;4> 指明触发器执行时应做的动作。其名称必须遵循标识符的命名规则,数据库像存储普通数据那样存储触发器。触发器可以引用当前数据库以外的对象,但只能在当前数据库中创建触发器。尽管不能在临时表或系统表上创建触发器,但是触发器可以引用临时表。
触发动作实际上是一系列SQL语句,可以有两种方式:
(1) 对被事件影响的每一行(FOR EACH ROW),每一元组执行触发过程,称为行级触发器。
(2) 对整个事件只执行一次触发过程(FOR EACH STATEMENT),称为语句级触发器。该方式是触发器的默认方式。
7.1 创建触发器
触发器主要包括两个方面:指明触发器的触发事件,指明触发器执行的动作。
触发事件包括表中行的插入、删除和修改,即执行 INSERT、DELETE、UPDATE 语句。 在修改操作 (UPDATE)中,还可以指定特定的属性或属性组的修改为触发条件。事件的触发还有两个相关的时间:BEFORE 和 AFTER。BEFORE 触发器是在事件发生之前触发,AFTER 触发器是在事件发生之后触发。创建触发器语句格式如下:
CREATE TRIGGER <触发器名> [{BEFORE|AFTER}]
{[DELETE|INSERT|UPDATE OF[列名清单]]}
ON
[REFERENCING <临时视图名>]
[FOR EACH ROW|FOR EACH STATEMENT]
[WHEN <触发条件>]
BEGIN
<触发动作>
END [触发器名];
参数说明:
- BEFORE:指示 DBMS 在执行触发语句之前激发触发器。
- AFTER:指示 DBMS在执行触发语句之后激发触发器。
- DELETE:指明是 DELETE 触发器,每当一个DELETE 语句从表中删除一行时激发触发器。
- INSERT:指明是 INSERT 触发器,每当一个INSERT语句向表中插入一行时激发触发器
- UPDATE:指明是 UPDATE 触发器,每当 UPDATE 语句修改由 OF 子句指定的列值时,激发触发器。如果忽略 OF 子句,每当 UDPATE 语句修改表的任何列值时,DBMS 都将激发触发器。
- REFERENCING <临时视图名>:指定临时视图的别名 。在触发器运行过程中,系统会生成两个临时视图,分别存放被更新值(旧值)和更新后的值(新值)。对于行级触发器, 默认临时视图名分别是 OLD 和 NEW;对于语句级触发器,默认临时视图名分别是 OLD-TABLE 和 NEW-TABLE。一旦触发器运行结束,临时视图就不在。
- WHEN <触发条件>:指定触发器的触发条件。当满足触发条件时,DBMS 才激发触发器。触发条件中必须包含临时视图名,不包含查询。
示例24. 银行数据库关系模式如下:
Account (Account-no, branch-name, balance)
Loan (Loan-no, branch-name, amount)
Depositor (customer-name, Account-no)
账户关系模式 Account 中的属性 Account-no 表示账号 ,branch-name 表示支行名称,balance 表示余额。贷款关系模式 Loan 中的属性 Loan-no 表示贷款号,branch-name 表示支行名称,amount 表示金额。存款关系模式 Depositor 中的属性 customer-name 表示存款人姓名。SQL-99 创建触发器如下所示:
CREATE TRIGGER overdraft_trigger AFTER UPDATE ON Account
REFERENCING NEW ROW AS nrow
FOR EACH ROW
WHEN nrow.balance < 0
BEGIN ATOMIC
INSERT INTO borrower
(SELECT customer-name,Account-no
FROM Depositor
WHERE nrow.account-no = Depositor.account-no);
INSERT INTO loan VALUES (nrow.account-no,branch-name,-nrow.balance);
UPDATE account SET balance = 0
WHERE account.account-no = nrow.account-no;
END;
When语句指定一个条件nrow.balance<0。仅对满足条件的元组才会执行余下的触发器;BEGIN ATOMIC 子句用来将多行SQL语句集成为一个复合语句,该子句中的两条 INSERT INTO 语句执行了在borrower和loan关系中建立新的贷款业务;UPDATE语句用来将账户余额清零;
示例25. 仓库管理数据库中有如下关系,请创建一个重新订购商品的触发器。
inventory(item,level) :表示某种商品在仓库中的现有量
minlevel(item,level) :表示某种商品在仓库中存有的最小量
reorder(item,amount) :表示某种商品小于最小量的时候要订购的数量
orders(item,amount):表示某种商品被定购的量
CREATE TRIGGER reorder_trigger AFTER UPDATE OF amount on inventory
REFERENCING OLD ROW AS orow,NEW ROW AS nrow
FOR EACH ROW
WHEN nrow.level <= (SELECT level
FROM minlevel
WHERE minlevel.item = orow.item)
AND orow.level > (SELECT level
FROM minlevel
WHERE minlevel.item = orow.item)
BEGIN
INSERT INTO orders
(SELECT item,amount
FROM reorder
WHERE reorder.item = orow.item)
END;
示例26. 若修改某商品的库存时,使得库存值小于或等于其最小库存值,则向采购表插入一条记录,要求采购的数量是该商品最小库存值的两倍再加上10。
CREATE TRIGGER 采购-trigger AFTER UPDATE ON 商品 [AFTER UPDATE OF 库存 ON 商品]
REFERENCING NEW ROW AS nrow
FOR EACH ROW
WHEN nrow.库存<=nrow.最小库存
BEGIN
INSERT INTO 采购(商品号,采购数量)
VALUES(nrow.商品号,nrow.最小库存*2+10)
END;
7.2 更改和删除触发器
使用系统命令 ALTER TRIGGER 更改指定的触发器的定义,语法如下:
ALTER TRIGGER <触发器> [{BEFORE|AFTER}] {[DELETE|INSERT|UPDATE OF [列名清单]]}
ON 表名|视图名
AS
BEGIN
SQL STATEMENTS
END;
使用 DROP TRIGGER <触发器>[,…n]
,其中,n 表示可以指定多个触发器的占位符。
8. 嵌入式 SQL 和动态 SQL
8.1 嵌入式 SQL
SQL 提供了将 SQL 语句嵌入某种高级语言中的使用方式,但是如何识别嵌入在高级语言中的 SQL 语句,通常采用预编译的方法。该方法的关键问题是必须区分主语言中嵌入的 SQL语句,以及主语言和 SQL 间的通信问题。采用的方法由 DBMS 的预处理程序对源程序进行扫描,识别出 SQL 语句,把它们转换为主语言调用语句,以使主语言编译程序能识别它,最后由主语言的编译程序将整个源程序编译成目标码。
如何区分主语言语句与 SQL 语句?
需要在所有的 SQL 语句前加前缀 EXEC SQL
,而 SQL 的结束标志随主语言的不同而不同。
- PL/1 和 C 语言的引用格式为:
EXEC SQL <SQL语句>
; - COBOL 语言的引用格式为:
EXEC SQL <SQL 语句> END-EXEC
;
主语言与数据库如何通信?
(1)SQL 通信区 (SQL Communication Area,SQLCA) 向主语言传递 SQL 语句执行的状态信息,使主语言能够根据此信息控制程序流程。
(2)主变量也称共享变量。主语言向 SQL 语句提供参数主要通过主变量,主变量由主语言的程序定义,并用 SQL 的 DECLARE 语句说明,引用变量一定要加上:
。
EXEC SQL BEGIN DECLARE SECTION; /*说明主变量*/
char Msno[4],Mcno[3],Givensno[5];
int Mgrade;
char SQLSTATE[6];
EXEC SQL END DECLARE SECTION;
分析:以上说明了五个共享变量,其中,SQLSTATE 是一个特殊的共享变量,解释 SQL 语句执行状况的作用。当 SQL 语句执行成功时,系统自动给 SQLSTATE 赋值上全零值,否则为非全零(”02000”)。因此,当执行一条SQL语句后,可以根据 SQLSTATE 的值转向不同的分支,以控制程序的流向。引用时,为了与 SQL 属性名相区别,需在主变量前加:
。
示例27. 根据共享变量 givensno 值查询学生关系students 中学生的姓名、年龄和性别。
EXEC SQL SELECT sname,age,sex
INTO :Msno,Mcno,:givensno
FROM students
WHERE sno=:Msno;
示例28. 某学生选修了一门课程信息,将其插入学生选课表SC中,假设学号、课程号、成绩已分别赋给主变量HSno、Hcno和Hgrade。
EXEC SQL INSERT
INTO SC(Sno,Cno,Grade)
VALUES(:Hsno,:Hcno,:Hgrade);
(3)游标。SQL 语言是面向集合的,一条 SQL 语句可产生或处理多条记录。而主语言是面向记录的,一组主变量一次只能放一条记录,所以,引入游标,通过移动游标指针来决定获取哪一条记录。
- 定义游标:说明性语句,定义中的 SELECT 语句并不立即执行。语法如下:
EXEC SQL DECLARE <游标名> CURSOR FOR
<SELECT 语句>
- 打开游标:执行游标定义中的 SELECT 语句,同时游标处于活动状态,游标是一个指针,此时指向查询结果的第一行之前。语法如下:
EXEC SQL OPEN <游标名>
- 推进游标:使用时,游标会推进一行,并把指针指向的行(称为当前行)中的值取出,送到共享变量中。变量表由逗号分开的共享变量组成。该语句经常用于宿主程序的循环结构中,并借助宿主语言的处理语句逐一处理查询结果中的一个元组。语法如下:
EXEC SQL FETCH FROM <游标名> INTO <变量表>
- 关闭游标:使用时,关闭游标,不再和查询结果相联系。关闭了的游标,可以再次打开,与新的查询结果相联系。在游标处于活动状态时,可以修改和删除游标指向的元组。语法如下:
EXEC SQL CLOSE <游标名>
示例29. 在C语言中嵌入SQL的查询,检索某学生的学习成绩,其学号由共享主变量givensno给出,结果放在主变量Sno,Cno,Grade中。如果成绩不及格,则删除该记录,如果成绩为60~69分,则将成绩修改为70分,并显示学生的成绩信息(除60分以下的)
#DEFINE NO_MORE_TUPLES !(strcmp(SQLSTATE,"02000"))
void sel()
{EXEC SQL BEGIN DECLARE SECTION; /*说明主变量*/
char Msno[4],Mcno[3],givensno[5];
int Mgrade;
char SQLSTATE[6];
EXEC SQL END DECLARE SECTION;
EXEC SQL DECLARE Scx CURSOR FOR /* 说明游标Scx,将查询结果与Scx建立联系*/
SELECT Sno,Cno,Grade
FROM SC
WHERE Sno=:givensno;
EXEC SQL OPEN Scx;
While(1) /* 用循环结构逐条处理结果集中的记录*/
{EXEC SQL FETCH FROM Scx /* 游标推进一行*/
INTO :Msno,:Mcno,:Mgrade; /* 送入主变量,注意每个变量要加分号;*/
if (NO_MORE_TUPLES) Break; /* 处理完退出循环*/
print("%s,%s,%d\n",sno,cno,g);
};
if (Mgrade<60) /* 成绩<60*/
EXEC SQL DELETE FROM SC WHERE CURRENT OF Scx;
Else
{if (Mgrade < 70) /* 成绩<70*/
EXEC SQL UPDATE SC SET grade = 70 WHERE CURRENT OF Scx;
MGrade = 70
}
Printf("%s,%s,%d",Msno,Mcno,Mgrade); /*显示学生记录*/
EXEC SQL CLOSE Scx; /* 关闭游标*/
};
8.2 动态 SQL
动态 SQL 允许程序在运行时构造、提交 SQL 查询。使用动态 SQL 语句时,还可以改进技术;当预备语句组合而成的 SQL 语句只需执行一次, 那么预备语句可以在程序运行时由用户输入才组合起来,但并不执行。
动态 SQL 预备语句格式
EXEC SQL PREPARE <动态SQL语句名> FROM <共享变量或字符串>;
动态 SQL 执行语句格式
EXEC SQL EXECUTE <动态SQL语句名>;
示例30. 动态 SQL 示例
char*query = "UPDATE sc SET Grade = Grade*1.1 WHERE Cno = ?";
EXEC SQL PREPARE dynprog FROM :query;
char cno[5] = "C4";
EXEC SQL EXECUTE dynprog USING :cno;
9. SQL-99 所支持的对象关系模型
对象- 关系数据模型扩展关系数据模型的方式是通过提供一个包括复杂数据类型和面向对象的更丰富的类型系统。
9.1 嵌套关系
嵌套关系模型(Nested Relational Model)是关系模型的一个扩展,域可以是原子的也可以赋值为关系。这样元组在一个属性上的取值可以是一个关系,于是关系可以存储在关系中。从而一个复杂对象就可以用嵌套关系的单个元组来表示。如果我们将嵌套关系的一个元组视为一个数据项,在数据项和用户数据库观念上的对象之间就有了一个一一对应的关系。
图书馆示例,每本书存储有书名、作者集合、出版商、关键字集合四种属性。如果这些信息定一个关系,下面的属性将是非原子的:
1)作者。一本书可能有一组作者。
2)关键字。一本书存储了一组关键字,我们希望能够检索出关键字包含该集合中的一个或多个关键字的所有书,这样就将关键字集合域视为非原子的。
3)出版商。与关键字和作者不同,出版社没有一个以集合为值的域。但是,我们可能将出版商视为由名字和分支机构这两个子字段组成的,使得出版商域成为非原子的。
定义一个示例关系 books,该关系是1NF, 如下:
假如需要访问单个作者和单个关键字,可以对 books 关系进行拆解,得到 flat-books 关系1NF如下:
如果 title→→author、title→→keyword、title→pub-name, pub-branch 是多值依赖,我们可以进一步把这个关系拆解成4NF,使得 flat-books 关系的冗余去除。拆解后关系和示例如下:
authors(title, author)
keywords(title, keyword)
books4(title, pub-name, pub-branch)
4NF 设计要求用户她们的查询包含连接操作,因此使得交互复杂化。
面向对象数据库系统支持面向对象数据模型,是一个持久的、可共享的对象库的存储和管理者,而一个对象库是由一个OO模型所定义的对象的集合体。
对象:是由一组数据结构和在这组数据结构上的操作的程序代码封装起来的基本单位。对象之间的界面由 一组消息定义。 一个对象包括属性集合、方法集合和消息集合。
对象标识:是指面向对象数据库中的每个对象都有一个唯一不变的标识。常用的几种标识有值标识、名标识和内标识。
- 值标识,使用一个值来标识,在关系数据库中通常使用这种形式的标识。例如,一个元组的主码标识了这个元组。
- 名标识,用用户提供的名称作为标识。这种形式的标识通常用于文件系统中的文件,不管文件的内容是什么,每个文件都被赋予一个名称来唯一标识。
- 内标识,是建立在数据模型或程序设计语言中内置的一种标识,不需要用户给出标识。面向对象系统中使用这种形式的标识,每个对象在创建时被系统自动赋予一个标识符。
封装:OO模型的一个关键概念就是封装。每一个对象是其状态和行为的封装。封装是对象的外部界面与内部实现之间实行清晰隔离的一种抽象,外部与对象的通信只能通过消息。
类:共享同样属性和方法集的所有对象构成了一个对象类(简称类)。例如,学生是一个类,黎明、张军、樊建喜是学生类中的一个对象。类是“型”,对象是“值”。
9.2 复杂类型
集合类型
集合是集合体类型(collection type)的一个实例,其他的集合体类型包括数组(array)和多重集合(multiset)(即无序的集合体,其中一个元素可以出现多次)。
SQL-99还提供了新字符型数据大对象数据类型(clob)和二进制数据大对象数据类型(blob)。
create table books(
keyword-set setof(varchar(2))
,author-array varchar (20) array [10]
,book-review clob (10KB)
,image blob (10MB)
,movie blob (2GB)
);
复杂类型值的创建
在SQL-99中构造器函数(constructor function)用来创建结构类型的值。与结构类型同名的函数就是这个结构类型的构造器函数。
示例20. 给Publisher类型声明一个构造器。
create function Publisher (n varchar(20),b varchar(20))
returns Publisher
begin
set name=n;
set branch=b;
end
然后我们可以用 Publisher(McGraw-Hill,NewYork) 来创建Publisher 类型的值。
9.3 继承
继承可以在类型的级别上进行,也可以在表级别上进行。
类型继承
如希望在数据库中对那些是学生和教师的人分别存储一些额外的信息。
示例31. 假定人的类型定义如下所示,定义学生和教师类型。由于学生和教师是人,所以可以使用继承。
create type Person(
name varchar(20)
,address varchar(20)
);
create type Student
under Person
(degree varchar(20),
department varchar(20));
create type Teacher
under Person
(salary integer,
department varchar(20))
Student 和Teacher 都继承了Person 的属性,即name和address。Student和Teacher被称为Person 的子类型,Person 是Student 的超类型,同时也是Teacher 的超类型。像属性一样,结构 类型的方法也被它的子类型继承。不过,子类型可以通过在 一个方法声明中使用overriding
method (重载方法)取代原method (方法)的方式重新声明方法,以重定义该方法的作用。
表继承
SQL-99中的子表(subtable)对应的是E-R概念中的特殊化/一般化。子表的类型必须是父表类型的子类型,因此,父表中的每一个属性均出现在子表中
create table people of Person;
create table students of Student under people;
create table teachers of Teacher under people;
当我们声明students 和teachers 作为people的子表时,每一个students 或teachers 中出现的 元组也隐式存在于peopl e中。如果 一个查询用到people 表,它将查找的不仅仅是直接插入到这 个表中的元组,而且还包含插入到它的子表(也就是students 和teachers)中的元组。
9.4 引用类型
面向对象的程序设计语言提供了引用对象的能力,类型的一个属性可以是对一个指定类型的对象的引用。
示例21. 定义一个包括name字段和head字段的Department类型,一个Department类型的表departments。其中,head字段引用到Person类型。方法如下所示
create type Department(
name varchar(20),
head ref(Person) scope people);
create table departments of Department;
这里,引用限制在people 表中的元组。在SQL-99中,对一个指向表的元组的引用范围 (scope) 的限制是强制的,它使引用的行为与外码类似。
9.5 与复杂类型有关的查询
路径表达式
在SQL-99中对引用取内容使用→符号。
# 查找各部门负责人的名字和地址
select head->name,head->address
from departments;
head->name
带有->符号的表达式被称为路径表达式。由于head是一个对people表中元组的引用,上述查询中的name属性就是people表中元组的name属性。引用可以用来隐藏连接操作,如果没有使用引用要找出一个部门负责人的姓名和地址,我们就需要将departments与people关系显式地做一个连接,因此使用引用可以明显地简化查询。
以集合体为值的属性
一个计算集合体值的表达式可以出现在关系名出现的任何地方。
# books关系模式,查询所有的码中包含“database” 字样的书
select title
from books
where 'database' in (unnest(keyword-set));
unnest(keyword-set)在无嵌套关系的SQL中相当于一个select-from-where的子表达式。
# 一本特定的书具有三个作者,拆出每个作者
select author-array[1],author-array[2],author-array[3]
from books
where title = 'Database System Concepts';
# 包含形式为“ 书名,作者名” ,对应每本书和书的每个作者
select b.title,a.name
from books as b,unnest(b.author-array) as a;
由于books的author-array属性是一个以集合体值的字段,因此可以用在需要有一个关系存在的from子句中。
嵌套与解除嵌套
将一个嵌套关系转换成具有更少(或没有)的关系为值的属性的形式的过程称为解除嵌套(unnesting)。books关系有author-array和keyword-set两个是集合体的属性;同时books关系另外还有title和publisher两个不是集合体的属性。
select title
,A as author
,publisher.name as pub-name
,publisher.branch as pub-branch
,K as keyword
from books as B,unnest(B.author-array) as A,unnest(B.keyword-set) as K;
from 子句中的变量B被声明为以books为取值范围,变量A被声明为以书B的author-array 中的作者为取值范围,同时K 被声明为以书B 的keyword-set 中的关键字为取值范围
9.6 函数和过程
SQL 函数和过程
定义一个函数,实现给定一个书名,返回作者数量:
create function author-count(title varchar(20))
returns integer
begin
declare a-count integer;
select count(author) into a-count
from authors
where authors.title = title
return a-count;
end;
以上也可以写成一个存储过程:
create procedure author-count-proc(in title varchar(20),out a-count integer)
begin
select count(author) into a-count
from authors
where authors.title = title
end;
# 应用
declare a-count integer;
call author-count-proc('Database Systems Concepts',a-count);
外部语言程序
SQL-99允许使用一种程序设计语言(如 C或C++)定义函数。这种方式定义的函数会比 SQL中定义的函数效率更高,无法在SQL中执行的计算可以由这些函数执行。
create procedure author-count-proc(in title varchar(20),out a-count integer)
language C
external name '/usr/avi/bin/author-count-proc';
create function author-count(title varchar(20))
returns integer
language C
external name '/usr/avi/bin/author-count';
过程的构造
1)while 和 repeat 循环语句
一个复合语句有begin … end的形式,在begin和end之间会包含多条SQL语句。语法如下:
declare n integer default 0;
while n<10 do
set n=n+1;
end while;
repeat
set n=n-1;
until n=0
end repeat;
2)for 循环语句
declare n integer default 0;
for r as
select balance
from account
where branc-name='Perryridge'
do
set n=n+r.balance
end for
程序在 for 循环开始执行的时候隐式地打开一个游标,并且用它每次获得一个行的值存入 for 循环变量(在上面例子中指r)中。语句leave可用来退出循环,而 iterate 表示跳过剩余语句从循环的开始进入下一个元组。
3)if-then-else 语句与 case 语句
条件语句 if-then-else 语法如下:
if r.balance<1000
then set l=l+r.balance
elseif r.balance<5000
then set m=m+r.balance
else set h=h+r.balance
end if;
10.总结
通过本文 SQL 语言的基本知识,多加实践掌握数据库数据查询 (Data Query)、数据操纵 (Data Manipulation)、数据定义 (Data Definition)、数据控制 (Data Control)等基本操作,进阶存储过程、触发器、动态 SQL 和嵌入式 SQL,并结合实际项目不断实践,你将能够熟练地处理各种数据库相关的任务,无论是简单的数据查询还是复杂的数据库设计和管理,相信不管遇到任何数据库你都可以得心应手。