参考友情链接QAQ,写的很好:快速入门SQL
1. 模式
1.1 创建模式
sql
-- 为用户 WANG 定义一个 学生-课程 模式 "S-T"
CREATE SCHEMA "S-T" AUTORIZATION WANG;
-- 模式名默认为用户名 WANG
CREATE SCHEMA AUTORIZATION WANG;
1.2 删除模式
sql
-- CASCADE(级联):删除模式同时把该模式下所有数据库对象删除;
DROP SCHEMA TEST CASCADE;
-- RESTRICT(限制):如果该模式中定义了数据库对象,则拒绝执行;
-- RESTRICT 为缺省值;
DROP SCHEMA TEST RESTRICT;
2. 基本表
2.1 定义基本元素
数据类型
| 数据类型 | 表示内容 |
|---|---|
| CHAR(n) | 长度为n的字符型 |
| VARCHAR(n) | 最大长度为n的变长字符型 |
| NUMBER(n) | 长度为n的数字型 |
| INT | 长整型(4B) |
| SMALLINT | 短整型(4B) |
| BIGINT | 大整型(8B) |
| FLOAT(n) | 精度至少为n位的浮点数 |
| DATE | 日期,格式为YYYY-MM-DD |
| TIME | 时间,格式为HH:MM:SS |
列级完整性约束
| 约束条件 | 意义 |
|---|---|
| PRIMARY KEY | 主码(元素唯一不能重复):当只有一个主码时,可直接在对应的属性列标注 |
| NOT NULL | 非空:表示该属性列不能取空值 |
| UNIQUE | 唯一值:表示该属性列只能取唯一值 |
| CHECK | 检查:检查该列是否满足某个条件,比如CHECK(某属性>20) |
表级完整性约束
| 约束条件 | 意义 |
|---|---|
| PRIMARY KEY(列名1,...,列名n) | 多个主码:当主码由多个属性构成 时, 必须作为表级完整性定义 |
| FOREIGN KEY(列名1) REFERENCES 被参照表(列名1) | 外码:被参照的列必须是 PRIMARY KEY 或 UNIQUE 约束 的列, 本表所有值来源于被参照的列 |
2.2 创建基本表

sql
-- 当主码为单一属性,PRIMARY KEY 约束与 NOT NULL+UNIQUE 等效
CREATE TABLE Student
(Sno CHAR(5) NOT NULL UNIQUE,
Sname CHAR(20) UNIQUE,
Ssex CHAR(2),
Sage INT,
Sdept CHAR(15),
PRIMARY KEY(Sno));
-- 当主码为多属性,主码只能用 PRIMARY KEY 表级完整性约束
CREATE TABLE SC(
Sno CHAR(5),
Cno CHAR(3),
Grade INT,
PRIMARY KEY(Sno, Cno),
FOREIGN KEY(Sno) REFERENCES Student(Sno),
FOREIGN KEY(Cno) REFERENCES Course(Cno));
2.3 删除基本表
sql
-- 级联,删除基本表的同时,相关依赖对象(视图,索引)一起删除
DROP TABLE Student CASCADE;
-- 限制,欲删除的表不能被其他表约束,也不能存在依赖对象
DROP TABLE Student RESTRICT;
2.4 修改基本表

sql
-- 向 Student 表增加"入学时间"列,数据类型为 DATE
ALTER TABLE Student ADD entrance DATE;
-- 删除 entrance 列
ALTER TABLE Student DROP entrance;
-- 将年龄数据类型改为半字长整数
ALTER TABLE Student ALTER COULUMN Sage SMALLINT;
-- 删除/增加学生姓名必须取唯一值的约束
ALTER TABLE Student ADD/DROP UNIQUE(Sname);
2.5 索引
创建索引

sql
-- Student 表按学号升序建单一索引
CREATE UNIQUE INDEX Stusno ON Student(Sno ASC);
-- Student 表按年龄降序建普通索引
CREATE INDEX Stusage ON Student(Sage DESC);
-- Course 表按课程号升序建单一索引,ASC 为缺省值
CREATE UNIQUE INDEX Cuncno ON Course(Cno);
-- SC 表按学号升序和课程号降序建单一索引
CREATE UNIQUE INDEX SCno ON SC(Sno, Cno DESC);
-- 在 Student 表的 Sname 列上建立一个聚簇索引,按 Sname 升序
CREATE CLUSTER INDEX Stusname ON Student(Sname);
删除索引
sql
-- 删除 Student 表的 Stusname 索引
DROP INDEX Stusname;
3. 单表查询
- 需要用到的三张基本表:
- 学生表 Student(Sno,Sname,Ssex,Sage,Sdept):
|-------|-------|------|------|-------|
| Sno | Sname | Ssex | Sage | Sdept |
| 95001 | 李勇 | 男 | 20 | CS |
| 95002 | 刘晨 | 女 | 19 | IS |
| 95003 | 王敏 | 女 | 18 | MA |
| 95004 | 张立 | 男 | 19 | IS |
| ... | ... | ... | ... | ... |
- 课程表: Course(Cno,Cname,Cpno,Ccredit):
|-----|----------|------|---------|
| Cno | Cname | Cpno | Ccredit |
| 1 | 数据库 | 5 | 4 |
| 2 | 数学 | NULL | 2 |
| 3 | 信息系统 | 1 | 4 |
| 4 | 操作系统 | 6 | 3 |
| 5 | 数据结构 | 7 | 4 |
| 6 | 数据处理 | NULL | 2 |
| 7 | PASCAL语言 | 6 | 4 |
| ... | ... | ... | ... |
- 学生选课表: SC(Sno,Cno,Grade):
|-------|-----|-------|
| Sno | Cno | Grade |
| 95001 | 1 | 92 |
| 95001 | 2 | 65 |
| 95001 | 4 | 88 |
| 95002 | 2 | 90 |
| 95002 | 5 | 73 |
| ... | ... | ... |
3.0 SQL 查询逻辑执行顺序
sql
-- 从哪查 -> 过滤行 -> 分组 -> 过滤组 -> 选择数据 -> 选择排列顺序
FROM -> WHERE -> GROUP BY -> HAVING -> SELECT -> ORDER BY
3.1 SELECT
sql
-- 查询全体学生的学号和姓名
SELECT Sno, Sname
FROM Student;
-- 查询全体学生全部信息
SELECT *
FROM Student;
-- 表达式,列别名
SELECT Sname NAME, 'Year of Birth:' BIRTH, 2025-Sage BIRTHDAY, LOWER(Sdept) DEPARTMENT
FROM Student;
-- 去重:查询选修了课程的学生学号
SELECT DISDINCT Sno
FROM SC;
-- DISDINCT 作用于所有目标列:查询选修各门课程的各种成绩
SELECT DISDINCT Cno, Grade
FROM SC;
3.2 WHERE(Step1 过滤行)


- 比较运算符
sql
-- 查询计算机系全体学生信息
SELECT *
FROM Student
WHERE Sdept='CS';
-- 查询所有年龄在 20 岁以下的学生姓名和年龄
SELECT Sname, Sage
FROM Student
WHERE Sage < 20;
- 确定范围
sql
-- 查询所有年龄在 20~23 的学生姓名和年龄
SELECT Sname, Sage
FROM Student
WHERE Sage BETWEEN 20 AND 23;
- 确定集合
sql
-- 查询 IS,MA,CS 系学生姓名和性别
SELECT Sname, Ssex
FROM Student
WHERE Sdept IN ('IS','MA','CS');
-- 查询不是 IS,MA,CS 系学生姓名和性别
SELECT Sname, Ssex
FROM Student
WHERE Sdept NOT IN ('IS','MA','CS');
- 字符串匹配
sql
-- 通配符:% 代表任意长度,_ 代表单个字符
-- 转义字符:ESCAPE'<换码字符>'
-- 查询学号为 95001 的学生信息
SELECT *
FROM Student
WHERE Sno LIKE 95001;
-- 查询姓名中第二个字为"阳"的学生姓名和学号
SELECT Sname, Sno
FROM Student
WHERE Sname LIKE '_阳%';
-- 查询所有不姓刘的学生姓名
SELECT Sname
FROM Student
WHERE Sname NOT LIKE '刘%';
-- 查询以"DB_"开头,倒数第三个字符为 i 的课程信息
SELECT *
FROM Course
WHERE Cname LIKE 'DB\_%i__' ESCAPE'\';
- 涉及空值
sql
-- 使用谓词 IS NULL / IS NOT NULL
-- 查询缺少成绩的学生的学号和课程号
SELECT Sno, Cno
FROM SC
WHERE Grade IS NULL;
- 多重条件
sql
-- 优先级:NOT > AND > OR,可以用括号改变优先级
-- 查询计算机系 20 岁以下学生姓名
SELECT Sname
FROM Student
WHERE Sdept='CS' AND Grade<20;
-- 查询 IS,MA,CS 系学生姓名和性别
SELECT Sname, Ssex
FROM Student
WHERE Sdept='IS' OR Sdept='MA' OR Sdept='CS';
3.3 ORDER BY

sql
-- 查询选修了 3 号课程的学生的学号和成绩,结果按分数降序排列
SELECT Sno, Grade
FROM SC
WHERE Cno='3'
ORDER BY Grade DESC;
-- 查询全体学生信息,结果按系名升序排列,同系按年龄降序排列
SELECT *
FROM Student
ORDER BY Sdept, Sage DESC;
3.4 集函数
- 集函数可以出现在:SELECT、HAVING、ORDER BY、子查询中;
- 集函数不能出现在:WHERE、GROUP BY 中直接使用;
- 有无 GROUP BY 的区别:
- 有 GROUP BY:按组聚合,返回多行;
- 无 GROUP BY:全局聚合,返回单行;

sql
-- 查询学生总人数
SELECT COUNT(*)
FROM Student;
-- 查询选修了课程的总人数,用 DISTINCT 避免重复
SELECT COUNT(DISTINCT Sno)
FROM SC
-- 查询选修了 1 号课程的学生最高分
SELECT MAX(Grade)
FROM SC
WHERE Cno='1';
-- 查询学生 95002 选修课程的总学分
SELECT SUM(Ccredit)
FROM SC, Course
WHERE Sno='95002' AND SC.Sno=Course.Sno;
3.5 GROUP BY - HAVING(Step2 过滤组)

- 使用GROUP BY子句后,SELECT子句的列名列表中只能出现分组属性和集函数;
- WHERE 过滤行,HAVING 过滤组,先 WHERE 后 GROUP BY 再 HAVING;
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 -- 先筛成绩 90 分以上的记录
GROUP BY Sno HAVING COUNT(*)>3; -- 再筛课程数大于 3 的学生
4. 连接查询
4.1 两表连接
sql
-- 查询每个学生及其选修课信息
SELECT Student.*, SC.*
FROM Student, SC
WHERE Student.Sno=SC.Sno;
4.2 自身连接
sql
-- 查询每一门课的间接先修课,需要给表起别名
SELECT FIRST.Cno, SECOND.Cpno
FROM Course FIRST, Course SECOND
WHERE FIRST.Cpno=SECOND.Cno;
4.3 外连接

- 左外连接保留左表的所有记录,并尽可能地匹配右表中的记录 右外连接保留右表的所有记录,并尽可能地匹配左表中的记录
- 将悬浮元组保留在结果关系中,没有属性值的位置填上NULL;
- SELECT 列名 FROM 表名1 LEFT OUTER JOIN 表名2 ON(连接条件)
- SELECT 列名 FROM 表名1 RIGHT OUTER JOIN 表名2 ON(连接条件)
sql
-- 查询每个学生及其选修课程的情况
SELECT Student.*, Cno, Grade
FROM Student LEFT OUTER JOIN SC
ON Student.Sno=SC.Sno
-- 或
SELECT Student.*, Cno, Grade
FROM Student LEFT OUTER JOIN SC
USING(Sno)
4.4 复合条件连接
sql
-- 查询选修 2 号课程且成绩在 90 以上的学生的学号和姓名
SELECT Student.Sno, Student.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; -- 三张表需要两个连接条件
5. 嵌套查询(两层 for 循环)
- 子查询不能使用 ORDER BY 子句;
5.1 IN 子查询
sql
-- 查询与"刘晨"在同一个系学习的学生(学号,姓名,系别)
-- step1. 确定"刘晨"所在系名
SELECT Sdept
FROM Student
WHERE Sname='刘晨';
-- step2. 查找所有在 IS 系学习的学生
SELECT Sno, Sname, Sdept
FROM Student
WHERE Sdept='IS';
-- 嵌套
SELECT Sno, Sname, Sdept
FROM Student
WHERE Sdept IN
(SELECT Sdept
FROM Student
WHERE Sname='刘晨');
sql
-- 查询选修了课程名为"数据库"的学生学号和姓名
SELECT Sno, Sname
FROM Student
WHERE Sno IN -- 最后在 Student 查询
(SELECT Sno
FROM SC
WHERE Cno IN -- 再在 SC 找选修了该课程的学生学号
(SELECT Cno
FROM Course
WHERE Cname='数据库') -- 先在 Course 找"数据库"课程号
5.2 比较运算符子查询
- 若内层查询返回单值,可用比较运算符;
sql
-- 查询与"刘晨"在同一个系学习的学生
SELECT Sno, Sname, Sdept
FROM Student
WHERE Sdept =
(SELECT Sdept
FROM Student
WHERE Sname='刘晨');
- 同表关联要别名,先写逻辑后关联;
sql
-- 找出每个学生超过他选修课平均分的课程号
SELECT Sno, Cno
FROM SC X
WHERE Grade >=
(SELECT AVG(Grade)
FROM SC Y -- 相关子查询,取别名
WHERE Y.Sno=X.Sno)
5.3 ANY / ALL 子查询
- ANY:某些值;ALL:所有值;
sql
-- 查询其他系中比IS系某些学生年龄小的学生姓名和年龄
SELECT Sname, Sage
FROM Student
WHERE Sage < ANY(SELECT Sage
FROM Student
WHERE Sdept='IS') -- 先找出IS系中所有年龄构成的集合
AND Sdept<>'IS';
-- 也可以用集函数实现
SELECT Sname, Sage
FROM Student
WHERE Sage < (SELECT MAX(Sage)
FROM Student
WHERE Sdept='IS')
AND Sdept<>'IS';
-- 查询其他系中比IS系所有学生年龄小的学生姓名和年龄
SELECT Sname, Sage
FROM Student
WHERE Sage < ALL(SELECT Sage
FROM Student
WHERE Sdept='IS')
AND Sdept<>'IS';
-- 也可以用集函数实现
SELECT Sname, Sage
FROM Student
WHERE Sage < (SELECT MIN(Sage)
FROM Student
WHERE Sdept='IS')
AND Sdept<>'IS';
5.4 EXISTS 子查询
- EXISTS 为存在量词,对应 NOT EXISTS;
- EXISTS 子查询不返回任何数据,结果非空返回 true,结果为空返回 false;
sql
-- 查询所有选修了 1 号课程的学生姓名
SELECT Sname
FROM Student
WHERE EXISTS
(SELECT * -- 由EXISTS引出的子查询,其目标列表达式通常都用*
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')
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));
6. 集合查询
6.1 并操作(UNION)
sql
-- 查询CS系的学生和年龄不大于19的学生
SELECT *
FROM Student
WHERE Sdept='CS'
UNION
SELECT *
FROM Student
WHERE Sage<=19;
-- 法二:单表查询
SELECT *
FROM Student
WHERE Sdept='CS' OR Sage<=19;
6.2 交操作(INTERSECT)
sql
-- 查询选修了课程1和课程2的学生学号
SELECT Sno
FROM SC
WHERE Cno='1'
INTERSECT
SELECT Sno
FROM SC
WHERE Cno='2';
-- 法二:连接查询(SC自身连接)
SELECT Sno
FROM SC X, SC Y
WHERE X.Sno=Y.Sno AND X.Cno='1' AND Y.Cno='2';
6.3 查操作(EXCEPT)
sql
-- 查询CS系年龄不大于19岁的学生学号
SELECT Sno
FROM Student
WHERE Sdept='CS'
EXCEPT
SELECT Sno
FROM Student
WHERE Sage>19;
-- 法二:单表查询
SELECT Sno
FROM Student
WHERE Sdept='CS' AND Sage<=19;
7. 数据更新
7.1 INSERT 插入
插入单个元组

sql
-- 插入一个新学生信息
INSERT
INTO Student
VALUES('95020','陈冬','男',18,'IS');
-- 插入一个选课记录('95020','1')
INSERT
INTO SC(Sno, Cno)
VALUES('95020','1') -- Grade 取空
插入子查询结果

sql
-- 对每一个系,求学生平均年龄,并存入数据库
INERT
INTO Deptage(Sdept, Avgage)
SELECT Sdept, AVG(Sage)
FROM Student
GROUP BY Sdept;
7.2 UPDATE 修改

sql
-- 将学生95001的年龄改为22岁
UPDATE Student
SET Sage=22
WHERE Sno='95001';
-- 将IS系所有学生的年龄增加1岁
UPDATE Student
SET Sage=Sage+1
WHERE Sdept='IS';
-- 将CS系所有学生成绩清零
UPDATE SC
SET Grade=0
WHERE Sno IN
(SELECT Sno
FROM Student
WHERE Sdept='CS');
7.3 DELETE 删除

sql
-- 删除学号95019的学生记录
DELETE
FROM Student
WHERE Sno='95019';
-- 删除2号课程所有选课记录
DELETE
FROM SC
WHERE Cno='2';
-- 删除所有选课记录
DELETE
FROM SC
-- 删除CS系所有学生的选课记录
DELETE
FROM SC
WHERE Sno IN
(SELECT Sno
FROM Student
WHERE Sdept='CS');
8. 视图
8.1 建立视图
- 若添加WITH句,则表示对视图进行增删改时要满足子查询中的条件表达式;
- 行列子集视图:由单个基本表导出,仅去掉了基本表的某些行和某些列,但保留了主码;

sql
-- 建立IS系学生视图
CREATE VIEW IS_Student
AS
SELECT Sno,Sname,Sage
FROM Student
WHERE Sdept='IS';
WITH CHECK OPTION; -- 如果要求透过该视图的更新操作只涉及IS系学生,需要加这句
-- 更新后每一行都应满足 Sdept='IS' 条件
-- 建立信息系选修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';
-- 建立信息系选修1号课程且成绩在90以上的学生视图
CREATE VIEW IS_S2(Sno,Sname,Grade)
AS
SELECT Sno,Sname,Grade
FROM IS_S1
WHERE Grade>=90;
-- 定义一个反映学生出生年份的视图
CREATE VIEW BT_S(Sno,Sname,Sbirth)
AS
SELECT Sno,Sname,2025-Sage
FROM Student
-- 将学生学号,平均成绩定义为一个视图
CREATE VIEW S_G(Sno,Gavg)
AS
SELECT Sno,AVG(Grade)
FROM SC
GROUP BY Sno;
8.2 删除视图

sql
-- 如果在视图IS_S1上建立了视图IS_S2,需要先删除IS_S2
DROP VIEW IS_S2;
-- 或
DROP VIEW IS_S1 CASCADE;
8.3 查询和更新视图
- 视图定义后,对视图进行查询和更新的语句和语法与基本表相同;
- 视图的查询与更新最终都会转换为对基本表的查询和更新,这一过程也被称为视图消解;
- 一般来说,行列子集视图的查询和更新都可以顺利转换,其他则不一定;
sql
SELECT Sno,Sage
FROM Student
WHERE Sdept='IS' AND Sage<20;
-- 利用视图
SELECT Sno,Sage
FROM IS_Student
WHERE Sage<20;
