【数据库-复试】sql语句综合练习
作业题1:利用订货管理数据库中表数据,用SQL完成查询。
答案
1. 检索在北京的供应商的名称
直接过滤
SELECT 供应商名 FROM 供应商 WHERE 地址 = '北京';
2. 检索向供应商 S3 发过订购单的职工的职工号和仓库号
方式一:IN 子查询
SELECT 职工号, 仓库号
FROM 职工
WHERE 职工号 IN (SELECT 职工号 FROM 订购单 WHERE 供应商号 = 'S3');
方式二:JOIN 关联
SELECT DISTINCT z.职工号, z.仓库号
FROM 职工 z
JOIN 订购单 d ON z.职工号 = d.职工号
WHERE d.供应商号 = 'S3';
3. 检索和职工 E1、E3 都有联系的北京供应商信息
(错题,有详解)
方式一:子查询 + GROUP BY
SELECT *
FROM 供应商
WHERE 地址 = '北京'
AND 供应商号 IN (
SELECT 供应商号
FROM 订购单
WHERE 职工号 IN ('E1', 'E3')
GROUP BY 供应商号
HAVING COUNT(DISTINCT 职工号) = 2
);
方式二:两次 JOIN
SELECT s.*
FROM 供应商 s
JOIN 订购单 d1 ON s.供应商号 = d1.供应商号 AND d1.职工号 = 'E1'
JOIN 订购单 d2 ON s.供应商号 = d2.供应商号 AND d2.职工号 = 'E3'
WHERE s.地址 = '北京';
最简写法 (满足题意且最简洁)
SELECT * FROM 供应商
WHERE 地址 = '北京'
AND 供应商号 IN (SELECT 供应商号 FROM 订购单 WHERE 职工号 = 'E1')
AND 供应商号 IN (SELECT 供应商号 FROM 订购单 WHERE 职工号 = 'E3');
4. 检索向 S4 供应商发出订购单的仓库所在城市
方式一:多层子查询
SELECT DISTINCT 城市
FROM 仓库
WHERE 仓库号 IN (
SELECT 仓库号
FROM 职工
WHERE 职工号 IN (
SELECT 职工号 FROM 订购单 WHERE 供应商号 = 'S4'
)
);
方式二:JOIN 关联
SELECT DISTINCT 仓库.城市
FROM 仓库
JOIN 职工 ON 仓库.仓库号 = 职工.仓库号
JOIN 订购单 ON 职工.职工号 = 订购单.职工号
WHERE 订购单.供应商号 = 'S4';
5. 检索工资多于 1230 元职工向北京供应商发的订购单号
方式一:子查询嵌套
SELECT 订购单号
FROM 订购单
WHERE 职工号 IN (SELECT 职工号 FROM 职工 WHERE 工资 > 1230)
AND 供应商号 IN (SELECT 供应商号 FROM 供应商 WHERE 地址 = '北京');
方式二:JOIN 关联
SELECT d.订购单号
FROM 订购单 d
JOIN 职工 z ON d.职工号 = z.职工号 AND z.工资 > 1230
JOIN 供应商 s ON d.供应商号 = s.供应商号 AND s.地址 = '北京';
6. 检索所有仓库的平均面积
唯一标准写法
SELECT AVG(面积) AS 平均面积 FROM 仓库;
7. 检索每个仓库中工资多于 1220 元的职工个数
标准写法
SELECT 仓库号, COUNT(职工号) AS 职工个数
FROM 职工
WHERE 工资 > 1220
GROUP BY 仓库号;
8. 检索工资低于本仓库平均工资的职工信息
(错题,有详解)
方式一:子查询 JOIN
SELECT t.*
FROM 职工 t
JOIN (
SELECT 仓库号, AVG(工资) AS avg_salary
FROM 职工
GROUP BY 仓库号
) s ON t.仓库号 = s.仓库号
WHERE t.工资 < s.avg_salary;
方式二:窗口函数(需数据库支持,如 MySQL 8.0+)
SELECT 仓库号, 职工号, 工资
FROM (
SELECT *, AVG(工资) OVER (PARTITION BY 仓库号) AS avg_salary
FROM 职工
) t
WHERE 工资 < avg_salary;
作业2:设教学数据库 Education 有三个关系
- 学生关系 S(SNO,SNAME,AGE,SEX,SDEPT)
- 学习关系 SC(SNO,CNO,GRADE)
- 课程关系 C(CNO,CNAME,CDEPT,TNAME)
查询问题:
1. 检索计算机系的全体学生的学号,姓名和性别;
2. 检索学习课程号为 C2 的学生学号与姓名;
3. 检索选修课程名为“DS”的学生学号与姓名;
4. 检索选修课程号为 C2 或 C4 的学生学号;
5. 检索至少选修课程号为 C2 和 C4 的学生学号;
6. 检索不学 C2 课的学生姓名和年龄;
7. 检索学习全部课程的学生姓名;
8. 查询所学课程包含学生 S3 所学课程的学生学号。
答案
(1)检索计算机系的全体学生的学号,姓名和性别
方法一:简单 WHERE 子句筛选
SELECT SNO, SNAME, SEX
FROM S
WHERE SDEPT = '计算机系';
解释:直接从学生关系表 S
中,通过 WHERE
子句筛选出所属系别为 “计算机系” 的记录,然后选择学号 SNO
、姓名 SNAME
和性别 SEX
列。
(2)检索学习课程号为 C2 的学生学号与姓名
方法一:通过 SC 表关联查询
SELECT S.SNO, S.SNAME
FROM S
JOIN SC ON S.SNO = SC.SNO
WHERE SC.CNO = 'C2';
方法二:子查询方式
SELECT SNO, SNAME
FROM S
WHERE SNO IN (
SELECT SNO
FROM SC
WHERE CNO = 'C2'
);
(3)检索选修课程名为 “DS” 的学生学号与姓名
方法一:三表连接查询
SELECT S.SNO, S.SNAME
FROM S
JOIN SC ON S.SNO = SC.SNO
JOIN C ON SC.CNO = C.CNO
WHERE C.CNAME = 'DS';
方法二:嵌套子查询
SELECT SNO, SNAME
FROM S
WHERE SNO IN (
SELECT SNO
FROM SC
WHERE CNO IN (
SELECT CNO
FROM C
WHERE CNAME = 'DS'
)
);
(4)检索选修课程号为 C2 或 C4 的学生学号
方法一:IN 操作符
SELECT DISTINCT SNO
FROM SC
WHERE CNO IN ('C2', 'C4');
从学习关系表 SC
中,使用 IN
操作符筛选出课程号为 C2
或者 C4
的记录,再通过 DISTINCT
去除重复的学号。
方法二:OR 逻辑表达式
SELECT DISTINCT SNO
FROM SC
WHERE CNO = 'C2' OR CNO = 'C4';
(5)检索至少选修课程号为 C2 和 C4 的学生学号
方法一:GROUP BY 和 HAVING 子句
SELECT SNO
FROM SC
WHERE CNO IN ('C2', 'C4')
GROUP BY SNO
HAVING COUNT(DISTINCT CNO) = 2;
解释:先通过 WHERE
子句筛选出课程号为 C2
或者 C4
的记录,然后按照学号 SNO
进行分组(GROUP BY
),最后使用 HAVING
子句筛选出分组中不同课程号数量为 2 的学号,即表示该学生同时选修了 C2
和 C4
课程。
方法二:自连接方式(较复杂)
SELECT DISTINCT s1.SNO
FROM SC s1
JOIN SC s2 ON s1.SNO = s2.SNO
WHERE s1.CNO = 'C2' AND s2.CNO = 'C4';
解释:将学习关系表 SC
进行自连接(相当于把 SC
表当作两个不同的表 s1
和 s2
进行连接),连接条件是两个表中的学号相等。然后在连接后的结果中,筛选出 s1
表中课程号为 C2
且 s2
表中课程号为 C4
的记录,最后通过 DISTINCT
去除重复的学号。
(6)检索不学 C2 课的学生姓名和年龄
方法一:NOT IN 子查询
SELECT SNAME, AGE
FROM S
WHERE SNO NOT IN (
SELECT SNO
FROM SC
WHERE CNO = 'C2'
);
方法二:NOT EXISTS 子查询
SELECT SNAME, AGE
FROM S s
WHERE NOT EXISTS (
SELECT 1
FROM SC sc
WHERE sc.SNO = s.SNO AND sc.CNO = 'C2'
);
解释:对于学生关系表 S
中的每一条记录(用 s
表示),通过 NOT EXISTS
子查询去检查在学习关系表 SC
中是否不存在满足学号相等且课程号为 C2
的记录。如果不存在这样的记录,那么该学生就是没有学习 C2
课程的,将其姓名和年龄选出来。
(7)检索学习全部课程的学生姓名
(错题,看详解)
方法一:使用 NOT EXISTS 和双重子查询
SELECT SNAME
FROM S s
WHERE NOT EXISTS (
SELECT 1
FROM C c
WHERE NOT EXISTS (
SELECT 1
FROM SC sc
WHERE sc.SNO = s.SNO AND sc.CNO = c.CNO
)
);
方法二:使用 COUNT 和分组(假设课程表中课程数量已知为 N )
SELECT SNAME
FROM S s
JOIN SC sc ON s.SNO = sc.SNO
GROUP BY s.SNO, s.SNAME
HAVING COUNT(DISTINCT sc.CNO) = N;
解释:先将学生关系表 S
和学习关系表 SC
基于学号连接,然后按照学生学号和姓名进行分组(GROUP BY
),对于每个分组,统计不同课程号的数量,当数量等于课程总数 N
时,说明该学生学习了全部课程,将其姓名选出来。但这种方法需要提前知道课程总数,通用性稍差。
(8)查询所学课程包含学生 S3 所学课程的学生学号
使用 NOT EXISTS 子查询
SELECT DISTINCT SNO
FROM SC s1
WHERE NOT EXISTS (
SELECT 1
FROM SC s2
WHERE s2.SNO = 'S3'
AND NOT EXISTS (
SELECT 1
FROM SC s3
WHERE s3.SNO = s1.SNO AND s3.CNO = s2.CNO
)
);
解释:对于学习关系表 SC
中的每个学生(用 s1
表示),通过最外层 NOT EXISTS
子查询判断。中间层子查询针对学生 S3
选修的每门课程(用 s2
表示)。最内层子查询检查对于某个学生 s1
是否存在 S3
选修的某门课程,而该学生没有选修的情况。如果不存在这样的情况,即表示该学生所学课程包含了 S3
所学课程,将其学号通过 DISTINCT
选出来。