三. 关系数据库标准语言SQL
3.1 SQL的基本概念
SQL:结构化查询语言,是关系数据库的标准语言
目前没有一个数据库系统可以支持SQL标准的所有概念和特性
SQL 的特点
-
综合统一
- 集数据定义语言(DDL),数据操纵语言(DML),数据控制语言(DCL)功能于一体
- 可以独立完成数据库生命周期的全部活动
-
高度非过程化
-
面向集合的操作方式
- 非关系数据模型采用面向记录的操作方式,操作对象是一条记录
- SQL采用集合操作方式
- 操作对象、查找结果可以是元组的集合
- 一次插入、删除、更新操作的对象可以是元组的集合
-
以同一种语言结构提供多种使用方式
- 是独立的语言
- 嵌入式语言
-
语言简洁、易学易用
-
SQL功能极强,完成核心功能只用到9个动词
SQL功能 动词 数据查询 SELECT 数据定义 CREATE, DROP, ALTER 数据操纵 INSERT, UPDATE, DELETE 数据控制 GRANT, REVOKE
-
SQL 支持关系数据库三级模式结构
1. 基本表(模式)
- 本身独立存在的表
- SQL中一个关系就对应一个基本表(模式)
- 一个或者(多个)基本表就对应一个存储文件(内模式)
- 一个表可以带若干索引
2. 存储文件(内模式)
- 逻辑结构组成了关系数据库的内模式
- 物理结构对用户是隐蔽的
3. 视图(外模式)
- 从一个或者几个基本表导出的表
- 数据库中只存放视图的定义而不存放视图对应的数据
- 视图是一个虚表
- 用户可以在视图上在定义视图
实例数据库
Student(Sno,Sname,Ssex,Sage,Sdept)
Course(Cno,Cname,Cpno,Ccredit)
SC(Sno,Cno,Grade)
3.2 数据定义
-
SQL 的数据定义功能:定义各种数据库对象
- 模式定义
- 表定义
- 视图定义
- 索引定义
-
一个数据库里可以建立多个模式
-
一个模式下通常包括多个表、视图和索引等数据库对象
数据字典
- 数据字典是关系数据库管理系统内部的一组系统表,它记录了数据库中所有对象的定义信息以及一些统计信息
- 关系模式、表、视图、索引的定义
- 完整性约束条件
- 各类用户对数据库的操作权限
- 统计信息等
- 关系数据库管理系统在执行SQL的数据定义语句时,实际上就是在更新数据字典表中的信息
1. 模式定义
定义模式
eg.3.1 为用户WANG定义一个学生-课程模式S-T
CREATE SCHEMA "S-T" AUTHORIZATION WANG;
eg.3.2 该语句没有定义模式名,模式名隐含为用户名
CREATE SCHEMA AUTHORIZATION WANG;
-
定义模式实际上定义了一个命名空间(或者说目录)
-
在这个空间上可以定义该模式包含的数据库对象,例如基本表,索引,视图等
-
在CREATE SCHEMA 中可以接受 CREATE TABLE(基本表),CREATE VIEW(视图),GRANT(索引) 子句
CREATE SCHEMA <模式名> AUTHORIZATION <用户名> [<表定义子句>|<视图定义子句>|<授权定义子句>]
eg.3.3 为用户WANG 创建了一个模式TEST ,并在其中定义一个表 TAB1
CREATE SCHEMA TEST AUTHORIZATION WANG
CREATE TABLE TAB1 (COL1 SMALLINT,
COL2 INT,
COL3 CHAR(20),
COL4 NUMERIC(10,3),
COL5 DECIMAL(5,2)
);
删除模式
语法
DROP SCHEMA <模式名> <CASCADE|RESTRICT>
- CASSADE(级联)
- 删除模式的同时把该模式中所有的数据库对象全部删除
- RESTRICT(限制)
- 如果该模式中定义了下属的数据库对象(如表,视图等),则拒绝该删除语句的执行
- 仅当该模式中没有任何下属对象时才能执行
eg.3.4 删除模式ZHANG 同时该模式中定义的表TAB1也被删除
DROP SCHEMA ZHANG CASCADE;
2. 表定义
定义基本表
CREATE TABLE <表名> /*基本表的名称*/
(<列名><数据类型>)[<列级完整性约束条件>] /*组成该表的列*/
[,<列名><数据类型>][<列级完整性约束条件>]]
...
[,<表级完整性约束条件>]);
- 列级完整性约束条件:涉及相应属性列的完整性约束条件
- 表级完整性约束条件:涉及一个或多个属性列的完整性约束条件
- 如果完整性约束条件涉及到该表的多个属性列,则必须定义在表级上
【eg.3.5】 建立“学生”表 Student.学号时主码,姓名值取唯一。
CREATE TABLE Student
(
Sno CHAR(9) PAIMARY KEY, /*列级完整性约束条件,Sno为主码*/
Sname CHAR(20) UNIQUE, /*Sname取唯一值*/
Ssex CHAR(2),
Sage SMALLINT,
Sdept CHAR(20)
);
【eg.3.6】建立一个”课程“表 Course
CREATE TABLE Course
(
Cno CHAR(4) PRIMARY KEY,
Cname CHAR(40),
Cpno CHAR(4),
Ccredit SMALLINT,
FOREIGN KEY(Cpno) REFERENCES Course(Cno) /*表级完整性,Cpno是外码,被参照表是Course,被参照列的是Cno*/
);
【eg.3.7】建立一个学生选课表 SC
CREATE TABLE sc
(
Sno CHAR(9),
Cno CHAR(4),
Grade SMALLINT,
PRIMARY KEY(Sno,Cno),/*主码由两个属性构成,必须作为表级完整性进行定义*/
FOREIGN KEY(Sno) REFERENCES Student(Sno),
/*表级完整性约束条件,Sno是外码,被参照表是Student*/
FOREIGN KEY(Cno) REFERENCES Course(Cno)
/*表级完整性约束条件,Cno是外码,被参照表是Course*/
);
数据类型
- 关系模式中"域"的概念用数据类型来实现
- 定义表的属性时需要指明其数据类型及长度
- 选用那种数据类型
- 取值范围
- 要做那些运算
模式与表
-
每一个基本表需属于某个模式
-
定义基本表所属模式的方式:
-
方法一:在表名中明显给出模式名
CREATE TABLE "S-T".Student(...); /* 模式名为S-T*/ CREATE TABLE "S-T".Course(...); CREATE TABLE "S-T".SC(...);
-
方法二:在创建模式的同时创建表
-
方法三:设置所属的模式
-
修改基本表
ALTER TABLE<表名>
[ADD[COLUMN]]<新列名><数据类型>[完整性约束]]
[ADD <表级完整性约束>]
[DROP[COLUMN]<列名>[CASCADE|RESTRICT]]
[DROP CONSTRAINT<完整性约束名>[RESTRICT|CASCADE]]
[ALTER COLUMN<列名><数据类型>];
-
表名是要修改的基本表
-
ADD子句用于添加新列、新的列级完整性约束条件和新的表级完整性约束条件
-
DROP COLUMN 子句用于删除表中的列
- 如果指定了CASCADE短语,则自动删除引用该列的对象
- 如果指定了RESTRICT 短语,则如果该列被其他对象引用,关系数据库管理系统将拒绝删除该列
-
DROP CONSTRAINT子句用于删除指定的完整性约束条件
-
ALTER COLUMN 子句用于修改原有的列定义包括修改列名和数控类型
【eg. 3.8】向Student表中增加“入学时间”列,其数据类型为日期型
ALTER TABLE Student ADD S_entrance DATE;
不管基本表中有无数据,新增加的列一列为空值
【eg. 3.9】将年龄的数据类型由字符型(假设原来的数据类型是字符型)改为整数;
ALTER TABLE Student ALTER COLUMN Sage INT;
【eg. 3.10】添加课程名称必须取唯一值的约束条件
ALTER TABLE Course ADD UNIQUE(Cname);
删除基本表
DROP TABLE<表名>[RESTRICT|CASCADE];
- RESTRICT: 删除表的时候是有限制的
- 欲删除的基本表不能被其他表的约束所引用
- 如果存在依赖该表的对象,则此不能被删除
- CASCADE: 删除该表没有限制
- 在删除基本表的同时,相关的依赖对象一起被删除
【eg. 3.11】删除Student表
DROP TABLE Student CASCADE;
- 基本表定义被删除,数据被删除
- 表上建立的索引,视图,触发器等一般也将会被删除
3. 索引定义
- 建立索引的目的:加快查询速度
- 关系数据库管理系统中常见索引:
- 顺序文件上的索引
- B+树索引
- 散列(hash)索引
- 位图索引
- 特点:
- B+树索引具有动态平衡的优点
- HASH索引具有查找速度快的特点
- 谁可以建立索引
- DBA或者表的属主(即建立表的人)
- 谁维护索引
- 关系DBMS自动完成
- 使用索引
- 关系DBMS自动选择合适的索引作为存取路径,用户不必也不能显示的选择索引
建立索引
-
语句格式
CREATE[UNIQUE][CLUSTER] INDEX<索引名> ON<表名>(<列名>[<次序>], [,<列名>[<次序>]]...);
- 表名:要建立索引的基本表的名字
- 索引:可以建立在该表的一列或多列上,各列名之间用逗号分开
- 次序:指定索引值的排序顺序,升序:ASC,降序:DESC,缺省值:ASC
- UNIQUE:此索引的每一个索引值只对应唯一的数据记录
- CLUSTER:表示要建立的索引是聚簇索引
【eg. 3.13】为学生-课程数据库中Student,Course,SC三个表建立索引。
Student表按照学号升序建唯一索引,
Course表按课程号升序建立唯一索引,
SC表按照学号升序和课程号降序建唯一索引。
CRATE UNIQUE INDEX Stusno ON Student(Sno);
CRATE UNIQUE INDEX Coucno ON Course(Cno);
CRATE UNIQUE INDEX SCno ON SC(Sno ASC,Cno DESC);
修改索引
-
语法格式
ALTER INDEX<旧索引> RENAME TO <新索引>;
【eg. 3.14】将SC表中的SCno索引名改为SCSno
ALTER INDEX SCno RENAME TO SCSno;
删除索引
-
语法格式
DROP INDEX <索引名>;
删除索引时,系统会从数据字典里删去有关该索引的描述
【eg. 3.15】删除Student 表中的Stusname索引
DROP INDEX Stusname;
3.3 数据查询
3.3.1 单表查询
数据查询
-
语法格式
SELECT[ALL|DISTINCT]<目标列表达式>[,<目标列>]... FROM<表名或者视图名>[,<表名或者视图名>]...|(SELECT 语句) [AS]<别名> [WHERE<条件表达式>] [GROUP BY <列名1>][HAVING<条件表达式>]] [ORDER BY <列名2>[ASC|DESC]];
子句 作用 SELECT子句 指定要现实的属性列 FROM子句 指定查询对象 WHERE子句 指定查询条件 GROUP BY 子句 对查询结果按指定列的值分组,该属性列值相等的元组为一组。通常会在每组中作用聚集函数。 HAVING 子句 只有满足指定田条件的组才予以输出 ORDER BY 子句 对查询结果表按指定列值的升序或降序排列
1.选择表中的若干列
- 查询指定列
【eg. 3.16】查询全体学生的学号和姓名
SELECT Sno,Sname
FROM Student;
- 查询全部列
- 选出所有属性列:
- 在SELECT关键词后面列出所有列名;
- 将 目标列表达式 指定为 *
- 选出所有属性列:
【eg. 3.17】查询全体学生的详细记录
SELECT Sno,Sname,Ssex,Sage,Sdept
FROM Student;
或
SELECT *
FROM Student;
- 查询经过计算的值
- “虚”列
- SELECT 子句的 目标列表达式 不仅可以为表中的属性列,也可以是表达式
- “虚”列
【eg. 3.18】查询全体学生的姓名及其出生年份
SELECT Sname,2020-Sage
FROM Student;
【eg. 3.19】查询全体学生的姓名,出生年份和所在的院系,要求小写字母表示系名
SELECT Sname,'Year of Birth:',2020-Sage,LOWER(Sdept)
FORM Student;
【eg. 3.21】使用列别名改变查询结果的列标题
SELECT Sname NAME,
'Year of Birth:' BIRTH,2020-Sage BIRTHDAY,LOWER(Sdept) DEPARTMANT
FROM Student;
2 选择表中的若干元组
(1).
- 取消选择重复的行
如果没有指定DISTINCT关键词,则缺省值为ALL(不去重)
【eg. 3.21】查询选修了课程的学生学号
SELECT Sno
FROM SC;
- 指定DISTINCT 关键词,去掉表中的重复列
SELECT DISTINCT Sno
FROM SC;
(2).查询满足条件的元组
- 比较大小
【eg. 3.22】查询计算机科学系全体学生的名单
SELECT Sname
FROM Student
WHERE Sdept='CS';
【eg. 3.23】查询所有年龄在20岁以下的学生姓名及年龄
SELECT Sname,Sage
FROM Student
WHERE Sage<20;
【eg. 3.24】查询考试有不及格的学生的学号
SELECT DISTINCT Sno
FROM Student
WHERE Grade<60;
-
确定范围
-
谓词:BETWEEN… AND…
NOT BETWEEN… AND…
-
【eg. 3.25】查询年龄在20—23岁(包括20和23)之间的学生姓名、系别、年龄
SELECT Sname,Sdept,Sage
FROM Student
WHERE Sage BETWEEN 20 AND 23;
/*年龄不在20—23岁(包括20和23)之间的学生姓名、系别、年龄*/
SELECT Sname,Sdept,Sage
FROM Student
WHERE Sage NOT BETWEEN 20 AND 23;
-
确定集合
-
谓词:IN<值表>,
NOT IN<值表>
-
【eg. 3.27】查询计算机系(CS)、数学系(MA)和信息系(IS) 学生姓名和性别
SELECT Sname,Sage
FROM Student
WHERE Sdept IN ('CS','MA','IS');
/*查询不是计算机系(CS)、数学系(MA)和信息系(IS) 学生姓名和性别*/
SELECT Sname,Sage
FROM Student
WHERE Sdept NOT IN ('CS','MA','IS');
-
字符匹配
-
谓词:[NOT] LIKE ‘<匹配串>’ [ESCAPE’<换码字符>’]
匹配串可以是一个完整的字符串,也可以含有通配符%和_
-
%(百分号) 代表任意长度(长度也可以为0)的字符串
例如a%b 表示以a 开头,以b 结尾的任意长度的字符串
-
_(下横线) 代表任意单个字符
例如a_b 表示以a 开头,以b 结尾的长度为3 的任意字符串
-
-
-
- 匹配串为固定字符串
【eg. 3.29】查询学号为2018304188 的学生的详细情况
SELECT *
FROM Student
WHERE Sno LIKE '2018314188';
等价于
SELECT *
FROM Student
WHERE Sno='2018314188';
-
- 匹配串为含通配符的字符串
【eg. 3.30】查询所有姓刘学生的姓名、学号和性别;
SELECT Sname,Sno,Ssex
FROM Student
WHERE Sname LIKE '刘%';
【eg. 3.31】查询姓“欧阳”且全名为三个汉字的学生的姓名;
SELECT Sname
FROM Student
WHERE Sname LIKE '欧阳__'; /*一个汉字两个字符长度*/
【eg. 3.32】查询名字中第二个字为“阳”字的学生的姓名和学号;
SELECT Sname,Sno
FROM Student
WHERE Sname LIKE '__阳%';
【eg. 3.33】查询所有不姓刘的学生姓名、学号和性别;
SELECT Sname,Sno,Ssex
FROM Student
WHERE Sname NOT LIKE '刘%';
-
- 使用换码字符将通配符转义成普通字符
【eg. 3.34】查询DB_Design 课程的课程号和学分;
SELECT Cno,Ccredit
FROM Course
WHERE Cname LIKE 'DB\_Design' ESCAPE'\'; /* ESCAPE'\' 表示\ 之后的字符为普通字符*/
-
- 使用换码字符将通配符转义成普通字符
【eg. 3.35】 查询以“DB_"开头,且倒数第三个字符为”i“ 的课程的详细情况
SELECT *
FROM Course
WHERE Cname LIKE 'DB\_%i__' ESCAPE'\'; /* ESCAPE'\' 表示"\"为转换字符 */
- 涉及空值的查询
谓词:
IS NULL或IS NOT NULL
【eg. 3.36】 某些学生选修课程后没有参加考试,所以有选课记录但没有考试成绩。查询缺少成绩的学生的学号和相应的课程号
SELECT Sno,Cno
FROM SC
WHERE Gade IS NULL;
-
多重条件查询
逻辑运算符:AND 和OR 来连接多个查询条件
- AND 优先级高于OR
- 可以用括号改变优先级
【eg. 3.38】 查询计算机系年龄在20岁以下的学生姓名
SELECT Sname
FROM Student
WHERE Sdept='CS' AND Sage<20;
【eg. 3.27】查询计算机系(CS)、数学系(MA)和信息系(IS) 学生姓名和性别
SELECT Sname,Sage
FROM Student
WHERE Sdept IN ('CS','MA','IS');
/*查询不是计算机系(CS)、数学系(MA)和信息系(IS) 学生姓名和性别*/
SELECT Sname,Sage
FROM Student
WHERE Sdept NOT IN ('CS','MA','IS');
也可以改写为
SELECT Sname,Sage
FROM Student
WHERE Sdept='CS' OR Sdept='MA' OR Sdept='IS';
3. ORDER BY子句
- ORDER BY子句
- 可以按照一个或者多个属性列排序
- 升序:ASC,降序:DESC;缺省值为升序
- 对于空值,排序时显示的次序由具体系统实现来决定
【eg. 3.39】查询选修了三门课的学生的学号以及成绩,查询结果按照分数降序排列
SELECT Sno,Grade
FROM SC
WHERE Cno='3'
ORDER BY Grade DESC;
【eg. 3.40】查询全体学生情况,查询结果按照所在院系的系号排列,同一系的学生按照年龄降序排列
SELECT *
FROM Student
ORDER BY Sdept (ASC),Sage DESC;
4. 聚集函数
-
聚集函数:
-
统计元组的个数
COUNT(*)
-
统计一列中值的个数
COUNT([DISTINCT|ALL]<列名>)
-
计算一列值的总和(此列必须为数值型)
SUM([DISTINCT|ALL<列名>])
-
计算一列值的平均值(此列必须为数值型)
AVG([DICTINCT|ALL<列名>])
-
求一列中最大值和最小值
MAX([DISTINCT|ALL<列名>]) MIN([DISTINCT|ALL<列名>])
-
【eg. 3.41】查询学生总人数
SELECT COUNT(*)
FROM Student;
【eg. 3.42】查询选修了课程的总人数
SELECT COUNT(DISTINCT Sno)
FROM SC;
【eg. 3.43】计算1号课程的学生平均成绩
SELECT AVG(Grade)
FROM SC
WHERE Cno='1';
【eg. 3.44】查询选修了1号课程的学生的最高分数
SELECT MAX(Grade)
FROM SC
WHERE Cno='1';
【eg. 3.45】查询学生2018304188选修课程的总学分数
SELECT SUM(Ccredit)
FROM SC,Course
WHERE Sno='2018304188' AND SC.Cno=Course.Cno;
5. GROUP BY子句
-
GROUP BY 子句
细化聚集函数的作用对象
- 如果未对查询结果分组,聚集函数将作用于整个查询结果
- 对查询结果分组后,聚集函数将分别作用于每个组
- 按指定的一列或多列分组,值相等的为一组
【eg. 3.46】求每个课程号及相应的选课人数
SELECT Cno,COUNT(Sno)
FROM SC
GROUP BY Cno; /* 以Cno为分组属性 */
【eg. 3.47】查询选修了三门以上的课程的学生学号
SELECT Sno
FROM SC
GROUP BY Sno
HAVING COUNT(*)>3;
【eg. 3.48】查询平均成绩大于等于90分的学生学号和平均成绩
SELECT Sno,AVG(Grade)
FROM SC
GROUP BY Sno
HAVING AVG(Grade)>=90;
WHERE 子句不能用聚集函数作为条件表达式。
- HAVING短句和WHERE 子句的区别
- 作用对象不同
- WHERE子句作用于基表或视图,从中选择满足条件的元组
- HAVING短语作用于组,从中选择满足条件的组
6.综合练习
【练习一】列出计算机系姓刘的同学的信息,按照学号大小排序
SELECT *
FROM Student
WHERE Sdept='CS' AND Sname LIKE '刘%'
ORDER BY Sno;
【练习二】按系区分男女统计各系学生的人数、并按照人数降序排列
SELECT Sdept,Ssex,COUNT(Sno)
FROM Student
GRUOP BY Sdept,Ssex
ORDER BY COUNT(Sno)DESC;
3.3.2 连接查询
-
不像关系代数中”连接“是用一个特殊符号来表达的,在SQL中”连接“是用”连接条件“来表达的
-
连接条件或连接谓词:用来连接两个表的条件
一般格式
- [<表名1>.]<列名1><比较运算符>[<表名2>.]<列名2>
-
连接字段:连接谓词中的列名称
- 连接条件中的各连接字段类型必须是可比较的,但名字不必相同
1.等值与非等值查询
- 等值连接:连接运算符为”=”
【eg. 3.49】查询每个学生及其选修课程的情况
SELECT Student.*,SC.*
FROM Student,SC
WHERE Student.Sno=SC.Sno;
- 自然连接
- 采用在SELECT中去掉重复字段的方式实施
【eg. 3.50】对上例用自然连接完成
SELECT Student.Sno,Sname,Ssex,Sage.Sdept,Cno,Grade/*Student.Sno表明只保留了Student中的Sno */
FROM Student,SC
WHERE Student.Sno=SC.Sno;
连接操作的执行过程
(1). 嵌套循环法(NESTED-LOOP)
- 首先在表1中找到第一个元组,然后从头开始扫描表2,逐一查找满足连接件的元组,找到后就将表1中第一个元组与该元组拼接起来,形成结果表中一个元组;
- 表2全部查找完后,再找表1中第二个元组,然后再从头开始扫描表2 ,逐一查找满足连接条件的元组,找到后就将表1 中的第二个元组与该元组拼接起来,形成结果表中一个元组;
- 重复上述操作,直到表1中全部元组都处理完毕。
(2).排序合并法(SORT-MERGE)
(3).索引连接法(INDEX-JOIN)
【eg. 3.51】查询选修了2号课程且成绩在90分以上的所有学生的学号和姓名
SELECT Student.Sno,Sname
FROM Student,SC
WHERE Student.Sno=SC.Sno AND SC.Cno='2' AND SC.Grade>90;
/* Student.Sno=SC.Sno 连接谓词“=” 选择谓词 AND*/
2. 自身连接
- 自身连接:一个表与其自己进行连接,是一种特殊的连接
- 需要给表起别名以示区别
- 由于所有属性名都是同名属性,因此必须使用别名前缀
【eg. 3.52】查询每一门课的直接先修课的名称
SELECT FIRST.Cname,SECOND.Cname
FROM Course FIRST,Course SECOND
WHERE FIRST.Cpno=SECOND.Cno;
3. 外连接
- 外连接与普通连接的区别
- 普通连接操作只输出满足连接条件的元组
- 外连接操作以指定表为连接主体,将主题表中不满足连接条件的元组一并输出
- 左外连接
- 列出左边关系中所有元组
- 右外连接
- 列出右边关系中所有元组
【eg. 3.53】改写【eg. 3.49】查询每个学生及其选修课程的情况
SELECT Student.*,SC.*
FROM Student,SC
WHERE Student.Sno=SC.Sno;
SELECT Student.Sno,Sname,Ssex,Sage,Sdept,Cno,Grade
FROM Student LEFT OUT JOIN SC ON (Student.Sno=SC.Sno);
或
WHERE Student.Sno(+)=SC.Sno;
4. 多表连接
- 两个以上的表进行连接
【eg. 3.53】查询每个学生的学号、姓名、选修的课程名以及成绩
SELECT Student.Sno,Sname,Cno,Grade
FROM Student,SC,Course /*多表连接*/
WHERE Student.Sno=SC.Sno
AND SC.Cno=Course.Cno;
3.3.3 嵌套查询
-
嵌套查询概述
- 一个SELECT-FROM-WHERE 语句称为一个查询块
- 将一个查询块嵌套在另外一个查询块的WHERE子句或HAVING短语的条件中的查询称为嵌套查询
SELECT Sname /*外层查询/父查询*/ FROM Student WHERE Sno IN (SELECT Sno /*内层查询/子查询*/ FROM SC WHERE Cno='2' );
- 上层查询快称为外层查询或父查询
- 下层查询称为内层查询或子查询
- SQL语句允许多层嵌套查询
- 子查询的限制
- 不能使用ORDER BY 子句
-
嵌套查询求解方法
- 不相关子查询:子查询的查询条件不依赖于父查询
- 由里向外逐层处理,即每个子查询在上一级查询处理之前求解,子查询的结果用于建立其父查询的查找条件。
- 相关子查询:子查询的查询条件依赖于父查询
- 首先取外层查询表中的第一个元素,根据它与内层查询相关的属性值处理内层查询,若WHERE子句返回值为真,则取此元组放入结果表
- 然后再取外层表的下一个元组
- 重复这一过程,直至外层表全部检查完为止
- 不相关子查询:子查询的查询条件不依赖于父查询
1.带有IN谓词的子查询
【eg. 3.55】查询与“刘晨”在同一个系学习的学生,此查询要求可以分布完成
/*1.确定“刘晨”所在系名*/
SELECT Sdept
FROM Studnt
WHERE Sname='刘晨';
/*结果为:CS*/
/*2. 查找所有在CS系学习的学生*/
SELECT Sno,Sname,Sdept
FROM Student
WHERE Sdept='CS';
/*将第一步查询嵌入第二步查询的条件中*/
SELECT Sno,Sname,Sdept
FROM Student
WHERE Sdept IN
(SELECT Sdept
FROM Student
WHERE Sname='刘晨');
此查询为不相关子查询
用自身连接完成【eg. 3.55】查询要求
SELECT S1.Sno,S1.Sname,S1.Sdept
FROM Student S1,Student S2
WHERE S1.Sdept=S2.Sdept AND S2.Sname='刘晨';
【eg. 3.56】查询选修了课程名为“信息系统”的学生姓名和学号
SELECT Sno,Sname /*3. 最后再Student关系中取出Sno和Snmae*/
FROM Student
WHERE Sno IN
(SELECT Sno /*2. 然后再SC关系里找到选修了三号课程的学生学号*/
FROM SC
WHERE Cno IN
(SELECT Cno /*1. 首先在Course关系中找出“信息系统”的课程号,为三号*/
FROM Course
WHERE Cname='信息系统'
)
);
用连接查询来实现【eg. 3.56】
SELET Sno,Sname
FROM Student,SC,Course
WHERE Student.Sno=SC.Sno AND
SC.Cno=Course.Cno AND
Course.Cname='信息系统';
2. 带有比较运算符的子查询
- 当能确切知道内层查返回单值时。可以用比较运算符(>,<,=,>=,<=,!=,或<>)。
在【eg. 3.55】查询与“刘晨”在同一个系学习的学生,此查询要求可以分布完成
SELECT Sno,Sname,Sdept
FROM Student
WHERE Sdept = /*由于一个学生只能在一个系学习,用=代替*/
(SELECT Sdept
FROM Student
WHERE Sname='李晨'
);
【eg. 3.57】找出每个学生超过他选修课平均成绩的课程号
SELECT Cno
FROM SC x
WHERE Grade>=(SELECT AVG(Grade)
FROM SC y
WHERE y.Sno=x.Sno
);
此查询为相关子查询
-
可能执行的过程
- 从外查询中选出SC的一个元组x,将元组x的Sno值(2018304188)传送给内查询
SELECT AVG(Grade) FROM SC y WHERE y.Sno='2018304188';
- 执行内查询,得到近似值88,用该值代替内层查询,得到外层查询
SELECT Sno,Cno FROM SC x WHERE Grade>=(SELECT AVG(Grade) FROM SC y WHERE y.Sno='2018304188');
执行该查询,得到结果
3. 带有ANY(SOME) 或 ALL 谓词的子查询
- 引入ANY和ALL谓词,其对象为某个查询结果,表示其中任意一个值或全部值
- 引入EXIST谓词,其对象也是某个查询结果,但表示这个查询结果是否为空,返回空值
使用ANY 或 ALL 谓词时必须同时使用比较运算
语义为:
>ANY | 大于子查询结果中的某个值 |
>ALL | 大于子查询中的所有值 |
<ANY | 小于子查询结果中的某个值 |
<ALL | 小于子查询结果中的所有值 |
>=ANY | 大于等于子查询结果中的某个值 |
>=ALL | 大于等于子查询结果中的所有值 |
【eg. 3.58】查询非计算机科学系中比计算机科学系 任意一个 学生连接小的学生姓名和年龄
SELECT Sname,Sage
FROM Student
WHERE Sage<ANY (SELECT Sage
FROM Student
WHERE Sdept='CS'
)
AND Sdept<>'CS'; /*父查询块中的条件 Sdept!='CS'*/
- 用聚集函数实现【eg. 3.58】
SELECT Sname
FROM Student
WHERE Sage<
(SELECT MAX(Sage)
FROM Student
WHERE Sdept='CS'
)
AND Sdept<>'CS';
【eg. 3.59】查询非计算机科学系中比计算机科学系 所有 学生年龄都小的学生姓名及年龄
方法一:用ALL谓词
SELECT Sname,Sage
FROM Student
WHERE Sage<ALL
(SELECT Sage
FROM Student
WHERE Sdept='CS')
AND Sdept<>'CS';
方法二:用聚集函数
SELECT Sname,Sage
FROM Student
WHERE Sage<
(SELECT MIN(Sage)
FROM Student
WHERE Sdept='CS'
)
AND Sdept<>'CS';
4. 带有EXISTS 谓词的子查询
- EXISTS谓词
- 带有EXISTS 谓词的子查询不返回任何数据,只产生逻辑真值“true” 或逻辑假值 “false”
- 若内层查询结果非空,则外层的WHERE子句返回真值
- 若内层查询结果为空,则外层的WHERE子句返回假值
- 由EXISTS 引出的子查询,其目标列表达式通常都用*,因为带EXISTS的子查询只能返回真值或者假值,给出列名无意义
- 带有EXISTS 谓词的子查询不返回任何数据,只产生逻辑真值“true” 或逻辑假值 “false”
【eg. 3.60】查询所有选修了1号课程的学生姓名
思路分析:
-
-
- 本查询涉及Student和SC关系
- 在Student中依次取每个元组的Sno值,用此值取检查SC表
- 若SC中存在这样的元组,其Sno值等于此Student.Sno值,并且其Cno=‘1’,则取此Student.Sname 送入结果值
-
SELECT Sname
FROM Student
WHERE EXISTS
(SELECT*
FROM SC
WHERE Sno=Student.Sno AND Cno='1'
);
【eg. 3.61】查询没有选修课程1号的学生姓名
SELECT Sname
FROM Student
WHERE NOT EXISTS
(SELECT*
FROM SC
WHERE Sno=Student.Sno AND Cno='1');
- 用EXISTS代替其他谓词
- 所有带IN 谓词,比较运算符,ANY和ALL谓词的子查询都能用带EXISTS 谓词的子查询等价替换
- 用EXISTS/NOT EXISTS 实现全称量词
- 用EXISTS实现逻辑蕴涵
- 可以把带有全称量词的谓词转换成等价的带有存在量词的谓词
【eg. 3.55】查询与“刘晨”在同一个系学习的学生,此查询要求可以分布完成
可以用带EXISTS谓词的相关子查询替代
SELECT Sno,Sname,Sdept
FROM Student S1
WHERE EXISTS
(SELECT*
FROM Student S2
WHERE S2.Sdept=S1.Sdept AND S2.Sname='刘晨');
【eg. 3.62】查询选修了全部课程的学生姓名
SELECT Sname
FROM Student
WHERE NOT EXISTS /*返回真值*/
(SELECT *
FROM Course
WHERE NOT EXISTS /*返回真值*/
(SELECT *
FROM SC
WHERE Sno=Student.Sno
AND Cno=Course.Cno)
);
- 用EXISTS实现逻辑蕴涵
【eg. 3.63】查询了至少选修了学生2018304188选修的全部课程的学生号码
SELECT DISTINCT SCX.Sno
FROM SC SCX
WHERE NOT EXISTS
(SELECT *
FROM SC SCY
WHERE SCY.Sno='2018304188' AND
NOT EXISTS /*选修了学生2018304188选修的全部课程*/
(SELECT *
FROM SC SCZ
WHERE SCZ.Sno=SCX.Sno AND
SCZ.Cno=SCY.Cno)
);
3.3.4 集合查询
- 集合操作的种类
- 并操作UNION
- 交操作INTERSECT
- 差操作EXCEPT
- 参加集合操作的各查询结果的列数必须相同,对应的数据类型也必须相同
【eg. 3.64】查询计算机科学系的学生及年龄不大于19岁的学生
SELECT *
FROM Student
WHERE Sdept='CS'
UNION
SELECT *
FROM Student
WHERE Sage<=19;
- UNION:将多个查询结果合并起来,系统自动去除重复元组
- UNION ALL:将多个查询结果合并起来,保留重复元组
【eg. 3.65】查询选修了课程1或者课程2 的学生
SELECT Sno
FROM SC
WHERE Cno='1'
UNION
SELECT Sno
FROM SC
WHERE Cno='2';
【eg. 3.66】查询计算机系的学生与年龄不大于19岁的学生的交集
SELECT *
FROM Student
WHERE Sdept='CS'
INTERSECT
SELECT *
FROM Student
WEERE Sage<=19;
/*等价于*/
SELECT *
FROM Student
WHERE Sdept='CS' AND Sage<=19;
【eg. 3.67】查询即选修了课程1,又选修了课程2 的学生
SELECT Sno
FROM SC
WHERE Cno='1'
INTERSECT
SELECT Sno
FROM SC
WHERE Cno='2';
/*等价于*/
SELECT Sno
FROM SC
WHERE Cno='1' AND Sno IN
(SELECT Sno
FROM SC
WHERE Cno='2');
【eg. 3.68】查询计算机系的学生与年龄不大于19岁的差集
SELECT *
FROM Student
WHERE Sdept='CS'
EXCEPT
SELECT *
FROM Student
WHERE Sage<=19;
/*实际上是查询计算机系中年龄大于19岁的学生,故等价于*/
SELECT *
FROM Student
WHERE Sdept='CS' AND Sage>19;
3.4 数据更新
3.4.1 插入数据
- 两种插入数据方式
- 插入元组
- 插入子查询结果
- 可以一次插入多个元组
1. 插入元组
-
语句格式
INSERT INTO <表名>[(<属性列1>)[,<属性列2>....)]] VALUES(<常量1>[,<常量2>]...);
-
功能
- 将新元组插入指定表中
-
INTO子句
- 指定要插入数据的表名及属性列
- 属性列的顺序可与表定义中的顺序不一致
- 没有指定属性列:表示要插入的是一条完整的元组,且属性列属性与表定义中的顺序一致
- 没有指定部分属性列:插入元组在其余属性列上取空值
-
VALUES子句
提供的值必须与INTO子句匹配
- 值的个数
- 值的类型
【eg. 3.69】将一个新学生元组(学号:2018304188;姓名:王;性别:男;所在系:IS;年龄:18)插入到Student表中
INSERT
INTO Student(Sno,Sname,Ssex,Sdept,Sage)
VALUES('2018304188','王','男',IS','18');
【eg. 3.71】插入一条选课记录(‘2018304188’,‘1’).
INSERT
INTO SC(Sno,Cno)
VALUES('2018304188','1');
关系DBMS 将在新插入记录的Grade列上自动赋空值
或者
INSERT
INTO SC
VALUES('201215128','1',NULL);
【eg. 3.70】将学生张成民的信息插入到Student表中
INSERT
INTO Student
VALUES('201215126','张成民','男','18','CS');
2. 插入子查询结果
-
语句格式
INSERT INTO<表名>[(<属性列1>)[,<属性列2>...]] 子查询;
-
INTO子句
-
子查询
- SELECT 子句目标列必须与INTO子句匹配
- 值的个数
- 值的类型
- SELECT 子句目标列必须与INTO子句匹配
【eg. 3.72】对每个系,求学生的平均年龄,并把结果存入数据库
第一步:建表
CREATE TABLE Dept_age /*表名*/
(Sdept CHAR(15) /*系名*/
AVG_age SMALLINT); /*学生平均年龄*/
第二步:插入数据
INSERT
INTO Dept_age(Sdept,AVG_age)
SELECT Sdept,AVG_age
FROM Student
GROUP BY Sdept;
3.4.2 修改数据
-
语句格式
UPDATE<表名> SET<列名>=<表达式>[,<列名>=<表达式>]... [WHERE<条件>];
-
功能
- 修改指定表中满足WHERE子句条件的元组
- SET子句给出表达式的值用于取代相应的属性列
- 如果省略WHERE子句,表示要修改表中的所有元组
-
三种修改方式
- 修改某一元组的值
- 修改多个元组的值
- 带子查询的修改语句
1. 修改某一元组的值
【eg. 3.73】将学生201215121的年龄改为22岁
UPDATE Student
SET Sage=22
WHERE Sno='201215121';
2. 修改多个元组的值
【eg. 3.74】把所有学生的年龄都增加1
UPDATE Student
SET Sage=Sage+1;
3. 带子查询的修改语句
【eg. 3.75】将计算机系全体学生的成绩置为0
UPDATE SC
SET Grade=0
WHERE Sno IN
(SELECT Sno
FROM Student
WHERE Sdept='CS'
);
3.4.3 删除数据
-
语句格式
DELETE FROM <表名> [WHERE<条件>];
-
功能
- 删除指定表中满足WHERE 子句条件的元组
-
WHERE子句
- 指定要删除的元组
- 无该子句将会删除表中的全部元组
-
三种删除方式
- 删除某一元组的值
- 删除多个元组的值
- 带子查询的删除语句
1. 删除某一元组的值
【eg. 3.76】删除学号为201215128的学生记录
DELETE FROM Student
WHERE Sno='201215128';
2. 删除多个元组的值
【eg. 3.77】删除所有学生选课记录
DELETE FROM SC;
3. 带子查询的删除语句
【eg. 3.78】删除计算机系所有学生的选课记录
DELETE
FROM sc
WHERE Sno IN
(SELECT Sno
FROM Student
WHERE Sdept='cs');
3.5 空值的处理
- 空值就是“不知道”或“不存在”或“无意义”的值
- 一般有以下几种情况
- 该属性应该有一个值,但目前不知道它的具体值
- 该属性不应该有值
- 由于某种原因不便于填写
1. 空值的产生
-
空值是一个很特殊的值,含有不确定性。对关系运算带来特殊问题,需要做特殊的处理
-
空值产生有其实际需要
例如学生在选课中,用空值来表示没选课
2. 空值的判断
- 判断一个属性的值是否为空值,用IS NULL 或 IS NTI NULL 来表示
【eg. 3.81】找出漏填了性别或者年龄信息的记录
SELECT *
FROM Student
WHERE Ssex IS NULL OR Sage IS NULL;
3. 空值的约束条件
- 属性定义(或者域定义中)
- 有NOT NULL约束条件的不能取空值
- 加了UNIQUE 限制的不能取空值
- 码属性不能取空值
- 空值和另外一个值(包括另外一个空值)的运算结果为空值
- 空值与另外一个值的比较运算结果为UNKNOWN
- 有UNKNOWN 后,传统二值(TRUE,FALSE)运算逻辑就扩展到了三值运算
【eg. 3.82】找出选修一号课程的不及格的学生
SELECT Sno
FROM SC
WHERE Grade<60 AND Cno='1';
查询结果不包括缺考的学生,因为他们的Grade值为NULL
【eg. 3.83】选出课程以号不及格的学生以及缺考的考生
SELECT Sno
FROM SC
WHERE Cno='1' AND (Grade<60 OR Grade IS NULL);
4. 空值的算数运算、比较运算和逻辑运算
3.6 视图
- 视图的特点
- 虚表:是从一个表或者几个基本表(或视图)导出的表;
- 只存放视图的定义,不存放视图对应的数据;
- 基表中的数据发生变化,从视图中查询的数据也随之改变;
3.6.1 定义视图
1. 定义视图
-
语句格式
CREATE VIEW <视图名>[(<列名>[,<列名>]...)] AS <子查询> [ WITH CHECK OPTION];
-
WITH CHECK OPTION
- 对视图进行UPDATE,INSERT,DELETE操作时要保证更新、插入、删除的行满足视图定义中的谓词条件(即子查询中的条件表达式)
-
子查询可以是任何SELECT 语句,是否含有ORDER BY子句和DISTINCT 短语,则决定具体系统的实现
-
组成视图的属性列名:全部省略或全部指定
- 全部省略:由子查询中SELECT 目标列中的诸字段组成
- 明确指定视图中的所有列名
- 某个目标列时聚集函数或列表达式
- 多表连接时选出几个同列名作为视图的字段
- 需要在视图中为某个列启用新的名字
-
关系DBMS执行CREATE VIEW 语句时,只是把视图定义存入数据字典,并不执行其中的SELECT语句
-
在对视图查询时,按视图的定义从基本表中将数据查出
【eg. 3.84】建立信息系学生的视图
CREATE VIEW IS_Student
AS
SELECT Sno,Sname,Sage
FROM Student
WHERE Sdept='IS';
【eg. 3.84】建立信息系学生的视图,并要求进行修改和插入操作时任保证该视图只有信息系的学生
CREATE VIEW IS_Studnt
AS
SELECT Sno,Sname,Sage
FROM Student
WHERE Sdept='IS'
WITH CHECK OPTION;
-
定义IS_Student 视图时加上WITH CHECK OPTION 子句,对该视图进行插入、修改、删除操作时,RDBMS会自动加上Sdept='IS’的条件
-
若一个视图是从单个基本表中导出的,并且只是去掉了基本表的某些行和某些列,但保留了主码,我们称这类视图为行列子集视图
- IS_Student视图就是一个行列子集视图
-
基于多个表的视图
【eg. 3.86】建立信息系选修了1号课程的学生的视图(包括学生学号,姓名,成绩等)
CREATE VIEW IS_S1(Sno,Sname,Grade)
AS
SELECT Student.Sno,Sname,Grade
FROM Student,SC
WHERE Sdept='IS' AND
Student.Sno=SC.Sno AND
SC.Cno='1';
- 基于视图的视图
【eg. 3.87】建立信息系选修了1号课程且成绩在90分以上的学生的视图
CREATE VIEW IS_S2
AS
SELECT Sno,Sname,Grade
FROM IS_S1
WHERE Grade>=90;
- 带表达式的视图
【eg. 3.88】定义一个反映学生出生年份的视图
CREATE VIEW BT_S(Sno,Sname,Sbirth)
AS
SELECT Sno,Sname,2020-Sage
FROM Student;
- 分组视图
【eg. 3.89】将学生的学号及平均成绩定义为一个视图
CREATE VIEW S_G(Sno,Gavg)
AS
SELECT Sno,AVG(Grade)
FROM SC
GROUP BY Sno;
【eg. 3.90】将Student表中所有女生记录定义为一个视图
CREATE VIEW F_Student(F_Sno,name,sex,age,dept)
AS
SELECT * /*没有不指定属性列*/
FROM Student
WHERE Ssex='女';
缺点:修改基本表Student的机构后,Student表与F_Student视图的映像关系被破坏,导致视图不能正确工作
2. 删除视图
-
语句的格式
DROP VIEW <视图名>[CASCADE]
- 该语句从数据字典中删除指定的视图定义
- 如果该视图上还导出了其他视图,使用CASCADE 级联删除语句,就是把该视图和由他导出的所有视图一起删除
- 删除基表时,由该基表导出的所有视图定义都必须显式的使用DROP VIEW 语句删除
【eg. 3.91】删除视图BT_1和IS_S1
DROP VIEW BT_S; /*成功执行*/
DROP VIEW IS_S1; /*拒绝执行*/
要删除IS_S1 需使用级联删除:
DROP VIEW IS_S1 CASCADE;
3.6.2 查询视图
- 用户角度:查询视图与查询基本表相同
- 关系数据库管理系统实现视图查询的方法
- 视图消解法
- 进行有效检查
- 转换成等价的对基本表的查询
- 执行修正后的查询
- 视图消解法
【eg. 3.92】在信息系学生的视图中找出年龄小于20岁的学生
SELECT Sno,Sage
FROM Student
WHERE Sage<20;
视图消极转换后的查询语句为
SELECT Sno,Sage
FROM Student
WHERE Sdept='IS' AND Sage<20;
【eg. 3.93】查询选修了课程1号的信息系学生
SELECT IS_Student.Sno,Sname
FROM IS_Student,SC
WHERE IS_Student.Sno=SC.Sno AND SC.Cno='1';
【eg. 3.94】在S_G 视图中查询平均成绩在90分以上的学生学号和平均成绩
SELECT *
FROM S_G
GROUP BY Sno
HAVING Gavg>=90;
/*S_G视图的定义如下*/
CREATE VIEW S_G(Sno,Gavg)
AS
SELECT Sno,AVG(Grade)
FROM SC
GROUP BY Sno;
也可以用SQL语句完成
SELECT *
FROM (SELECT Sno,AVG(Grade)
FROM SC
GROUP BY Sno)
AS
S_G(Sno,Gavg)
WHERE Gavg>=90;
3.6.3 更新视图
【eg. 3.95】将信息系学生视图IS_Student中学号为2018304188的学生姓名改为小王
UPDATE IS_Student
SET Sname='小王'
WHERE Sno='2018304188';
转换后的语句
UPDATE Student
SET Sname='小王'
WHERE Sno='2018304188' AND Sdept='IS';
【eg. 3.96】向信息系学生视图IS_Student 中插入一个新的学生记录,其中学号为“2018304188”,姓名为小郑,年龄为20岁
INSERT
INTO IS_Stdent(Sno,Sname,Sage)
VALUES('2018304188','小郑','20');
转换为对基本表的更新
INSERT
INTO Student(Sno,Sname,Sage,Sdept)
VALUES('2018304188','小郑','20','IS');
【eg. 3.97】删除信息系学生视图IS_Student中学号为2018304188的记录
DELETE
FROM IS_Student
WHERE Sno='2018304188'
转换为对基本表的更新
DELETE
FROM Student
WHERE Sno='2018304188' AND Sdept='IS';
-
更新视图的限制:有一些视图是不可更新的,因为对这些视图的更新不能唯一的有意义的转换称对相应基本表的更新
-
允许对行列子集视图(单表上的视图)进行更新
-
对其他类型的视图的更新不同系统由不同限制
-
DB2对视图的更新的限制
- 若视图是由两个以上基本表导出的,则此视图不允许更新
- 若视图的字段来自字段表达式或常数,则不允许执行INSERT 和UPDATE 操作,但允许DELETE 操作
- 若视图是字段来自集函数,则此视图不允许更新
- 若视图定义中含有GROUP BY子句 或DISTINCT短语
- 若视图定义中有嵌套查询
3.6.4 视图的作用
- 视图能简化用户的操作
- 视图使用户能以多个角度看待同一数据
- 视图对重构数据库提供了一定的逻辑独立性
- 视图能对机密数据提供安全保护
- 适当的利用视图能更清晰的表达查询