山东大学 2020级数据库系统 实验二

What’s more

山东大学 2020级数据库系统 实验一
山东大学 2020级数据库系统 实验二
山东大学 2020级数据库系统 实验三
山东大学 2020级数据库系统 实验四
山东大学 2020级数据库系统 实验五
山东大学 2020级数据库系统 实验六
山东大学 2020级数据库系统 实验七
山东大学 2020级数据库系统 实验八、九

写在前面

做数据库实验一定要静得下心来,才能发现其中的错误然后进行改正。同时,如果发现 SQL 语句总是报错,“一定是你错了,只是不知道错在哪里”

其次,SQL 语句中较为复杂的点博主都进行了注释,希望大家一定要看懂思路后自己写一遍,而不是盲目的 Ctrl+C,Ctrl+V,切记切记!!

实验二

注意:实验二使用的表是:pub.student_course, pub.student, pub.course 哦,别搞错了!!

检索查询部分应该算是数据库中较为困难的一部分了,每道题我会先写出思路,同时在 SQL 语句中进行注释,希望能够看懂。

  • 2-1 找出没有选修任何课程的学生的学号、姓名(即没有选课记录的学生)
    思路:
    1. 找出有选课记录的同学的学号,不妨记为关系 A;
    2. 使用 not in 在 pub.student 中筛选不在 A 中有记录的学生的学号及姓名;
create view test2_01 as
	select sid, name
	from pub.student
	where sid not in
		(
			select sid			-- 选出有选课记录的同学的学号
			from pub.student_course
		)
  • 2-2 找出至少选修了学号为“200900130417”的学生所选修的一门课的学生的学号、姓名(不包含这名同学)。
    思路 1:

    1. 选出该同学所选修了的课程号 cid,作为集合 A;
    2. 使用 = some 来对 pub.student_course 中每个同学的课程号 cid 进行筛选;
    3. 最后在 pub.student 中选出对应的学号 sid 及姓名 name

    思路 2:

    1. 选出该同学所选修了的课程号 cid,作为集合 A;
    2. 使用 in 根据 A 筛选出课程号在集合 A 中的同学对应的学号及姓名;
    3. 集合运算 minus(except) 减去该同学的学号及姓名
---------------思路 1---------------------
create view test2_02 as
	select sid, name
	from pub.student
	where sid in
		(
			select sid
			from pub.student_course
			where sid <> '200900130417' 		-- 不包含这名同学
			and cid = some				-- 课程号至少有一个与 A 中元素相同的才满足条件
				(
					select cid
					from pub.student_course
					where sid = '200900130417'
				)
		)
---------------思路 2---------------------
create view test2_02 as
	(
		select sid, name					-- 选出对应的学生的学号及姓名(此时包括学号为‘200900130417’的这名同学)
		from pub.student
		where sid in
			(
				select sid					-- 选出至少有一个 cid 在 A 中的同学的 sid
				from pub.student_course
				where cid in
				(
					select cid				-- 集合 A
					from pub.student_course
					where sid = '200900130417'
				)
			)
	)
	except--(Oracle 中是 minus 哦~~)		-- 除去这名同学的信息
	(
		select sid, name
		from pub.student
		where sid = '200900130417'
	)
  • 2-3 找出至少选修了一门其先行课程号为“300002”号课程的学生的学号、姓名
    思路:

    1. 根据先行课程号 fcid 在 pub.course 中选出对应的选修课程号 cid,记为集合 A;
    2. 在 pub.student_course 中根据集合 A 筛选出所选课程在 A 中的学生的 sid,记为集合 B;
    3. 在 pub.student 中根据集合 B 筛选出对应学生的 sid 和 name;

    第 2 步以及第 3 步可以使用 natural join 进行连接。

create view test2_03 as
	select sid, name
	from pub.student natural join pub.student_course
	where cid in
		(
			select cid
			from pub.course
			where fcid = '300002'
		)
  • 2-4 找出选修了“操作系统”并且也选修了“数据结构”,但是没有选修“程序设计语言”的学生的学号、 姓名
    思路:

    1. 找出选修了“操作系统”的学生的 sid,记为集合 A;
    2. 找出选修了“数据结构”的学生的 sid,记为集合 B;
    3. 找出没有选修“程序设计语言”的学生的 sid,记为集合 C;
    4. (A intersect B) except C 即可;

    (注意 Oracle 中将 except 换为 minus 就行)

create view test2_04 as
	select sid, name
	from pub.student
	where sid in
		(
			(
				select sid
				from pub.student_course
				where cid in
					(
						select cid
						from pub.course
						where name = '操作系统'
					)
			)
			intersect
			(
				select sid
				from pub.student_course
				where cid in
					(
						select cid
						from pub.course
						where name = '数据结构'
					)
			)
			except(Oracle 中是 minus 哦~~)
			(
				select sid
				from pub.student_course
				where cid in
					(
						select cid
						from pub,course
						where name = '程序设计语言'
					)
			)
		)
  • 2-5 查询 20 岁的所有有选课的学生的学号、姓名、平均成绩(avg_score,此为列名,下同)(平均成绩四舍 五入到个位)、总成绩(sum_score)
    思路:

    1. 使用 natural join 将 pub.student 和 pub.student_course 连接起来;
    2. 计算对应的信息即可;

    注意:函数 round(number, precision) 可以将 number 按照 precision 精确到对应位。round(avg(number), 0) 即代表将 score 的平均值保留到整数位。

create view test2_05 as
	select sid, name, round(avg(score), 0) avg_score, sum(score) sum_score
	from pub.student natural join pub.student_course
	where age = 20
	group by sid, name
  • 2-6 查询所有课的最高成绩、最高成绩人数,test2_06 有四个列:课程号 cid、课程名称 name、最高成绩 max_score、最高成绩人数 max_score_count(一个学生同一门课成绩都是第一,只计一次,需要考虑刷 成绩情况,一个同学选了一个课程多次,两次都是最高分。如果结果错一行,可能就是没有考虑这种情 况,这里是为了考核“去重复计数”知识点的)。如果没有学生选课,则最高成绩为空值,最高成绩人 数为零
    此题应该算是实验二中最难的一道了。希望我能将思路说清楚
    主要用到的表如下:

    1. t1:得出 cid,name
    2. t2:得出max_score
    3. t5:得出max_score_count

    思路:
    1. t1, t2 表的求解过程不用多说,简单的分组查询(查询时一定要加上 cid ,因为后面要将它们连接起来);
    2. 由于需要数最高分的人数,因此首先要在查询 max_score_count 部分的 from 子句中计算出 max_score (t4 表);
    3. 然后针对 score 对学生进行筛选,让 t3.cid = t4.cid 以及 t3.score = t4.max_score,得出每门课的最高分的学生的信息;
    4. 再进行分组后 count 得到 max_score_count;
    5. 最后将这些表的信息连起来 t1.cid = t2.cid and t2.cid = t5.cid;

create view test2_06 as
	select t1.cid, name, max_score, max_score_count
	from 
	(
		select cid, name
		from pub.course
		group by cid, name
	) t1,
	(
		select cid, max(score) max_score
		from pub.student_course
		group by cid
	) t2,
	(
		select t3.cid, count(distinct sid) max_score_count
		from pub.student_course t3,
		(
			select cid, max(score) max_score
			from pub.student_course
			group by cid
		) t4
		where t3.cid = t4.cid
		and t3.score = t4.max_score
		group by t3.cid
	) t5
	where t1.cid = t2.cid
	and t2.cid = t5.cid
  • 2-7 查询所有不姓张、不姓李、也不姓王的学生的学号 sid、姓名 name
    思路 1:

    1. 直接使用函数 substr(string, p1, p2),该函数可以针对取得字符串相对应部分的字符。如 substr(name, 0, 1) 就可以取得学生姓名 name 的第一个字符,即为姓。
    2. 然后接着判断取得的字符串是否是“张”、“李”、“王”即可。

    思路 2:

    1. 使用 not like 子句对字符串进行处理即可;
------------------思路 1-------------------
create view test2_07 as 
	select sid, name
	from pub.student
	where substr(name, 0, 1) <> '张'
	and substr(name, 0, 1) <> '李'
	and substr(name, 0, 1) <> '王'
------------------思路 2-------------------
create view test2_07 as
	select sid, name
	from pub.student
	where name not like '张%'
	and name not like '李%'
	and name not like '王%'
  • 2-8 查询学生表中每一个姓氏及其人数(不考虑复姓),test2_08 有两个列:second_name、p_count
    思路:
    1. 使用 substr 函数来取得学生的姓,然后进行 count 即可;
create view test2_08 as
	select substr(name, 0, 1) second_name, count(*) p_count
	from pub.student
	group by substr(name, 0, 1)
  • 2-9 查询选修了 300003 号课程的学生的 sid、name、score
    思路:
    1. 使用 natural join 将 pub.student 和 pub.student_course 进行连接;
    2. 从中寻找对应的 sid, name 以及 score;
create view test2_09 as
	select sid, name, score
	from pub.student natural join pub.student_course
	where cid = '300003'
  • 2-10 找出同一个同学同一门课程有两次或以上不及格的所有学生的学号、姓名(即一门课程需要补考两次 或以上的学生的学号、姓名)
    思路 1:

    1. 先选出 score < 60 的学生,并将此时的 pub.student_course 记为 S;
    2. 然后将子查询中的 pub.student_course 记为 T,在 S.cid = T.cid, T.score < 60 的条件下,查找是否还存在记录;
    3. 若存在,则满足条件;不满足则不输出;

    思路 2:

    1. 选出分数 < 60 的学生的 sid 以及 cid,并按照这两个标准对 pub.student_course进行分组;
    2. 在分组的基础上使用 count 来数出每一组的人数。若人数 > 2,则满足条件,记录其 sid;反之则不记录;
----------------思路 1-----------------
--思路 1有点缺陷,建议思路 2--
create view test2_10 as
	select sid, name
	from pub.student
	where sid in
		(
			select sid
			from pub.student_course S
			where score < 60
			and exists
				(
					select sid
					from pub.student_course T
					where S.cid = T.cid
					and T.score < 60
					and S.score <> T.score
				)
		)
----------------思路 2-----------------
create view test2_10 as
	select sid, name
	from pub.student
	where sid in
		(
			select sid
			from 
			(
				select sid, cid, count(*) s_count
				from pub.student_course
				where score < 60
				group by sid, cid
			)
			where s_count >= 2
		)

再次强调:一定是看懂思路之后自己实践哈~~
有问题还请斧正!

请添加图片描述

你可能感兴趣的:(数据库,数据库,oracle)