关系数据库标准语言 SQL
一、SQL 概述
1.1 什么是 SQL
SQL(Structured Query Language,结构化查询语言) 是关系数据库的标准语言,是一个通用的、功能极强的关系数据库语言。
1.2 SQL 标准的演进
| 标准 | 页数 | 发布日期 |
|---|---|---|
| SQL/86 | — | 1986.10 |
| SQL/89 (FIPS 127-1) | 120 页 | 1989 年 |
| SQL/92 | 622 页 | 1992 年 |
| SQL99 | 1700 页 | 1999 年 |
| SQL2003 | 3600 页 | 2003 年 |
| SQL2008 | 3777 页 | 2006 年 |
| SQL2011 | 3817 页 | 2010 年 |
| SQL2016 | 4035 页 | 2016 年 |
| SQL2023 | — | 2023 年 |
目前没有一个数据库系统能支持 SQL 标准的所有概念和特性。
1.3 SQL 的主要特点
- 功能综合,风格统一 — 集 DDL(数据定义语言)、DML(数据操纵语言)、DCL(数据控制语言)于一体,可独立完成数据库全生命周期活动。
- 高度非过程化 — 用户只需提出"做什么",无需指定存取路径,由系统自动完成。
- 面向集合的操作方式 — 操作对象、查找结果、增删改操作的对象都是元组的集合。
- 以统一语法提供多种使用方式 — 既可作为独立语言用于联机交互,也可嵌入 C、C++、Java 等高级语言中使用。
- 语言简洁,易学易用。
1.4 SQL 的基本概念
SQL 支持关系数据库的三级模式结构:
- 基本表:本身独立存在的表,一个关系对应一个基本表,一个(或多个)基本表对应一个存储文件。
- 存储文件:逻辑结构和物理结构组成关系数据库的内模式,物理结构对用户透明。
- 视图:从一个或几个基本表(或其他视图)导出的表,是虚表——只存放定义,不存放数据。
二、数据定义(DDL)
2.1 模式的定义与删除
定义模式
CREATE SCHEMA [<模式名>] AUTHORIZATION <用户名>;- 未指定模式名则隐含为用户名。
- 需 DBA 权限或 CREATE SCHEMA 权限。
示例:
-- 为用户 WANG 定义模式 S-T
CREATE SCHEMA "S-T" AUTHORIZATION WANG;
-- 模式名隐含为用户名 WANG
CREATE SCHEMA AUTHORIZATION WANG;
-- 在 CREATE SCHEMA 中同时创建表
CREATE SCHEMA TEST AUTHORIZATION ZHANG
CREATE TABLE TAB1 (
COL1 SMALLINT,
COL2 INT,
COL3 CHAR(20),
COL4 NUMERIC(10,3),
COL5 DECIMAL(5,2)
);删除模式
DROP SCHEMA <模式名> <CASCADE|RESTRICT>;- CASCADE(级联):删除模式及其所有数据库对象。
- RESTRICT(限制):仅当模式中无下属对象时才允许删除。
-- 删除模式 TEST 及其所有对象
DROP SCHEMA TEST CASCADE;2.2 基本表的定义、删除与修改
定义基本表
CREATE TABLE <表名> (
<列名> <数据类型> [列级完整性约束],
[<列名> <数据类型> [列级完整性约束], ...]
[, 表级完整性约束]
);常用完整性约束:
PRIMARY KEY— 主码约束UNIQUE— 唯一性约束NOT NULL— 非空约束FOREIGN KEY ... REFERENCES ...— 参照完整性约束
PRIMARY KEY 与 UNIQUE 的区别:一个表只能有一个 PRIMARY KEY,但可以有多个 UNIQUE;PRIMARY KEY 隐含 NOT NULL。
示例:
-- 学生表
CREATE TABLE Student (
Sno CHAR(5) NOT NULL UNIQUE,
Sname CHAR(20) UNIQUE,
Ssex CHAR(2),
Sage INT,
Sdept CHAR(15)
);
-- 课程表(含自参照外键)
CREATE TABLE Course (
Cno CHAR(4) PRIMARY KEY,
Cname CHAR(40),
Cpno CHAR(4),
Ccredit SMALLINT,
FOREIGN KEY (Cpno) REFERENCES Course(Cno)
);
-- 选课表(复合主码)
CREATE TABLE SC (
Sno CHAR(5),
Cno CHAR(4),
Grade INT,
PRIMARY KEY (Sno, Cno),
FOREIGN KEY (Sno) REFERENCES Student(Sno),
FOREIGN KEY (Cno) REFERENCES Course(Cno)
);模式与表
基本表属于某个模式。定义所属模式的方式:
- 在表名中显式指定:
CREATE TABLE "S-T".Student(...) - 在 CREATE SCHEMA 中同时创建
- 设置搜索路径:
SET search_path TO "S-T", PUBLIC;
CREATE TABLE Student (...); -- 归属于 S-T 模式查看搜索路径:SHOW search_path;
修改基本表
ALTER TABLE <表名>
[ADD [COLUMN] <新列名> <数据类型> [完整性约束]]
[ADD <表级完整性约束>]
[DROP [COLUMN] <列名> [CASCADE|RESTRICT]]
[DROP CONSTRAINT <完整性约束名> [CASCADE|RESTRICT]]
[RENAME COLUMN <列名> TO <新列名>]
[ALTER COLUMN <列名> TYPE <数据类型>];示例:
-- 增加入学时间列(新列默认取空值)
ALTER TABLE Student ADD Scome DATE;
-- 修改年龄数据类型
ALTER TABLE Student ALTER COLUMN Sage TYPE SMALLINT;
-- 增加课程名称唯一约束
ALTER TABLE Course ADD UNIQUE(Cname);删除基本表
DROP TABLE <表名> [RESTRICT|CASCADE];- RESTRICT:有限制删除(有依赖对象时拒绝)。
- CASCADE:级联删除(连同索引、视图、触发器等一起删除)。
DROP TABLE Student CASCADE;
-- 注意:视图 IS_Student 也被自动删除2.3 索引的建立与删除
索引的作用
- 加快查询速度的重要手段。
- 常见索引类型:B+ 树索引、Hash 索引、位图索引。
- B+ 树具有动态平衡优点;Hash 索引查找速度快。
- DBA 或表属主可建立索引;DBMS 自动维护并选择索引。
建立索引
CREATE [UNIQUE] [CLUSTER] INDEX <索引名>
ON <表名>(<列名> [<次序>] [, <列名> [<次序>] ...]);UNIQUE:唯一索引,每个索引值对应唯一数据记录。CLUSTER:聚簇索引,索引项顺序与物理记录顺序一致。- 次序:
ASC(升序,默认),DESC(降序)。
示例:
-- 学号升序唯一索引
CREATE UNIQUE INDEX Stusno ON Student(Sno);
-- 课程号升序唯一索引
CREATE UNIQUE INDEX Coucno ON Course(Cno);
-- 按学号升序、课程号降序建唯一索引
CREATE UNIQUE INDEX SCno ON SC(Sno ASC, Cno DESC);
-- 聚簇索引(一个表最多一个)
CREATE CLUSTER INDEX Stusname ON Student(Sname);聚簇索引适用范围:很少对基表进行增删操作,很少对变长列进行修改操作。
修改索引
ALTER INDEX <旧索引名> RENAME TO <新索引名>;删除索引
DROP INDEX <索引名>;2.4 数据字典
数据字典是 RDBMS 内部的系统表集合,记录了所有定义信息:
- 关系模式定义、视图定义、索引定义
- 完整性约束定义
- 各类用户的数据库操作权限
- 统计信息等
执行 DDL 语句实际上就是在更新数据字典。
三、数据查询(SELECT)
3.1 查询语句完整格式
SELECT [ALL|DISTINCT] <目标列表达式> [, <目标列表达式>] ...
FROM <表名或视图名> [, <表名或视图名>] ... | (<SELECT语句>) [AS] <别名>
[WHERE <条件表达式>]
[GROUP BY <列名1> [HAVING <条件表达式>]]
[ORDER BY <列名2> [ASC|DESC]]
[LIMIT <行数1> [OFFSET <行数2>]];SELECT:指定显示属性列FROM:指定查询对象(基本表或视图)WHERE:指定查询条件GROUP BY:按指定列分组,常与集函数配合HAVING:筛选组ORDER BY:对结果排序LIMIT ... OFFSET:限制返回行数
示例数据库(学生-课程数据库):
Student(Sno, Sname, Ssex, Sage, Sdept)
Course(Cno, Cname, Cpno, Ccredit)
SC(Sno, Cno, Grade)3.2 单表查询
一、选择若干列
-- 查询指定列
SELECT Sno, Sname FROM Student;
-- 查询全部列
SELECT * FROM Student;
-- 查询经过计算的值:使用算术表达式、字符串常量、函数、列别名
SELECT Sname, 2025 - Sage FROM Student;
SELECT Sname, 'Year of Birth:' AS BIRTH, 2025 - Sage AS BIRTHDAY,
LOWER(Sdept) AS DEPARTMENT
FROM Student;二、选择若干元组
(1)消除重复行 — DISTINCT
SELECT DISTINCT Sno FROM SC; -- 去除重复学号
SELECT DISTINCT Cno, Grade FROM SC; -- DISTINCT 作用于所有目标列(2)WHERE 条件查询
| 查询条件 | 谓词 |
|---|---|
| 比较 | =, >, <, >=, <=, !=, <>, !>, !< |
| 确定范围 | BETWEEN ... AND ..., NOT BETWEEN ... AND ... |
| 确定集合 | IN, NOT IN |
| 字符串匹配 | LIKE, NOT LIKE |
| 空值 | IS NULL, IS NOT NULL |
| 多重条件 | AND, OR |
示例:
-- 比较大小
SELECT Sname, Sage FROM Student WHERE Sage < 20;
-- 确定范围(包含边界)
SELECT Sname, Sdept, Sage FROM Student WHERE Sage BETWEEN 20 AND 23;
-- 确定集合
SELECT Sname, Ssex FROM Student WHERE Sdept IN ('IS', 'MA', 'CS');
SELECT Sname, Ssex FROM Student WHERE Sdept NOT IN ('IS', 'MA', 'CS');
-- 字符串匹配(LIKE)
-- % 代表任意长度字符串;_ 代表单个字符
SELECT Sname, Sno, Ssex FROM Student WHERE Sname LIKE '刘%'; -- 姓刘
SELECT Sname FROM Student WHERE Sname LIKE '欧阳__'; -- 姓欧阳且三个汉字
SELECT Sname, Sno FROM Student WHERE Sname LIKE '__阳%'; -- 第二个字为"阳"
-- 转义通配符:使用 ESCAPE 短语
SELECT Cno, Ccredit FROM Course WHERE Cname LIKE 'DB\_Design' ESCAPE '\';
SELECT * FROM Course WHERE Cname LIKE 'DB\_%i__' ESCAPE '\';
-- 空值查询
SELECT Sno, Cno FROM SC WHERE Grade IS NULL;
SELECT Sno, Cno FROM SC WHERE Grade IS NOT NULL;
-- 多重条件
SELECT Sname FROM Student WHERE Sdept = 'CS' AND Sage < 20;三、对查询结果排序
-- 按成绩降序
SELECT Sno, Grade FROM SC WHERE Cno = '3' ORDER BY Grade DESC;
-- 多列排序:先按系号升序,同一系按年龄降序
SELECT * FROM Student ORDER BY Sdept, Sage DESC;四、集函数
COUNT([DISTINCT|ALL] *) -- 计数
COUNT([DISTINCT|ALL] <列名>)
SUM([DISTINCT|ALL] <列名>) -- 求和
AVG([DISTINCT|ALL] <列名>) -- 平均值
MAX([DISTINCT|ALL] <列名>) -- 最大值
MIN([DISTINCT|ALL] <列名>) -- 最小值示例:
SELECT COUNT(*) FROM Student; -- 学生总人数
SELECT COUNT(DISTINCT Sno) FROM SC; -- 选修课程的学生人数(去重)
SELECT AVG(Grade) FROM SC WHERE Cno = '1'; -- 1号课程平均分
SELECT MAX(Grade) FROM SC WHERE Cno = '1'; -- 1号课程最高分五、对查询结果分组
-- 求各课程号及选课人数
SELECT Cno, COUNT(Sno) FROM SC GROUP BY Cno;
-- 查询选修了3门以上课程的学生学号
SELECT Sno FROM SC GROUP BY Sno HAVING COUNT(*) > 3;
-- 查询有3门以上课程90分以上的学生的学号及课程数
SELECT Sno, COUNT(*) FROM SC
WHERE Grade >= 90
GROUP BY Sno
HAVING COUNT(*) >= 3;HAVING 与 WHERE 的区别:WHERE 作用于基表/视图,选择满足条件的元组;HAVING 作用于组,选择满足条件的组。
六、LIMIT 子句
-- 查询选修1号课程成绩前10名的学号
SELECT Sno FROM SC
WHERE Cno = '1'
ORDER BY Grade DESC
LIMIT 10;
-- 查询平均成绩排名第3~7名的学号和平均成绩
SELECT Sno, AVG(Grade) FROM SC
GROUP BY Sno
ORDER BY AVG(Grade) DESC
LIMIT 5 OFFSET 2;3.3 连接查询
连接查询同时涉及多个表。
连接条件格式:
[<表名1>.]<列名1> <比较运算符> [<表名2>.]<列名2>连接操作执行算法:
- 嵌套循环法(NESTED-LOOP):外层表逐行扫描,对内层表全表扫描匹配。
- 排序合并法(SORT-MERGE):先按连接属性排序,再合并扫描(常用于等值连接)。
- 索引连接法(INDEX-JOIN):对表2按连接字段建索引,表1每行通过索引查找。
等值连接与非等值连接
-- 等值连接:查询每个学生及其选修课程情况
SELECT Student.*, SC.*
FROM Student, SC
WHERE Student.Sno = SC.Sno;
-- 自然连接(去掉重复列)
SELECT Student.Sno, Sname, Ssex, Sage, Sdept, Cno, Grade
FROM Student, SC
WHERE Student.Sno = SC.Sno;自身连接
表与其自身连接,需起别名。
-- 查询每一门课的间接先修课
SELECT FIRST.Cno, SECOND.Cpno
FROM Course FIRST, Course SECOND
WHERE FIRST.Cpno = SECOND.Cno
AND SECOND.Cpno IS NOT NULL;外连接
保留不满足连接条件的元组。
-- 左外连接:查询每个学生及其选修课程情况(含未选课学生)
SELECT Student.Sno, Sname, Ssex, Sage, Sdept, Cno, Grade
FROM Student LEFT OUTER JOIN SC ON (Student.Sno = SC.Sno);- 左外连接:列出左边关系的所有元组
- 右外连接:列出右边关系的所有元组
复合条件连接
-- 查询选修2号课程且成绩90分以上的学生学号、姓名
SELECT Student.Sno, Sname
FROM Student, SC
WHERE Student.Sno = SC.Sno -- 连接谓词
AND SC.Cno = '2' -- 限定条件
AND SC.Grade > 90; -- 限定条件多表连接
-- 查询每个学生的学号、姓名、选修的课程名及成绩
SELECT Student.Sno, Sname, Cname, Grade
FROM Student, SC, Course
WHERE Student.Sno = SC.Sno
AND SC.Cno = Course.Cno;3.4 嵌套查询
一个 SELECT-FROM-WHERE 称为一个查询块,嵌套在另一个查询块的条件中的查询称为嵌套查询。
- 外层查询:父查询
- 内层查询:子查询
- 子查询中不能使用 ORDER BY。
- 分为不相关子查询(子查询不依赖父查询,由里向外逐层处理)和相关子查询(子查询依赖父查询,外层逐行处理)。
带有 IN 谓词的子查询
-- 查询与"刘晨"在同一个系学习的学生
SELECT Sno, Sname, Sdept
FROM Student
WHERE Sdept IN (
SELECT Sdept
FROM Student
WHERE Sname = '刘晨'
);
-- 查询选修了"信息系统"课程的学生学号和姓名(三层嵌套)
SELECT Sno, Sname
FROM Student
WHERE Sno IN (
SELECT Sno
FROM SC
WHERE Cno IN (
SELECT Cno
FROM Course
WHERE Cname = '信息系统'
)
);带有比较运算符的子查询
子查询返回单值时使用。
SELECT Sno, Sname, Sdept
FROM Student
WHERE Sdept = (
SELECT Sdept
FROM Student
WHERE Sname = '刘晨'
);子查询必须跟在比较运算符之后。
带有 ANY 或 ALL 谓词的子查询
| 谓词 | 含义 |
|---|---|
> ANY | 大于子查询结果中的某个值 |
> ALL | 大于子查询结果中的所有值 |
< ANY | 小于子查询结果中的某个值 |
< ALL | 小于子查询结果中的所有值 |
= ANY | 等于子查询结果中的某个值 |
!= ALL | 不等于子查询结果中的任何一个值 |
-- 查询其他系中比信息系任意一个学生年龄小的学生
SELECT Sname, Sage
FROM Student
WHERE Sage < ANY (
SELECT Sage FROM Student WHERE Sdept = 'IS'
)
AND Sdept <> 'IS';
-- 可用集函数替代(效率更高)
SELECT Sname, Sage
FROM Student
WHERE Sage < (
SELECT MAX(Sage) FROM Student WHERE Sdept = 'IS'
)
AND Sdept <> 'IS';
-- 查询其他系中比信息系所有学生年龄都小的学生
SELECT Sname, Sage
FROM Student
WHERE Sage < ALL (
SELECT Sage FROM Student WHERE Sdept = 'IS'
)
AND Sdept <> 'IS';用集函数实现通常比用 ANY/ALL 效率更高(减少比较次数)。
带有 EXISTS 谓词的子查询
EXISTS:存在量词,子查询非空则返回 true。NOT EXISTS:子查询为空则返回 true。- EXISTS 子查询的目标列通常用
*,因为只关心有没有返回值。
-- 查询所有选修了1号课程的学生姓名
SELECT Sname
FROM Student
WHERE EXISTS (
SELECT *
FROM SC
WHERE Sno = Student.Sno AND Cno = '1'
);
-- 查询没有选修1号课程的学生姓名(用连接运算难于实现)
SELECT Sname
FROM Student
WHERE NOT EXISTS (
SELECT *
FROM SC
WHERE Sno = Student.Sno AND Cno = '1'
);用 EXISTS/NOT EXISTS 实现全称量词(难点):
SQL 中没有全称量词 ∀,可通过转换:(∀x)P ≡ ¬(∃x(¬P))
-- 查询选修了全部课程的学生姓名
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/NOT EXISTS 实现逻辑蕴涵:
-- 查询至少选修了学生95002选修的全部课程的学生号码
SELECT DISTINCT Sno
FROM SC SCX
WHERE NOT EXISTS (
SELECT *
FROM SC SCY
WHERE SCY.Sno = '95002'
AND NOT EXISTS (
SELECT *
FROM SC SCZ
WHERE SCZ.Sno = SCX.Sno AND SCZ.Cno = SCY.Cno
)
);3.5 集合查询
集合操作要求各查询结果列数相同,对应数据类型相同。
并操作(UNION)
-- 查询计算机科学系的学生及年龄不大于19岁的学生
SELECT * FROM Student WHERE Sdept = 'CS'
UNION
SELECT * FROM Student WHERE Sage <= 19;
-- 用 UNION ALL 保留重复元组
SELECT * FROM Student WHERE Sdept = 'CS'
UNION ALL
SELECT * FROM Student WHERE Sage <= 19;
-- 查询选修课程1或课程2的学生
SELECT Sno FROM SC WHERE Cno = '1'
UNION
SELECT Sno FROM SC WHERE Cno = '2';交操作(INTERSECT)
-- 查询计算机科学系中年龄不大于19岁的学生
SELECT * FROM Student WHERE Sdept = 'CS'
INTERSECT
SELECT * FROM Student WHERE Sage <= 19;
-- 查询既选修了课程1又选修了课程2的学生
SELECT Sno FROM SC WHERE Cno = '1'
INTERSECT
SELECT Sno FROM SC WHERE Cno = '2';差操作(EXCEPT)
-- 查询计算机科学系中年龄大于19岁的学生
SELECT * FROM Student WHERE Sdept = 'CS'
EXCEPT
SELECT * FROM Student WHERE Sage <= 19;对集合操作结果的排序
ORDER BY 只能用于最终结果,且用数字指定排序属性:
SELECT * FROM Student WHERE Sdept = 'CS'
UNION
SELECT * FROM Student WHERE Sage <= 19
ORDER BY 1; -- 按第一列排序3.6 基于派生表的查询
子查询出现在 FROM 子句中,生成临时派生表。
-- 找出每个学生超过自己平均成绩的课程号
SELECT Sno, Cno
FROM SC, (
SELECT Sno, AVG(Grade)
FROM SC
GROUP BY Sno
) AS Avg_sc(avg_sno, avg_grade)
WHERE SC.Sno = Avg_sc.avg_sno
AND SC.Grade >= Avg_sc.avg_grade;
-- 查询所有选修1号课程的学生姓名
SELECT Sname
FROM Student, (
SELECT Sno FROM SC WHERE Cno = '1'
) AS SC1
WHERE Student.Sno = SC1.Sno;视图与派生表的区别:视图定义存入数据字典可反复引用;派生表仅在语句执行时临时定义,用完即删。
3.7 SELECT 语句一般格式总结
SELECT [ALL|DISTINCT] <目标列表达式> [别名] [, ...]
FROM <表名或视图名> [别名] [, ...] | (<SELECT语句>) [AS] <别名>
[WHERE <条件表达式>]
[GROUP BY <列名1> [HAVING <条件表达式>]]
[ORDER BY <列名2> [ASC|DESC]];目标列表达式格式:
[<表名>.]*[<表名>.]<属性列名表达式>[, ...]— 含属性列、集函数、常量的算术运算
集函数格式:
COUNT|SUM|AVG|MAX|MIN ([DISTINCT|ALL] <列名>)COUNT ([DISTINCT|ALL] *)
条件表达式格式:
- 比较运算:
<属性列名> θ <常量/ANY|ALL(SELECT语句)> - 范围:
<属性列名> [NOT] BETWEEN <值> AND <值> - 集合:
<属性列名> [NOT] IN (<值列表> | SELECT语句) - 字符串:
<属性列名> [NOT] LIKE <匹配串> - 空值:
<属性列名> IS [NOT] NULL - 存在:
[NOT] EXISTS (SELECT语句) - 多重条件:
<条件> AND|OR <条件>
四、数据更新
4.1 插入数据
插入单个元组
INSERT INTO <表名> [(<属性列1> [, <属性列2> ...])]
VALUES (<常量1> [, <常量2> ...]);- INTO 未指定属性列 → 完整元组,按表定义顺序。
- INTO 指定部分属性列 → 其余列取空值。
- VALUES 的值必须与 INTO 子句匹配(个数、类型)。
-- 插入完整元组
INSERT INTO Student VALUES ('95020', '陈冬', '男', 'IS', 18);
-- 插入部分属性(Grade 取空值)
INSERT INTO SC(Sno, Cno) VALUES ('95020', '1');插入子查询结果
INSERT INTO <表名> [(<属性列1> [, ...])] <子查询>;-- 先建表,再插入各系平均年龄
CREATE TABLE Deptage (
Sdept CHAR(15),
Avgage SMALLINT
);
INSERT INTO Deptage(Sdept, Avgage)
SELECT Sdept, AVG(Sage) FROM Student GROUP BY Sdept;DBMS 会检查插入是否破坏完整性约束(实体、参照、用户定义)。
4.2 修改数据
UPDATE <表名>
SET <列名> = <表达式> [, <列名> = <表达式> ...]
[WHERE <条件>];修改单个元组
UPDATE Student SET Sage = 22 WHERE Sno = '95001';修改多个元组
-- 所有学生年龄加1
UPDATE Student SET Sage = Sage + 1;
-- 信息系学生年龄加1
UPDATE Student SET Sage = Sage + 1 WHERE Sdept = 'IS';带子查询的修改
-- 将计算机科学系全体学生成绩置零
UPDATE SC
SET Grade = 0
WHERE 'CS' = (SELECT Sdept FROM Student WHERE Student.Sno = SC.Sno);DBMS 会检查修改是否破坏完整性约束(主码不允许修改,NOT NULL/UNIQUE/值域约束等)。
4.3 删除数据
DELETE FROM <表名> [WHERE <条件>];删除单个元组
DELETE FROM Student WHERE Sno = '95019';删除多个元组
-- 删除2号课程的所有选课记录
DELETE FROM SC WHERE Cno = '2';
-- 删除所有选课记录(表定义仍在)
DELETE FROM SC;带子查询的删除
-- 删除计算机科学系所有学生的选课记录
DELETE FROM SC
WHERE 'CS' = (SELECT Sdept FROM Student WHERE Student.Sno = SC.Sno);DBMS 会检查删除是否破坏参照完整性(可能拒绝或级联删除)。
五、空值的处理
5.1 空值的概念
空值表示"不知道"、"不存在"或"无意义"。产生方式:
-- 插入时指定 NULL
INSERT INTO SC(Sno, Cno, Grade) VALUES ('95004', '1', NULL);
-- 插入时缺省属性列
INSERT INTO SC(Sno, Cno) VALUES ('95004', '1');
-- 更新为空值
UPDATE Student SET Sdept = NULL WHERE Sno = '95004';5.2 空值的判断
SELECT * FROM Student
WHERE Sname IS NULL OR Ssex IS NULL OR Sage IS NULL OR Sdept IS NULL;必须用
IS NULL,不能用= NULL。
5.3 空值的约束
- NOT NULL 约束的列不能取空值
- 主码列不能取空值
5.4 空值的运算
- 算术运算:空值与任何值(包括空值)的算术运算结果为空值。
- 比较运算:空值与任何值的比较结果为 UNKNOWN(三值逻辑:TRUE / FALSE / UNKNOWN)。
-- 找出选修1号课程不及格的学生(不含缺考)
SELECT Sno FROM SC WHERE Grade < 60 AND Cno = '1';
-- 包含缺考学生
SELECT Sno FROM SC WHERE Grade < 60 AND Cno = '1'
UNION
SELECT Sno FROM SC WHERE Grade IS NULL AND Cno = '1';
-- 或
SELECT Sno FROM SC
WHERE Cno = '1' AND (Grade < 60 OR Grade IS NULL);六、视图
6.1 视图的特点
- 虚表:从基本表(或其他视图)导出,只存放定义,不存放数据。
- 基表数据变化时,视图查询结果随之变化。
- 允许在视图上再定义视图。
6.2 定义视图
CREATE VIEW <视图名> [(<列名> [, <列名> ...])]
AS <子查询>
[WITH CHECK OPTION];- DBMS 执行 CREATE VIEW 时只存定义到数据字典,不执行 SELECT。
WITH CHECK OPTION:保证增删改操作满足视图定义中的谓词条件。
常见的视图形式
(1)行列子集视图
CREATE VIEW IS_Student AS
SELECT Sno, Sname, Sage
FROM Student
WHERE Sdept = 'IS'
WITH CHECK OPTION;(2)基于多个基表的视图
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';(3)基于视图的视图
CREATE VIEW IS_S2 AS
SELECT Sno, Sname, Grade
FROM IS_S1
WHERE Grade >= 90;(4)带表达式的视图
CREATE VIEW BT_S(Sno, Sname, Sbirth) AS
SELECT Sno, Sname, 2025 - Sage
FROM Student;(5)分组视图(通常不可更新)
CREATE VIEW S_G(Sno, Gavg) AS
SELECT Sno, AVG(Grade)
FROM SC
GROUP BY Sno;6.3 删除视图
DROP VIEW <视图名> [CASCADE];- CASCADE 级联删除由该视图导出的所有视图。
- 删除基表时,导出的视图定义必须用 DROP VIEW 显式删除。
6.4 查询视图
DBMS 实现视图查询的两种方法:
- 视图消解法:将视图定义中的子查询与用户查询结合,转换为对基本表的查询。
- 实体化法:执行视图定义生成临时表,再查询临时表,查询完毕删除。
-- 例:在信息系学生视图中查年龄小于20岁的学生
-- 视图消解后等价于:
SELECT Sno, Sage
FROM Student
WHERE Sdept = 'IS' AND Sage < 20;视图消解法的局限:某些包含分组和集函数的视图不能直接消解(如 HAVING 条件在消解后会出现在 WHERE 中,导致错误)。这种情况下需改用派生表查询。
6.5 更新视图
对视图的更新最终转换为对基本表的更新。有 WITH CHECK OPTION 时 DBMS 会自动添加条件检查。
-- 更新视图中的姓名
UPDATE IS_Student SET Sname = '刘辰' WHERE Sno = '95002';
-- 转换为:UPDATE Student SET Sname = '刘辰' WHERE Sno = '95002' AND Sdept = 'IS';
-- 向视图插入记录
INSERT INTO IS_Student VALUES('95029', '赵新', 20);
-- 转换为:INSERT INTO Student(Sno, Sname, Sage, Sdept) VALUES('95029', '赵新', 20, 'IS');
-- 删除视图中的记录
DELETE FROM IS_Student WHERE Sno = '95029';
-- 转换为:DELETE FROM Student WHERE Sno = '95029' AND Sdept = 'IS';视图更新的限制
以下类型的视图通常不允许更新:
- 由两个以上基本表导出的视图
- 视图字段来自字段表达式或常数(不允许 INSERT 和 UPDATE,允许 DELETE)
- 视图字段来自集函数
- 视图定义中含有 GROUP BY
- 视图定义中含有 DISTINCT
- 视图定义中有嵌套查询,且内层 FROM 中涉及的表也是导出该视图的基本表
- 不可更新视图上定义的视图
一般地,行列子集视图是可更新的。
6.6 视图的作用
- 以多种角度看待同一数据 — 不同用户可定义不同视图,适应共享需求。
- 提供一定程度的逻辑独立性 — 数据库重构时可通过视图保持外模式不变,用户程序无需修改。
- 对机密数据提供安全保护 — 不同用户看到不同的数据子集;配合 WITH CHECK OPTION 可限制操作时间。
- 简化用户操作 — 将多表连接、复杂嵌套查询、导出属性封装为视图,用户直接查询视图即可。
本章小结
本章围绕 SQL 标准语言展开,重点掌握 SQL 的数据定义、数据查询、数据更新、空值处理和视图机制。
关键内容
- SQL 概述:SQL 集 DDL、DML、DCL 于一体,具有高度非过程化、面向集合和使用方式统一等特点。
- 数据定义:掌握模式、基本表、索引和数据字典的定义、修改与删除。
- 数据查询:掌握
SELECT的完整结构,以及单表查询、连接查询、嵌套查询、集合查询和派生表查询。 - 数据更新:掌握
INSERT、UPDATE、DELETE的基本用法及其完整性检查。 - 空值处理:理解空值含义,使用
IS NULL/IS NOT NULL判断空值,注意空值参与运算后的不确定结果。 - 视图机制:掌握视图定义、删除、查询、更新限制及其在逻辑独立性、安全保护和简化查询中的作用。
核心要点
- SQL 查询的核心是
SELECT-FROM-WHERE,复杂查询通常由分组、连接、嵌套和集合运算组合而成。 - WHERE 作用于元组筛选,HAVING 作用于分组筛选,二者使用场景不同。
- 视图是虚表,通常只保存定义;行列子集视图一般可更新,含分组、集函数或多表复杂派生的视图通常不可更新。
关键术语
SQL, DDL, DML, DCL, SELECT, JOIN, GROUP BY, HAVING, 子查询, UNION, INTERSECT, EXCEPT, NULL, 视图, WITH CHECK OPTION。