Skip to content

关系数据库标准语言 SQL

一、SQL 概述

1.1 什么是 SQL

SQL(Structured Query Language,结构化查询语言) 是关系数据库的标准语言,是一个通用的、功能极强的关系数据库语言。

1.2 SQL 标准的演进

标准页数发布日期
SQL/861986.10
SQL/89 (FIPS 127-1)120 页1989 年
SQL/92622 页1992 年
SQL991700 页1999 年
SQL20033600 页2003 年
SQL20083777 页2006 年
SQL20113817 页2010 年
SQL20164035 页2016 年
SQL20232023 年

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

1.3 SQL 的主要特点

  1. 功能综合,风格统一 — 集 DDL(数据定义语言)、DML(数据操纵语言)、DCL(数据控制语言)于一体,可独立完成数据库全生命周期活动。
  2. 高度非过程化 — 用户只需提出"做什么",无需指定存取路径,由系统自动完成。
  3. 面向集合的操作方式 — 操作对象、查找结果、增删改操作的对象都是元组的集合。
  4. 以统一语法提供多种使用方式 — 既可作为独立语言用于联机交互,也可嵌入 C、C++、Java 等高级语言中使用。
  5. 语言简洁,易学易用

1.4 SQL 的基本概念

SQL 支持关系数据库的三级模式结构:

  • 基本表:本身独立存在的表,一个关系对应一个基本表,一个(或多个)基本表对应一个存储文件。
  • 存储文件:逻辑结构和物理结构组成关系数据库的内模式,物理结构对用户透明。
  • 视图:从一个或几个基本表(或其他视图)导出的表,是虚表——只存放定义,不存放数据。

二、数据定义(DDL)

2.1 模式的定义与删除

定义模式

sql
CREATE SCHEMA [<模式名>] AUTHORIZATION <用户名>;
  • 未指定模式名则隐含为用户名。
  • 需 DBA 权限或 CREATE SCHEMA 权限。

示例:

sql
-- 为用户 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)
  );

删除模式

sql
DROP SCHEMA <模式名> <CASCADE|RESTRICT>;
  • CASCADE(级联):删除模式及其所有数据库对象。
  • RESTRICT(限制):仅当模式中无下属对象时才允许删除。
sql
-- 删除模式 TEST 及其所有对象
DROP SCHEMA TEST CASCADE;

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

定义基本表

sql
CREATE TABLE <表名> (
  <列名> <数据类型> [列级完整性约束],
  [<列名> <数据类型> [列级完整性约束], ...]
  [, 表级完整性约束]
);

常用完整性约束:

  • PRIMARY KEY — 主码约束
  • UNIQUE — 唯一性约束
  • NOT NULL — 非空约束
  • FOREIGN KEY ... REFERENCES ... — 参照完整性约束

PRIMARY KEY 与 UNIQUE 的区别:一个表只能有一个 PRIMARY KEY,但可以有多个 UNIQUE;PRIMARY KEY 隐含 NOT NULL。

示例:

sql
-- 学生表
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)
);

模式与表

基本表属于某个模式。定义所属模式的方式:

  1. 在表名中显式指定:CREATE TABLE "S-T".Student(...)
  2. 在 CREATE SCHEMA 中同时创建
  3. 设置搜索路径:
sql
SET search_path TO "S-T", PUBLIC;
CREATE TABLE Student (...);  -- 归属于 S-T 模式

查看搜索路径:SHOW search_path;

修改基本表

sql
ALTER TABLE <表名>
  [ADD [COLUMN] <新列名> <数据类型> [完整性约束]]
  [ADD <表级完整性约束>]
  [DROP [COLUMN] <列名> [CASCADE|RESTRICT]]
  [DROP CONSTRAINT <完整性约束名> [CASCADE|RESTRICT]]
  [RENAME COLUMN <列名> TO <新列名>]
  [ALTER COLUMN <列名> TYPE <数据类型>];

示例:

sql
-- 增加入学时间列(新列默认取空值)
ALTER TABLE Student ADD Scome DATE;

-- 修改年龄数据类型
ALTER TABLE Student ALTER COLUMN Sage TYPE SMALLINT;

-- 增加课程名称唯一约束
ALTER TABLE Course ADD UNIQUE(Cname);

删除基本表

sql
DROP TABLE <表名> [RESTRICT|CASCADE];
  • RESTRICT:有限制删除(有依赖对象时拒绝)。
  • CASCADE:级联删除(连同索引、视图、触发器等一起删除)。
sql
DROP TABLE Student CASCADE;
-- 注意:视图 IS_Student 也被自动删除

2.3 索引的建立与删除

索引的作用

  • 加快查询速度的重要手段。
  • 常见索引类型:B+ 树索引、Hash 索引、位图索引。
  • B+ 树具有动态平衡优点;Hash 索引查找速度快。
  • DBA 或表属主可建立索引;DBMS 自动维护并选择索引。

建立索引

sql
CREATE [UNIQUE] [CLUSTER] INDEX <索引名>
  ON <表名>(<列名> [<次序>] [, <列名> [<次序>] ...]);
  • UNIQUE:唯一索引,每个索引值对应唯一数据记录。
  • CLUSTER:聚簇索引,索引项顺序与物理记录顺序一致。
  • 次序:ASC(升序,默认),DESC(降序)。

示例:

sql
-- 学号升序唯一索引
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);

聚簇索引适用范围:很少对基表进行增删操作,很少对变长列进行修改操作。

修改索引

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

删除索引

sql
DROP INDEX <索引名>;

2.4 数据字典

数据字典是 RDBMS 内部的系统表集合,记录了所有定义信息:

  • 关系模式定义、视图定义、索引定义
  • 完整性约束定义
  • 各类用户的数据库操作权限
  • 统计信息等

执行 DDL 语句实际上就是在更新数据字典。

三、数据查询(SELECT)

3.1 查询语句完整格式

sql
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 单表查询

一、选择若干列

sql
-- 查询指定列
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

sql
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

示例:

sql
-- 比较大小
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;

三、对查询结果排序

sql
-- 按成绩降序
SELECT Sno, Grade FROM SC WHERE Cno = '3' ORDER BY Grade DESC;

-- 多列排序:先按系号升序,同一系按年龄降序
SELECT * FROM Student ORDER BY Sdept, Sage DESC;

四、集函数

sql
COUNT([DISTINCT|ALL] *)       -- 计数
COUNT([DISTINCT|ALL] <列名>)
SUM([DISTINCT|ALL] <列名>)    -- 求和
AVG([DISTINCT|ALL] <列名>)    -- 平均值
MAX([DISTINCT|ALL] <列名>)    -- 最大值
MIN([DISTINCT|ALL] <列名>)    -- 最小值

示例:

sql
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号课程最高分

五、对查询结果分组

sql
-- 求各课程号及选课人数
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 子句

sql
-- 查询选修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 连接查询

连接查询同时涉及多个表。

连接条件格式:

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

连接操作执行算法:

  1. 嵌套循环法(NESTED-LOOP):外层表逐行扫描,对内层表全表扫描匹配。
  2. 排序合并法(SORT-MERGE):先按连接属性排序,再合并扫描(常用于等值连接)。
  3. 索引连接法(INDEX-JOIN):对表2按连接字段建索引,表1每行通过索引查找。

等值连接与非等值连接

sql
-- 等值连接:查询每个学生及其选修课程情况
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;

自身连接

表与其自身连接,需起别名。

sql
-- 查询每一门课的间接先修课
SELECT FIRST.Cno, SECOND.Cpno
FROM Course FIRST, Course SECOND
WHERE FIRST.Cpno = SECOND.Cno
  AND SECOND.Cpno IS NOT NULL;

外连接

保留不满足连接条件的元组。

sql
-- 左外连接:查询每个学生及其选修课程情况(含未选课学生)
SELECT Student.Sno, Sname, Ssex, Sage, Sdept, Cno, Grade
FROM Student LEFT OUTER JOIN SC ON (Student.Sno = SC.Sno);
  • 左外连接:列出左边关系的所有元组
  • 右外连接:列出右边关系的所有元组

复合条件连接

sql
-- 查询选修2号课程且成绩90分以上的学生学号、姓名
SELECT Student.Sno, Sname
FROM Student, SC
WHERE Student.Sno = SC.Sno         -- 连接谓词
  AND SC.Cno = '2'                 -- 限定条件
  AND SC.Grade > 90;               -- 限定条件

多表连接

sql
-- 查询每个学生的学号、姓名、选修的课程名及成绩
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 谓词的子查询

sql
-- 查询与"刘晨"在同一个系学习的学生
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 = '信息系统'
  )
);

带有比较运算符的子查询

子查询返回单值时使用。

sql
SELECT Sno, Sname, Sdept
FROM Student
WHERE Sdept = (
  SELECT Sdept
  FROM Student
  WHERE Sname = '刘晨'
);

子查询必须跟在比较运算符之后。

带有 ANY 或 ALL 谓词的子查询

谓词含义
> ANY大于子查询结果中的某个值
> ALL大于子查询结果中的所有值
< ANY小于子查询结果中的某个值
< ALL小于子查询结果中的所有值
= ANY等于子查询结果中的某个值
!= ALL不等于子查询结果中的任何一个值
sql
-- 查询其他系中比信息系任意一个学生年龄小的学生
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 子查询的目标列通常用 *,因为只关心有没有返回值。
sql
-- 查询所有选修了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))

sql
-- 查询选修了全部课程的学生姓名
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 实现逻辑蕴涵:

sql
-- 查询至少选修了学生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)

sql
-- 查询计算机科学系的学生及年龄不大于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)

sql
-- 查询计算机科学系中年龄不大于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)

sql
-- 查询计算机科学系中年龄大于19岁的学生
SELECT * FROM Student WHERE Sdept = 'CS'
EXCEPT
SELECT * FROM Student WHERE Sage <= 19;

对集合操作结果的排序

ORDER BY 只能用于最终结果,且用数字指定排序属性:

sql
SELECT * FROM Student WHERE Sdept = 'CS'
UNION
SELECT * FROM Student WHERE Sage <= 19
ORDER BY 1;  -- 按第一列排序

3.6 基于派生表的查询

子查询出现在 FROM 子句中,生成临时派生表。

sql
-- 找出每个学生超过自己平均成绩的课程号
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 语句一般格式总结

sql
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] *)

条件表达式格式:

  1. 比较运算:<属性列名> θ <常量/ANY|ALL(SELECT语句)>
  2. 范围:<属性列名> [NOT] BETWEEN <值> AND <值>
  3. 集合:<属性列名> [NOT] IN (<值列表> | SELECT语句)
  4. 字符串:<属性列名> [NOT] LIKE <匹配串>
  5. 空值:<属性列名> IS [NOT] NULL
  6. 存在:[NOT] EXISTS (SELECT语句)
  7. 多重条件:<条件> AND|OR <条件>

四、数据更新

4.1 插入数据

插入单个元组

sql
INSERT INTO <表名> [(<属性列1> [, <属性列2> ...])]
VALUES (<常量1> [, <常量2> ...]);
  • INTO 未指定属性列 → 完整元组,按表定义顺序。
  • INTO 指定部分属性列 → 其余列取空值。
  • VALUES 的值必须与 INTO 子句匹配(个数、类型)。
sql
-- 插入完整元组
INSERT INTO Student VALUES ('95020', '陈冬', '男', 'IS', 18);

-- 插入部分属性(Grade 取空值)
INSERT INTO SC(Sno, Cno) VALUES ('95020', '1');

插入子查询结果

sql
INSERT INTO <表名> [(<属性列1> [, ...])] <子查询>;
sql
-- 先建表,再插入各系平均年龄
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 修改数据

sql
UPDATE <表名>
SET <列名> = <表达式> [, <列名> = <表达式> ...]
[WHERE <条件>];

修改单个元组

sql
UPDATE Student SET Sage = 22 WHERE Sno = '95001';

修改多个元组

sql
-- 所有学生年龄加1
UPDATE Student SET Sage = Sage + 1;

-- 信息系学生年龄加1
UPDATE Student SET Sage = Sage + 1 WHERE Sdept = 'IS';

带子查询的修改

sql
-- 将计算机科学系全体学生成绩置零
UPDATE SC
SET Grade = 0
WHERE 'CS' = (SELECT Sdept FROM Student WHERE Student.Sno = SC.Sno);

DBMS 会检查修改是否破坏完整性约束(主码不允许修改,NOT NULL/UNIQUE/值域约束等)。

4.3 删除数据

sql
DELETE FROM <表名> [WHERE <条件>];

删除单个元组

sql
DELETE FROM Student WHERE Sno = '95019';

删除多个元组

sql
-- 删除2号课程的所有选课记录
DELETE FROM SC WHERE Cno = '2';

-- 删除所有选课记录(表定义仍在)
DELETE FROM SC;

带子查询的删除

sql
-- 删除计算机科学系所有学生的选课记录
DELETE FROM SC
WHERE 'CS' = (SELECT Sdept FROM Student WHERE Student.Sno = SC.Sno);

DBMS 会检查删除是否破坏参照完整性(可能拒绝或级联删除)。

五、空值的处理

5.1 空值的概念

空值表示"不知道"、"不存在"或"无意义"。产生方式:

sql
-- 插入时指定 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 空值的判断

sql
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)。
sql
-- 找出选修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 定义视图

sql
CREATE VIEW <视图名> [(<列名> [, <列名> ...])]
AS <子查询>
[WITH CHECK OPTION];
  • DBMS 执行 CREATE VIEW 时只存定义到数据字典,不执行 SELECT。
  • WITH CHECK OPTION:保证增删改操作满足视图定义中的谓词条件。

常见的视图形式

(1)行列子集视图

sql
CREATE VIEW IS_Student AS
SELECT Sno, Sname, Sage
FROM Student
WHERE Sdept = 'IS'
WITH CHECK OPTION;

(2)基于多个基表的视图

sql
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)基于视图的视图

sql
CREATE VIEW IS_S2 AS
SELECT Sno, Sname, Grade
FROM IS_S1
WHERE Grade >= 90;

(4)带表达式的视图

sql
CREATE VIEW BT_S(Sno, Sname, Sbirth) AS
SELECT Sno, Sname, 2025 - Sage
FROM Student;

(5)分组视图(通常不可更新)

sql
CREATE VIEW S_G(Sno, Gavg) AS
SELECT Sno, AVG(Grade)
FROM SC
GROUP BY Sno;

6.3 删除视图

sql
DROP VIEW <视图名> [CASCADE];
  • CASCADE 级联删除由该视图导出的所有视图。
  • 删除基表时,导出的视图定义必须用 DROP VIEW 显式删除。

6.4 查询视图

DBMS 实现视图查询的两种方法:

  1. 视图消解法:将视图定义中的子查询与用户查询结合,转换为对基本表的查询。
  2. 实体化法:执行视图定义生成临时表,再查询临时表,查询完毕删除。
sql
-- 例:在信息系学生视图中查年龄小于20岁的学生
-- 视图消解后等价于:
SELECT Sno, Sage
FROM Student
WHERE Sdept = 'IS' AND Sage < 20;

视图消解法的局限:某些包含分组和集函数的视图不能直接消解(如 HAVING 条件在消解后会出现在 WHERE 中,导致错误)。这种情况下需改用派生表查询。

6.5 更新视图

对视图的更新最终转换为对基本表的更新。有 WITH CHECK OPTION 时 DBMS 会自动添加条件检查。

sql
-- 更新视图中的姓名
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';

视图更新的限制

以下类型的视图通常不允许更新

  1. 由两个以上基本表导出的视图
  2. 视图字段来自字段表达式或常数(不允许 INSERT 和 UPDATE,允许 DELETE)
  3. 视图字段来自集函数
  4. 视图定义中含有 GROUP BY
  5. 视图定义中含有 DISTINCT
  6. 视图定义中有嵌套查询,且内层 FROM 中涉及的表也是导出该视图的基本表
  7. 不可更新视图上定义的视图

一般地,行列子集视图是可更新的

6.6 视图的作用

  1. 以多种角度看待同一数据 — 不同用户可定义不同视图,适应共享需求。
  2. 提供一定程度的逻辑独立性 — 数据库重构时可通过视图保持外模式不变,用户程序无需修改。
  3. 对机密数据提供安全保护 — 不同用户看到不同的数据子集;配合 WITH CHECK OPTION 可限制操作时间。
  4. 简化用户操作 — 将多表连接、复杂嵌套查询、导出属性封装为视图,用户直接查询视图即可。

本章小结

本章围绕 SQL 标准语言展开,重点掌握 SQL 的数据定义、数据查询、数据更新、空值处理和视图机制。

关键内容

  • SQL 概述:SQL 集 DDL、DML、DCL 于一体,具有高度非过程化、面向集合和使用方式统一等特点。
  • 数据定义:掌握模式、基本表、索引和数据字典的定义、修改与删除。
  • 数据查询:掌握 SELECT 的完整结构,以及单表查询、连接查询、嵌套查询、集合查询和派生表查询。
  • 数据更新:掌握 INSERTUPDATEDELETE 的基本用法及其完整性检查。
  • 空值处理:理解空值含义,使用 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。