SQL练习3 数据查询 数据库系统概论第五版 王珊

查询格式

having 和 where 的区别:having可作用于中间表(如:group by生成的表),而where只作用于当前表

select [all|distinct] <目标表达式>
from <表名或视图名>
where <条件表达式>
group by <列名1> [having<条件表达式>]
order by <列名2>[ASC|DESC];

1.单表查询

1.1选中表中的若干列

-- 查询全体学生的学号与姓名
select Sname,Sname
from Student;

-- 查询全体学生的姓名,学号,所在系.
select Sname,Sno,Sdept
from Student

-- 查询全体学生的详细记录
select *from Student;
-- 等价于
select Sno,Sname,Ssex,Sage,Sdept
from Student;

-- **查询全体学生的姓名及其出生年份**
select Sname,2020-Sage /*查询结果第二列是一个算术表达式,列名为'2020-Sage',列值为2020减去对应的年龄*/
from Student

-- 查询全体学生的姓名,出生年份和所在的院系,要求用小写字母表示系名
select Sname,'Year of Birth',2014-Sage,LOWER(Sdept)
from Student;

-- 用户可以指定别名来改变查询结果的列名,对比上题
select Sname,'Year of Birth:' Birth,2014-Sage BirthDay ,LOWER(Sdept)
from Student;

1.2选择表中的若干元组

消除取值重复的行

-- 查询了选修了课程的学生学号
select Sno from SC; -- 有重复行
select distinct Sno from SC; -- 去重复行
/*如果没有指定distinct,则默认为all,即保留重复行*/
select all Sno from SC;

查询满足条件的元组

-- 1.比较大小
-- 查询计算机科学系的全体学生名单
select Sname 
from Student
where Sdept = 'CS';

-- 查询所有年龄在20岁以下的学生名单
select Sname,Sage
from Student
where Sage < 20;

-- 查询开始成绩不及格的学生的学号
select Distinct Sno
from SC
where Grade < 60;

-- 2.确定范围 between..and  in(,,,)
-- 查询年龄不在20-23岁之间的学生姓名.系别和年龄
select Sname,Sdept,Sage
from Student
where Sage not between 20 and 23;

-- 3.确定集合 IN(,,,)
-- 查询既不是计算机系(CS),数学系(MA)和信息系(IS)的学生姓名和性别
select Sname,Ssex
from Student
 where Sdept not in('CS','MA','IS');

-- 4.字符匹配 
/*格式:[not] like '<匹配字符串>' [escape<换码字符>]
通配符:%和_ 
%: 代表任意长度(长度可以是0)的字符串.例如:a%b表示以a开头,以b结尾的任意长度的字符串.如abc,ab,adidb都满足
_: 代表任意单个字符,例如:a_b表示三个字符,以a开头以b结束的字符串.如anb,afb
*/

-- 查询学号为201215121的学生详细情况
select * -- *表示全部
from Student
where Sno like '201215121';
-- 等价于
select * -- *表示全部
from Student
where Sno = '201215121';
/*如果like后面的匹配串不含通配符,则可以用=(等于)来取代like或用 !=或<>(不等于)来取代not like谓词*/

-- 查询所有姓刘的学生的姓名.学号和性别
select Sname,Sno,Ssex
from Student
where Sname like '刘%';

-- 查询行'欧阳'且全名为3个汉字的学生姓名
select Sname
from Studen
where Sname like '欧阳_';

-- 查询名字第二个字为"阳"的学生姓名和学号
select Sname,Sno
from Student
where Sname like "_阳%";

-- 查出所有不姓刘的学生姓名,学号和性别
select Sname,Sno,Ssex
from Student
where Sname not like '刘%';

/*如果查询的字符串本身就含有通配符%或_,这是就要用escape'<换码子符>'短语对通配符进行转义*/
-- 查询DB_Design课程的课程号和学分
select Cno,Ccredit
from Course
where Cname like 'DB\_Design' escape'\';  /*'\'表示换码字符.将_转化为普通文本*/

-- 查询DB_开头,并且倒数第三个字符为i的课程的详细情况
select *
from Course
where Cname like 'DB\_%i__' escape '\';

-- 5.设计空值的查询
-- 某些学生选课但是未参加考试分数为null,查询有选课记录单未参加考试的学生
select Sno,Cno
from SC
where Grade is null; /*is不能用等号代替*/

-- 查询所有有成绩的学生学号和课程号
select Sno,Cno
from SC
where Grade is not null;

-- 6.多重条件查询
-- 查询在计算机系并且年龄小于20岁的学生姓名
select Sname 
from Student
where Sdept ='CS' and Sage < 20;

-- in实际上是多个or运算符的缩写

1.3ORDER BY字句

用户可以用order by字句对查询结果按照一个或多个属性列的升序(ASC)或者降序(DESC),默认值为升序.

-- 查询选修了3号课程的学生学号及成绩,查询结果按分数的降序排列
select Sno,Grade
from SC
where Cno='3'
order by Grade DESC;
/*对于空值显示次序由具体的系统实现来决定*/

-- 查询全体学生的情况,查询结果按系号升序,同一系中的学生按年龄降序
select *
from Student
order by Sdept,Sage DESC;

1.4聚合函数

cout(*) 统计元组个数
count([distinct|all]<列名>) 统计一列中值的个数
sum,avg,max,min

-- 查询学生总人数
select count(*)
from Student;

-- 查询选修了课程的学生人数
select count(distinct Sno)
from SC;

-- 计算1号课程的平均成绩
select avg(grade)
from SC
where Cno = '1';

-- 查询选修了1号课程的最高分
select max(grade)
from  SC
where Cno=  '1';

-- 查询学生201215012选修课程的总学分数
select sum(credit)
from SC,Course
where Sno = '201215012' and SC.Cno=Course.Cno;

1.5Group BY字句

group by子句将查询结果按某一列或者多列的值分组,值相等的为一组
group by的是谁,count(*)统计的就是谁的记录数 :见下图
sc表
by sno
by cno

-- 求各个课程号及相应的选课人数
select Cno,count(Sno)  
from SC
group by Cno;

-- 查询选修了三门课程以上的学生学号
select Sno
from SC
group by Sno
having count(Cno) >= 3;

-- 查询各课程平均成绩大于等于90分的学生学号和平均成绩
select Sno,avg(Grade),Cno
from sc
group by Sno
having avg(Grade) >= 90;

-- 查学生平均成绩大于等于90分的课程
select Cno,avg(Grade)
from sc
group by Cno
having avg(Grade) >= 90;

2连接查询

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;

-- 查询选修了二号课程切成绩在90分元以上的所有学生的学号和姓名
select student.sno,sname
from student,sc
where student.sno=sc.sno and
	sc.cno='2'  and sc.grade>90;

2.2自身连接

一个表可以与自身连接查询.

-- 查询每一门课的间接先行课
select first.cno,second.cpno
from course first,course second
where first.cpno = second.cno and

2.3外连接

以student表为主体列出每个学生的基本情况及选课情况.若某个学生没有选课,仍把Student悬浮元组保存在结果关系中.

-- 左外连接
select student.sno,sname,Ssex,Sage,Sdept,Cno,Grade
from Student left outer join SC on(Student.Sno=SC.Sno);

-- 剔除了含有null的元素
select student.sno,sname,Ssex,Sage,Sdept,Cno,Grade
from Student  join SC on(Student.Sno=SC.Sno);

3嵌套查询

在SQL语句中,一个select-from-where称为一个查询块.将一个查询块嵌套在另一个查询快的where语句中称为嵌套查询
上层的查询块称为 外层查询或者父查询,下层查询称为内层查询子查询

3.1带有IN谓词的子查询

a.不相关子查询

-- 查询选修了2号课程的学生姓名
select Sname /*外层查询*/
from student 
where Sno in
	(select Sno /*内层查询*/
	from SC
	where Cno ='2');

-- 查询选修了2号课程的学生姓名
select Sname 
from student,SC
where Student.Sno=Sc.Sno and Sc.Cno='2';

-- 查询与'刘晨'在同一个系学习的学生
1 确定'刘晨'所在系名
select Sdept
from student
where Sname = '刘晨';
2 查找所有在CS系的学生
select Sno,Sname,Sdept
from Student
where Sdept = 'CS';

3 合并1 2
select Sno,Sname,Sdept
from Student
where Sdept in
	(select Sdept 
	from Student
	where Sname='刘晨');

-- 也可用自身连接来完成
select s1.Sno,s1.Sname,s1.Sdept
from Student s1,Student s2
where s1.Sdept=s2.Sdept and
s2.Sname='刘晨';

-- 查询选修了课程名为"信息系统"的学生学号和姓名
select Sno,Sname -- ③最后在Student关系中取出Sno和Sname
from Student
where Sno in
	(select Sno -- ②然后在课程表中找出选修了3号课程的学号
	 from SC
	 where Cno in
		(select Cno -- ①首先找出在Course关系中"信息系统"的课程号,结果为3号
		from Course
		where Cname='信息系统'
		)
 	 );

-- 本查询同样也可以用连接实现
select  Student.Sno,Sname -- 注意不能写成sno,因为两个表有这个属性
from student,sc,course
where student.sno = SC.sno and
	course.cno = sc.cno and
	course.cname = '信息系统';

b.相关子查询

3.2带有比较运算符的子查询

-- 找出每个课程超出他自己选修课程平均分的课程号
/*这个是书上的答案貌似有问题*/
select Sno,cno
from sc x
where x.grade >=(select avg(y.grade)
				from sc y
				where x.sno = y.sno);
-- 求每门课程的平均成绩
select cno,avg(grade)
from sc
group by cno;

-- 会报错
select sno,cno
from sc x
where x.grade >= (select avg(y.grade)
				from sc y
				group by y.cno);

3.3带有ANY(some)或ALL谓词的子查询

-- 查询非计算机科学系中比计算机科学系任意一个学生年龄小的学生姓名和年龄.
select Sname,Sage
from student
where Sage< ANY
	(select Sage
	from student
	where Sdept = 'cs')
and Sdept <> 'cs'; /*注意这是父查询块中的条件*/

-- 本查询也可以用聚合函数来实现,比任意一个计算机的学生年龄小,就一定小于计算机系中年龄最大的
select Sname,Sage
from student
where Sage < 
	(select max(Sage)
	from student
	where Sdept = 'cs')
and Sdept <> 'cs';  -- 父查询条件

-- 查询非计算机系中比计算机科学系所有学生年龄都小的学生姓名及年龄.
select Sname,Sage
from student
where Sage < 
	(select min(Sage)
	from student
	where Sdept = 'cs')
and Sdept <> 'cs';  -- 父查询条件

3.4带有exists谓词的子查询

带有exists谓词的子查询不返回任何数据,只产生逻辑值"true"或逻辑假值"false".

-- 查询所有选修了1号课程的学生姓名
1 使用连接查询
select Sname
from Student ,SC
where student.sno = sc.sno and
sc.cno = '1';

2 使用嵌套查询
select Sname
from student
where exists
	(select*
	from sc
	where sno=student.sno 
	and cno = '1');

-- 查询没有选修了1号课程的学生姓名
1 连接查询
select Sname
from Student ,SC
where not student.sno = sc.sno and
sc.cno = '1';

2 嵌套查询
select Sname
from student
where not exists
	(select*
	from sc
	where sno=student.sno 
	and cno = '1');

-- 查询选修了全部课程的学生名字
全部选修 = 没有一门是不选的
全称量词改成存在量词的语义

select Sname
from student
where not exists	
	(select *
	from course
	where not exists
		(select *
		from sc
		where sno = student.sno
		and cno = course.cno)
	);

-- 查询至少选修了学生201215122选修的全部课程的学生号码.
本查询可以用逻辑蕴含来表达:查询学号为x的学生,对所有的课程y,
只要2015122选修了课程y,则x也选修了y.

p:学生2015122选修了课程y
q:学生x选修了课程y
(任意)P->q 恒等于 ()p析取q

转换语义:不存在这样的课程y,学生201215122选修了y,而学生x没有选.sql语言表达为

select distinct Sno
from SC SCX
where not exists(
	select *
	from SC SCY
	where SCY.Sno = '201215122' and
		not exists(
		select *
		from SC SCZ
		where SCZ.Sno = SCX.Sno and
			SCZ.Cno = SCY.Cno));

4集合查询

4.1union

4.2intersect

4.3except

集合操作主要包括并操作union,交操作intersect,差操作except.

3.64 查询计算机科学系的学生及年龄不大于19岁的学生
select *
from student
where sdept = 'cs'
union
select *
from student
where Sage <= 19;

3.65 查询选修了课程1或者选修了课程2的学生
select sno
from sc
where cno='1'
union
select sno
from sc
where cno='2';

3.66 查询计算机科学系的学生与年龄不大于19岁的学生的交集
select *
from student
where sdept='cs'
INTERSECT
select *
from student
where sage <= 19;

这实际上就是查询计算机科学系年龄不大于19岁的学生
select *
from student
where sdept = 'cs'
	and sage <= 19;

3.67 查询即选修了课程1又选修了课程二的学生.就是选修了课程1和选修了课程2同学的交集.

select Sno
from sc
where cno='1'
intersect
select sno
from sc
where cno='2';

可以以这么表示
select sno
from sc
where cno='1' and sno in
	(select sno
	from sc
	where cno = '2');

3.68 查询计算机科学系学生与年龄不大于19岁学生的差集
select * 
from student
where sdept='cs'
except
select *
from student
where sage <= 19;

5基于派生表查询

子查询不仅可以出现在where语句子句中,还可以出现在from字句中,这时子查询生成的临时派生表成为主查询的查询对象.

如 例题3.57 找出每个学生超过他自己选修课程平均成绩的课程号,可用如下查询
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;

3.60 查询所有选修了1号课程的学生姓名,可以用如下查询
select sname
from student,(select sno from sc where Cno='1') as SC1
where student.sno = SC1.sno;

使用连接方法
select sname
from student,sc
where student.sno=sc.sno 
	and sc.cno='1';
原创文章 36 获赞 8 访问量 2777

猜你喜欢

转载自blog.csdn.net/qq_41398619/article/details/105420500
今日推荐