MySQL-多表查询

MySQL-多表查询

课前题目:

  1. 建立一个school数据库,将school.sql数据导入到shool数据库中。建立xkgl数据库,将xkgl.sql数据导入到xkgl数据库中。
create database school;
create database xkgl;

image-20201031214341879

  1. 观察school数据库中course表每一列的列名,将xkgl数据库中course表前三行数据对应列内容插入到school的course表中,(请用insert语句+select语句完成将查找到的信息插入表的操作),数据插入后,打开school数据库的course表,应当看到如下三行内容)

img

INSERT INTO school.course 

SELECT courseid,coursename,bookname 

FROM course

LIMIT 0,3
  1. 输出平均成绩大于68分的同学的学号和他的平均成绩[school数据库]
SELECT studentid,AVG(grade)

FROM grade

GROUP BY school.grade

HAVING AVG(grade)>68

1. 多表连接查询

在关系型数据库管理系统中,通常一张表只会存储一个实体的相关信息,如果用户需要查询多张表中不同实体的数据,必须要将多个表的数据进行连接。

image-20201102114028606

image-20201102114056022

[shool数据库:]

[例]查询每条成绩对应的学生姓名,课程号,成绩

#1、自然连接

SELECT studentname,courseid,grade

FROM grade,student

WHERE grade.studentid = student.studentid

#2、内连接

SELECT studentname, courseid, grade 

FROM student

INNER JOIN grade ON grade.studentid = student.studentid

如果用户需要查询多张表中不同实体的数据,可以使对表执行连接查询操作,但前提条件是,这些表中必须存在具有相同意义的字段。

连接查询:同时涉及两个以上的表的查询,用连接字段连接

1.1 等值连接(相等连接):从where中连接(用=号进行匹配)
SELECT {*|col_list}

FROM table1,table2

WHERE table1.section_id=table_2.section_id;
1.2 自然连接

数据库应用中最常用的是“自然连接”,它在目标列中去除相同的字段名。

进行自然连接运算要求两个表有共同属性(列),自然连接运算的结果表是在参与操作的两个表的共同属性上进行等值连接后,再去除重复的属性后所得的新表。

等值连接和自然连接的区别:

1)等值连接中不要求相等属性值的属性名相同,而自然连接要求相等属性值的属性名必须相同,即两关系只有在同名属性才能进行自然连接。
2)等值连接不将重复属性去掉,而自然连接去掉重复属性,也可以说,自然连接是去掉重复列的等值连接。

1.3 自/内/外连接:从from中连接(JION连接)

自连接作为一种特例,可以将一个表与它自身进行连接,称为自连接。若要在一个表中查找具有相同列值的行,则可以使用自连接。使用自连接时需为表指定两个别名,且对所有列的引用均要用别名限定。

SELECT a.学号, a.课程号, b.课程号, a.成绩
	FROM CJB a  JOIN  CJB b 
			ON  a.成绩=b.成绩 AND  a.学号=b.学号 AND  a.课程号!=b.课程号

指定了INNER关键字的连接是内连接,内连接按照ON所指定的连接条件合并两个表,返回满足条件的行。内连接是系统默认的,可以省略INNER关键字。使用内连接后仍可使用WHERE子句指定条件。

指定了left/right关键字的为外连接,外连接的结果表不但包含满足连接条件的行,还包括相应表中的所有行

SELECT {*|col_list} 
 
FROM table_name1

INNER/LEFT/RIGHT JOIN table_name2 ON condition

[例]查询每条成绩对应的学生学号,姓名,课程号,成绩

SELECT grade.studentid,studentname,courseid,grade

FROM grade,student

WHERE grade.studentid = student.studentid 

[例]查询每个同学的成绩,输出信息包括学号,学生名,课程号,成绩,如果该学生没有选过课,则输出该同学的学号和姓名,课程号和成绩信息为空

SELECT student.studentid,studentname,courseid,grade

FROM student 

left JOIN grade

ON grade.studentid = student.studentid 

#权利大的表是基本表,基本表中的数据都会保留

[学习通]查询每条成绩对应的学号,课程号,课程名,成绩

[分别用自然连接和外连接两种方式做]

  1. 自然连接
SELECT studentid,grade.courseid,coursename,grade

FROM grade,course

WHERE grade.courseid = course.courseid
  1. 外连接
SELECT studentid,grade.courseid,coursename,grade

FROM grade

LEFT JOIN course 

ON grade.courseid = course.courseid
  1. 复合条件连接查询是通过在连接查询中添加过滤条件,以达到限制查询结果和筛选数据的目的

[例2]查询选修java程序设计课程且成绩在85分以上的所有学生的学号和姓名以及成绩。

#1、内连接

SELECT student.studentid,studentname,coursename,grade

FROM grade

INNER JOIN course ON grade.courseid = course.courseid

INNER JOIN student ON grade.studentid = student.studentid

WHERE coursename LIKE "J%" AND grade>85


#2、自然连接

SELECT student.studentid,studentname,coursename,grade

FROM grade,student,course

WHERE grade.studentid = student.studentid 

AND grade.courseid = course.courseid

[xkgl数据库:]

[课堂练习]查询计算机系所有学生的姓名和所在班级名

#1、自然连接

SELECT studentname,classname 

FROM department,student,class 

WHERE departmentname = "计算机系" 

AND class.DepartmentID = department.DepartmentID 

AND student.ClassID = class.ClassID

 

#2、内连接

SELECT studentname,classname 

FROM department

INNER JOIN class ON class.DepartmentID = department.DepartmentID

INNER JOIN student ON student.ClassID = class.ClassID 

WHERE departmentname = "计算机系"

[学习通]:查询每个同学的平均成绩,输出姓名,平均成绩

1、自然连接

SELECT studentname,AVG( grade ) AS 平均成绩 ,student.studentid

FROM grade,student

WHERE grade.StudentID = student.StudentID 

GROUP BY Studentid

#2、内连接

SELECT studentname,AVG( grade ) AS 平均成绩,student.studentid

FROM grade

INNER JOIN student ON grade.StudentID = student.StudentID

GROUP BY studentid

 

2. 子查询

如果一个查询语句中嵌套了一个或若干个其他的查询语句,那么在整个语句中,外层查询称为主查询,内层查询称为子查询或者嵌套查询。该类查询可以基于一个表或多个表。在此类查询中,系统会先执行子查询,将子查询的结果作为主查询的过滤条件,子查询分为以下几种:

2.1 带有IN谓词的子查询
2.2 带有ANY(SOME)或ALL谓词的子查询
2.3 带有条件判断符的子查询
2.4 带有EXISTS谓词的子查询

2.1 带有IN谓词的子查询

当子查询返回的是一个数据集合,主查询需要返回符合集合中条件的记录时,可以使用IN关键字,语法形式如下:

SELECT {*|col_list} FROM table_name1 

WHERE col_name1 IN 

(SELECT col_name2 FROM table_name2 [WHERE condition]);

[例]查询属于计算机系和信管系的班级名称,输出班级名称和所属系名称

SELECT

 ClassName,

 DepartmentName

FROM

 class c,

 department d 

WHERE

 c.DepartmentID = d.DepartmentID 

 AND c.DepartmentID IN ( SELECT DepartmentID FROM department d WHERE d.DepartmentName IN ( "计算机系", "信管系" ) );

[学习通]查询计算机系年龄最大的学生姓名及当前年龄(假设计算机系年龄最大的学生为20岁,将所有计算机系20岁的同学姓名都输出)

SELECT s.StudentName,'2020'-YEAR(birth1) as 年龄

FROM student s,department d,class c

WHERE s.ClassID = c.ClassID 

AND c.DepartmentID = d.DepartmentID

AND d.DepartmentName = "计算机系"

AND YEAR(birth1) =(

  SELECT DISTINCT YEAR(birth1) AS z

  FROM student

  ORDER BY z

  LIMIT 1

)

另外,子查询还可以和NOT IN配合使用,表示:不在此范围内任一数据都可以

[课堂练习]查询不属于计算机系的班级名称

SELECT

 ClassName,

 DepartmentName

FROM

 class c,

 department d 

WHERE

 c.DepartmentID = d.DepartmentID 

 AND c.DepartmentID IN (

 SELECT

  DepartmentID

 FROM

  department d 

 WHERE

 d.DepartmentName NOT IN ( "计算机系" ));

2.2 使用ANY、ALL关键字的子查询

l ANY(some)表示满足其中任一条件。

该类查询会创建一个表达式对子查询的返回值列表进行比较,只要满足子查询中的任一个比较条件,就返回一个结果。其语法形式如下:

SELECT {*|col_list} FROM table_name1 

WHERE col_name1<any 

(SELECT col_name2 FROM table_name2 [WHERE condition]);

ALL表示满足所有条件。

与ANY不同,使用关键字ALL的子查询,表示当一条记录符合子查询结果中所有的条件时,才会返回该记录。其语法形式如下:

SELECT {*|col_list} FROM table_name1 

WHERE col_name1>All 

(SELECT col_name2 FROM table_name2 [WHERE 条件]);

[例]找出其他班级中比“10电子商务1班“最大的学生年龄还大的同学姓名。

#方法一

SELECT StudentName,birth1

FROM student s,class c

WHERE s.classid=c.ClassID

AND c.ClassName != "10电子商务1班"

AND YEAR(birth1)<(

  SELECT MIN(YEAR(birth1))

  FROM student s,class c

  WHERE ClassName = '10电子商务1班'

  AND s.ClassID = c.ClassID

)

#方法二

SELECT StudentName,birth1

FROM student

WHERE YEAR(birth1) <ALL (

  SELECT YEAR(birth1)

  FROM student s,class c

  WHERE ClassName = '10电子商务1班'

  AND s.ClassID = c.ClassID

)

[课堂练习]找出其他班级中年龄比“10电子商务1班“最小的学生年龄大的同学姓名。

#方法一

SELECT StudentName,birth1

FROM student

WHERE YEAR(birth1)<(

  SELECT max(YEAR(birth1))

  FROM student s,class c

  WHERE ClassName = '10电子商务1班'

  AND s.ClassID = c.ClassID

);

 
#方法二

SELECT StudentName,birth1

FROM student

WHERE YEAR(birth1)<ANY(

  SELECT YEAR(birth1)

  FROM student s,class c

  WHERE ClassName = '10电子商务1班'

  AND s.ClassID = c.ClassID

);

2.3 使用条件判断符的子查询

在子查询中,还可以单独使用条件判断符。其语法形式如下:

SELECT {*|col_list} FROM table_name 1

WHERE col_name1 operators

(SELECT col_name2 FROM table_name2);

[例]找出’St0109010001’号学生超过他选修课程平均成绩的课程号。

SELECT courseid

FROM grade

WHERE studentid = "St0109010001" 

AND grade >(

 SELECT AVG(grade)

​     FROM grade

​     WHERE studentid = "St0109010001" 

)

[课堂练习]找出其他班级中比“10电子商务1班“最大的学生年龄还大的同学姓名。

SELECT StudentName,birth1

FROM student s,class c

WHERE s.classid=c.ClassID

AND c.ClassName != "10电子商务1班"

AND YEAR(birth1)<(

  SELECT MIN(YEAR(birth1))

  FROM student s,class c

  WHERE ClassName = '10电子商务1班'

  AND s.ClassID = c.ClassID

)

 

[思考题]找出每个学生超过他选修课程平均成绩的课程号。

SELECT courseid

FROM grade x

WHERE grade>(

  SELECT AVG(grade)

  FROM grade y

  WHERE y.studentid = x.studentid

)

不相关子查询:

子查询的查询条件不依赖于父查询

过程:由里向外逐层处理。即每个子查询在上一级查询处理之前求解,子查询的结果用于建立其父查询的查找条件。

相关子查询:

子查询的查询条件依赖于父查询

过程:首先取外层查询中表的第一个元组,根据它与内层查询相关的属性值处理内层查询,若WHERE子句返回值为真,则取此元组放入结果表。然后再取外层表的下一个元组,重复这一过程,直至外层表全部检查完为止。

2.4 含有EXISTS关键字的子查询

2.4.1 EXISTS谓词

带有EXISTS谓词的子查询不返回任何数据,只产生逻辑真值“true”或逻辑假值“false”。

若内层查询结果集非空,则外层的WHERE子句返回真值

若内层查询结果集为空,则外层的WHERE子句返回假值

由EXISTS引出的子查询,其目标列表达式通常都用 * ,因为带EXISTS的子查询只返回真值或假值,给出列名无实际意义。

2.4.2 NOT EXISTS谓词

若内层查询结果非空,则外层的WHERE子句返回假值

若内层查询结果为空,则外层的WHERE子句返回真值

Exists采用逐条遍历的方式,每次查询都会在内层进行匹配,若该记录返回条件为真,则输出当前记录,如果为假,则舍弃当前记录

[例]查询所有选修了Dp010001号课程的学生姓名。

SELECT studentname

FROM student s

WHERE EXISTS(

 SELECT *

 FROM grade g

 WHERE g.studentid = s.studentid 

 AND courseid = 'Dp010001'

)

[课堂练习]查询没有选修Dp010001号课程的学生姓名。

SELECT studentname

FROM student s

WHERE NOT EXISTS(

 SELECT *

 FROM grade g

 WHERE g.studentid = s.studentid 

 AND courseid = 'Dp010001'

)

[例]查询选修了全部课程的学生姓名。

SELECT studentname

FROM student s,grade g

WHERE s.studentid = g.studentid

GROUP BY s.studentid

HAVING COUNT(*) = (SELECT COUNT(*) FROM course)

所有带IN谓词、比较运算符、ANY和ALL谓词的子查询都能用带EXISTS谓词的子查询等价替换

一些带EXISTS或NOT EXISTS谓词的子查询不能被其他形式的子查询等价替换

用EXISTS/NOT EXISTS实现全称量词(难点)

SQL语言中没有全称量词 (For all)

可以把带有全称量词的谓词转换为等价的带有存在量词的谓词:

(x)P ≡  ( x( P))

[课堂练习]查询至少选修了学生St0109010002选修的全部课程的学生号。**

SELECT studentname

FROM student s

WHERE NOT EXISTS(

 SELECT *mysql

 FROM grade a

 WHERE a.studentid = 'St0109010002'

 AND NOT EXISTS(

  SELECT * 

  FROM grade b

  WHERE b.studentid = s.studentid

  AND a.courseid = b.courseid)

)

[课堂练习]查询所有同时选修了Dp010001号课程和Dp010004号课程的学生姓名。

SELECT StudentName

FROM student s

WHERE NOT EXISTS(

 SELECT *

 FROM course co 

 WHERE (CourseID = 'Dp010001' OR CourseID = 'Dp010004')

 AND not EXISTS(

  SELECT *

  FROM Grade g

  WHERE s.StudentID = g.StudentID

  AND g.CourseID = co.CourseID

 )

)

3. 派生表

子查询不仅可以出现在WHERE子句中,还可以出现在FROM子句中

将子查询做为一个表来处理,这个由子查询得出的新表就是我们说的"派生表"。

[例]利用派生表的方式查询所有选修了Dp010001号课程的学生姓名

SELECT StudentName 

FROM student s,( SELECT CourseID, StudentID FROM Grade WHERE CourseID

= 'Dp010001' ) AS co 

WHERE s.studentid = co.StudentID

【练习】用派生表方式找出每个学生超过他自己选修课程平均成绩的课程号

SELECT s.studentid,courseid

FROM grade g,( SELECT studentid, AVG( grade ) avg_grade

FROM grade GROUP BY studentid ) s

WHERE g.studentid = s.studentid

AND grade>avg_grade; 

4. 合并查询结果

合并查询结果就是使用UNION关键字,将多条查询语句的结果合并在一起显示。UNION有两种使用方法

1.查询结果不重复(过滤掉重复的记录)

2.保留所有查询结果。

应用场景:适用于查询多个表的时候,但多个表没有直接的关系,查询的列基本是一致时

特点

1、要求多条查询语句的查询列数必须一致

2、要求多条查询语句的查询的各列类型、顺序最好一致

3、union 去重,union all包含重复项

[例]查询所有选修了Dp010001号或Dp010004号课程的学生姓名。

SELECT

  StudentName ,st.StudentID 

FROM

 student st,

 grade g 

WHERE

 st.StudentID = g.StudentID 

 AND CourseID = 'Dp010001' 

UNION 

SELECT

 StudentName , st.StudentID

FROM

 student st,

 grade g 

WHERE

 st.StudentID = g.StudentID 

 AND CourseID = 'Dp010004';


[例]查询选修了Dp010001号或Dp010004号课程的记录一共有多少条?

SELECT

  StudentName ,st.StudentID,CourseID 

FROM

 student st,

 grade g 

WHERE

 st.StudentID = g.StudentID 

 AND CourseID = 'Dp010001' 

UNION ALL

SELECT

 StudentName , st.StudentID,CourseID 

FROM

 student st,

 grade g 

WHERE

 st.StudentID = g.StudentID 

 AND CourseID = 'Dp010004';

[练习]查找学生总数以及出生日期最早的同学姓名,将这两个查询结果合并到一个结果集中

SELECT COUNT(*) 学生总人数和出生最早学生

FROM student

UNION

SELECT StudentName

FROM student

WHERE birth1 = (SELECT MIN(birth1) FROM student)

5. 实验题:

1. 查询选修了课程的学生姓名

SELECT

 DISTINCT student.studentid,studentname

FROM

 student,grade

WHERE student.StudentID = grade.StudentID

img

2. 查询11网络工程和09数据库班每个学生的姓名和他所在的班级名,即使该班没有学生,也要输出班级名。

SELECT

 classname,studentname

FROM

 class LEFT JOIN student ON class.ClassID = student.classid

WHERE classname = "11网络工程" OR classname = "09数据库班"

img

3. 求出JAVA程序设计考试的前三名的姓名和成绩

SELECT

 student.studentid,

 grade,

 studentname,

 coursename 

FROM

 course,

 student,

 grade 

WHERE

 course.CourseID = grade.CourseID 

 AND student.StudentID = grade.StudentID 

 AND coursename = "JAVA程序设计" 

GROUP BY

 grade DESC,

 student.studentid 

 LIMIT 3;

img

4. 求信管系以外的同学姓名,所在班级名和所在系名

SELECT 

 studentname,classname,departmentname

FROM

 student,class,department

 WHERE department.Departmentid = class.DepartmentID

 AND student.ClassID = class.ClassID

and departmentname != "信管系"

img

5. 求姓名第二个字是丽的同学选修了哪些课程,请写出同学名和课程名。

SELECT

 studentname,

 coursename 

FROM

 student,

 grade,

 course 

WHERE

 student.studentid = grade.StudentID 

 AND grade.CourseID = course.CourseID 

 AND studentname LIKE "_丽%";

img

6. 查询11网络工程和10电子商务2班一共有多少人

SELECT count( StudentID ) 人数

FROM class c,student s 

WHERE s.ClassID = c.ClassID 

AND classname IN ('11网络工程','10电子商务2班')

img

7. 查询哪些课程没有被选修过

方法一:

SELECT CourseName

FROM course

WHERE CourseID not IN(

SELECT DISTINCT c.CourseID

FROM grade g

LEFT JOIN course c

ON c.CourseID = g.CourseID)

方法二:

SELECT CourseName 

FROM course 

WHERE coursename NOT IN (

SELECT DISTINCT CourseName 

FROM course co,grade g 

WHERE co.courseid = g.courseid 

AND StudentID IN ( SELECT DISTINCT StudentID 
	FROM course co, grade g 
	WHERE co.CourseID = g.CourseID )
	)

img

8. 找出选修了全部课程的同学的名字

SELECT StudentName

FROM student s

WHERE NOT EXISTS(

 SELECT *

 FROM course co

 WHERE NOT EXISTS(

  SELECT *

  FROM grade g

  WHERE g.StudentID = s.StudentID

  AND co.CourseID = g.CourseID

 )

)

img

9. 查询有哪些同学,没有选修学生st0210010005选修的课程(即st0210010005选修的课程他一门都没选)

SELECT StudentName

FROM Student s

WHERE NOT EXISTS(

 SELECT *

 FROM grade a

 WHERE a.StudentID = 'st0210010005'

 AND EXISTS(

  SELECT *

  FROM grade g

  WHERE g.studentid = s.StudentID

  AND a.courseid = g.CourseID

 )

)


img

10. 找出每个学院每个班级各有多少人(需要通过student表找),需要输出的结果如下:

image-20201102000045356

 

SELECT

 DepartmentID,

 c.ClassID,

  COUNT(*) 

FROM

 student s,

  class c 

WHERE

 s.ClassID = c.ClassID 

GROUP BY

 DepartmentID,

  ClassID

img

11.12题选做

11. 查找每个科目前三名的学号和成绩(需要输出课程号,学号和成绩)

SELECT

 CourseID 课程号,StudentID 学号,grade 成绩

FROM

 grade 

WHERE( 

 SELECT count(*) 

 FROM grade AS a

 WHERE grade.CourseID = a.CourseID 

 AND Grade.Grade < a.Grade 

)< 3 

ORDER BY

 CourseID ASC,

 Grade.Grade DESC;

img

12. 求每个学生的课是哪位教师教授的(求学号,学生姓名,教师姓名,课程名称)

SELECT
	student.studentname,
	course.coursename,
	teachername 
FROM
	student,
	grade,
	SCHEDULE,
	teacher,
	course 
WHERE
	student.studentid = grade.studentid 
	AND SCHEDULE.classid = student.classid 
	AND SCHEDULE.courseid = grade.courseid 
	AND course.courseid = grade.courseid 
	AND SCHEDULE.teacherid = teacher.teacherid;

image-20201102105037136

本文都是自己上课的笔记和作业,如果有觉得我题目做的不对的或者麻烦的希望能指出来,让我学到更多的知识,如果想要具体的数据库文件也可以联系我。

  • 5
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 10
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 10
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值