From affb278c40dd29288106c03fbcced17748d841a9 Mon Sep 17 00:00:00 2001 From: xcchengguo <2395432766@qq.com> Date: Sun, 5 Mar 2023 11:05:40 +0800 Subject: [PATCH] =?UTF-8?q?=E6=88=91=E7=9A=84=E7=AC=AC=E4=BA=94=E6=AC=A1?= =?UTF-8?q?=E4=BD=9C=E4=B8=9A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../20230305 \345\205\263\350\201\224.md" | 362 ++++++++++++++++++ 1 file changed, 362 insertions(+) create mode 100644 "18 \350\260\242\345\256\270/20230305 \345\205\263\350\201\224.md" diff --git "a/18 \350\260\242\345\256\270/20230305 \345\205\263\350\201\224.md" "b/18 \350\260\242\345\256\270/20230305 \345\205\263\350\201\224.md" new file mode 100644 index 0000000..1701335 --- /dev/null +++ "b/18 \350\260\242\345\256\270/20230305 \345\205\263\350\201\224.md" @@ -0,0 +1,362 @@ +# 第11章 关联查询(联合查询) + +## 11.1 什么是关联查询 + +关联查询:两个或更多个表一起查询。 + +前提条件:这些一起查询的表之间是有关系的(一对一、一对多),它们之间一定是有关联字段,这个关联字段可能建立了外键,也可能没有建立外键。 + +比如:员工表和部门表,这两个表依靠“部门编号”进行关联。 + +## 11.3 关联查询的SQL有几种情况 + +1、内连接:inner join ... on + +结果:A表 ∩ B表 + +2、左连接:A left join B on + +(2)A表全部 + +(3)A表- A∩B + +3、右连接:A right join B on + +(4)B表全部 + +(5)B表-A∩B + +4、全外连接:full outer join ... on,但是mysql不支持这个关键字,mysql使用union(合并)结果的方式代替 + +(6)A表∪B表: (2) A表结果 union (4)B表的结果 + +(7)A∪B - A∩B (3)A表- A∩B结果 union (5)B表-A∩B结果 + +```mysql +/* +(1)凡是联合查询的两个表,必须有“关联字段”, +关联字段是逻辑意义一样,数据类型一样,名字可以一样也可以不一样的两个字段。 +比如:t_employee (A表)中did和t_department(B表)中的did。 + +发现关联字段其实就是“可以”建外键的字段。当然联合查询不要求一定建外键。 + +(2)联合查询必须写关联条件,关联条件的个数 = n - 1. +n是联合查询的表的数量。 +如果2个表一起联合查询,关联条件数量是1, +如果3个表一起联合查询,关联条件数量是2, +如果4个表一起联合查询,关联条件数量是3, +。。。。 +否则就会出现笛卡尔积现象,这是应该避免的。 + +(3)关联条件可以用on子句编写,也可以写到where中。 +但是建议用on单独编写,这样呢,可读性更好。 + +每一个join后面都要加on子句 +A inner|left|right join B on 条件 +A inner|left|right join B on 条件 inner|left|right jon C on 条件 +``` + + + +### 1、内连接 + +```mysql +#演示内连接,结果是A∩B +/* +观察数据: +t_employee 看成A表 +t_department 看成B表 +此时t_employee (A表)中有 李红和周洲的did是NULL,没有对应部门, + t_department(B表)中有 测试部,在员工表中找不到对应记录的。 +*/ + +#查询所有员工的姓名,部门编号,部门名称 +#如果员工没有部门的,不要 +#如果部门没有员工的,不要 +/* +员工的姓名在t_employee (A表)中 +部门的编号,在t_employee (A表)和t_department(B表)都有 +部门名称在t_department(B表)中 +所以需要联合两个表一起查询。 +*/ +SELECT ename,did,dname +FROM t_employee INNER JOIN t_department; +#错误Column 'did' in field list is ambiguous +#因为did在两个表中都有,名字相同,它不知道取哪个表中字段了 +#有同学说,它俩都是部门编号,随便取一个不就可以吗? +#mysql不这么认为,有可能存在两个表都有did,但是did的意义不同的情况。 +#为了避免这种情况,需要在编写sql的时候,明确指出是用哪个表的did + +SELECT ename,t_department.did,dname +FROM t_employee INNER JOIN t_department; +#语法对,结果不太对 +#结果出现“笛卡尔积”现象, A表记录 * B表记录 +/* +(1)凡是联合查询的两个表,必须有“关联字段”, +关联字段是逻辑意义一样,数据类型一样,名字可以一样也可以不一样的两个字段。 +比如:t_employee (A表)中did和t_department(B表)中的did。 + +发现关联字段其实就是可以建外键的字段。当然联合查询不要求一定建外键。 + +(2)联合查询必须写关联条件,关联条件的个数 = n - 1. +n是联合查询的表的数量。 +如果2个表一起联合查询,关联条件数量是1, +如果3个表一起联合查询,关联条件数量是2, +如果4个表一起联合查询,关联条件数量是3, +。。。。 +否则就会出现笛卡尔积现象,这是应该避免的。 + +(3)关联条件可以用on子句编写,也可以写到where中。 +但是建议用on单独编写,这样呢,可读性更好。 + +每一个join后面都要加on子句 +A inner|left|right join B on 条件 +A inner|left|right join B on 条件 inner|left|right jon C on 条件 +*/ + + +SELECT ename,t_department.did,dname +FROM t_employee INNER JOIN t_department +ON t_employee.did = t_department.did; + +SELECT * +FROM t_employee INNER JOIN t_department +ON t_employee.did = t_department.did; + + +#查询部门编号为1的女员工的姓名、部门编号、部门名称、薪资等情况 +SELECT ename,gender,t_department.did,dname,salary +FROM t_employee INNER JOIN t_department +ON t_employee.did = t_department.did +WHERE t_department.did = 1 AND gender = '女'; + +#查询部门编号为1的员工姓名、部门编号、部门名称、薪资、职位编号、职位名称等情况 +SELECT ename,gender,t_department.did,dname,salary,job_id,jname +FROM t_employee INNER JOIN t_department ON t_employee.did = t_department.did + INNER JOIN t_job ON t_employee.`job_id` = t_job.`jid` +WHERE t_department.did = 1; + +``` + +### 2、左连接 + +```mysql +#演示左连接 +/* +(2)A +(3)A-A∩B +*/ +/* +观察数据: +t_employee 看成A表 +t_department 看成B表 +此时t_employee (A表)中有 李红和周洲的did是NULL,没有对应部门, + t_department(B表)中有 测试部,在员工表中找不到对应记录的。 +*/ +#查询所有员工,包括没有指定部门的员工,他们的姓名、薪资、部门编号、部门名称 +SELECT ename,salary,t_department.did,dname +FROM t_employee LEFT JOIN t_department +ON t_employee.did = t_department.did; +#查询的是A结果 A left join B + +#查询没有部门的员工信息 +SELECT ename,salary,t_department.did,dname +FROM t_employee LEFT JOIN t_department +ON t_employee.did = t_department.did +WHERE t_employee.did IS NULL; +#查询的结果是A - A∩B +#此时的where条件,建议写子表的关联字段is null,这样更准确一点。 +#如果要建外键,它们肯定有子表和父表的角色,写子表的关联字段is null +#因为父表中这个字段一般是主键,不会为null。 + +``` + +### 3、右连接 + +```mysql +/* +右连接 +(4)B +(5)B - A∩B +*/ +#演示右连接 +/* +观察数据: +t_employee 看成A表 +t_department 看成B表 +此时t_employee (A表)中有 李红和周洲的did是NULL,没有对应部门, + t_department(B表)中有 测试部,在员工表中找不到对应记录的。 +*/ +#查询所有部门,包括没有对应员工的部门,他们的姓名、薪资、部门编号、部门名称 +SELECT ename,salary,t_department.did,dname +FROM t_employee RIGHT JOIN t_department +ON t_employee.did = t_department.did; +#查询的是B结果 A RIGHT join B + +#查询没有员工部门的信息 +SELECT ename,salary,t_department.did,dname +FROM t_employee RIGHT JOIN t_department +ON t_employee.did = t_department.did +WHERE t_employee.did IS NULL; +#查询的结果是B - A∩B +#此时的where条件,建议写子表的关联字段is null,这样更准确一点。 +#如果要建外键,它们肯定有子表和父表的角色,写子表的关联字段is null +#因为父表中这个字段一般是主键,不会为null。 + + + +#查询所有员工,包括没有指定部门的员工,他们的姓名、薪资、部门编号、部门名称 +SELECT ename,salary,t_department.did,dname +FROM t_department RIGHT JOIN t_employee +ON t_employee.did = t_department.did; +#查询的是B结果 A RIGHT join B + +#查询没有部门的员工信息 +SELECT ename,salary,t_department.did,dname +FROM t_department RIGHT JOIN t_employee +ON t_employee.did = t_department.did +WHERE t_employee.did IS NULL; +#查询的结果是B - A∩B A right join B +#此时的where条件,建议写子表的关联字段is null,这样更准确一点。 +#如果要建外键,它们肯定有子表和父表的角色,写子表的关联字段is null +#因为父表中这个字段一般是主键,不会为null。 +``` + +### 作业 + +```mysql +create database guanlian charset (utf8); +USE guanlian; +CREATE TABLE `student` ( + `Sno` varchar(20) NOT NULL COMMENT '学号', + `name` varchar(20) NOT NULL, + `Seax` varchar(20) NOT NULL, + `Sbirthday` datetime DEFAULT NULL, + `Class` varchar(20) DEFAULT NULL, + PRIMARY KEY (`Sno`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; +CREATE TABLE `teacher` ( + `Tno` varchar(20) NOT NULL, + `Tname` varchar(20) NOT NULL, + `Tsex` varchar(20) NOT NULL, + `Tbirthday` datetime DEFAULT NULL, + `Prof` varchar(20) DEFAULT NULL, + `Depart` varchar(20) DEFAULT NULL, + PRIMARY KEY (`Tno`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; +CREATE TABLE `course` ( + `Cno` varchar(20) NOT NULL, + `Cname` varchar(20) NOT NULL, + `Tno` varchar(20) NOT NULL, + PRIMARY KEY (`Cno`), + KEY `Tno` (`Tno`), + CONSTRAINT `course_ibfk_1` FOREIGN KEY (`Tno`) REFERENCES `teacher` (`Tno`) ON DELETE CASCADE ON UPDATE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; +CREATE TABLE `score` ( + `Sno` varchar(20) NOT NULL, + `Cno` varchar(20) NOT NULL, + `Degree` decimal(4,1) DEFAULT NULL, + PRIMARY KEY (`Sno`,`Cno`), + KEY `Cno` (`Cno`), + CONSTRAINT `score_ibfk_1` FOREIGN KEY (`Sno`) REFERENCES `student` (`Sno`), + CONSTRAINT `score_ibfk_2` FOREIGN KEY (`Cno`) REFERENCES `course` (`Cno`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; +-- -- 1,查询所有学生,都学了哪些课程,要显示学生信息和课程信息/ +SELECT * FROM student INNER JOIN score ON student.Sno = score.Sno; +-- -- 2,查询没有学生的教师的所有信息 +SELECT * FROM teacher +LEFT JOIN course ON teacher.Tno = course.Tno +LEFT JOIN score ON course.Cno = score.Cno +WHERE Sno is NULL; +-- ① 查询Score表中的最高分的学生学号和课程号。 +SELECT MAX(Degree) FROM score; +SELECT Degree,Sno,Cno FROM score WHERE Degree = (SELECT MAX(Degree) FROM score); +-- ② 查询所有学生的Sname、Cno和Degree列。 +SELECT student.`name`,score.Cno,score.Degree FROM student INNER JOIN score ON student.Sno = score.Sno; +-- ③ 查询所有学生的Sno、Cname和Degree列。 +SELECT score.Sno,course.Cname,score.Degree FROM score INNER JOIN course on score.Cno = course.Cno; +-- ④ 查询所有学生的Sname、Cname和Degree列。 +SELECT student.`name`,course.Cname,score.Degree FROM student +LEFT JOIN score on student.Sno = score.Sno +LEFT JOIN course on score.Cno = course.Cno; +-- ⑤ 查询“95033”班学生的平均分。 +SELECT Sno FROM student WHERE Class = 95033; +SELECT SUM(Degree) FROM score WHERE Sno IN(101,107,108); +SELECT AVG(Degree) FROM score WHERE Sno IN(SELECT Sno FROM student WHERE Class = 95033); +-- SELECT * FROM student INNER JOIN score ON student.Sno = score.Sno WHERE Class = 95033; +-- ⑥ 查询选修“3-105”课程的成绩高于“109”号同学成绩的所有同学的记录。 +SELECT Degree FROM score WHERE Sno = 109 AND Cno = '3-105' +SELECT * FROM score WHERE Cno = '3-105' AND Degree>(SELECT Degree FROM score WHERE Sno = 109 AND Cno = '3-105'); +-- ⑦ 查询score中选学多门课程的同学中分数为非最高分成绩的记录。 +SELECT MAX(Degree) FROM score; +SELECT Sno,Cno,Degree FROM score WHERE Degree != (SELECT MAX(Degree) FROM score); +-- ⑧ 查询成绩高于学号为“109”、课程号为“3-105”的成绩的所有记录。 +SELECT Degree FROM score WHERE Sno = 109 AND Cno = '3-105'; +SELECT * FROM score WHERE Degree>(SELECT Degree FROM score WHERE Sno = 109 AND Cno = '3-105'); +-- ⑨ 查询和学号为108的同学同年出生的所有学生的Sno、Sname和Sbirthday列 +SELECT YEAR(Sbirthday) FROM student WHERE Sno = 108; +SELECT * FROM student WHERE Sno = 108 AND ; +SELECT sno,`name`,Sbirthday FROM student WHERE Sbirthday = (SELECT YEAR(Sbirthday) FROM student WHERE Sno = 108); +-- ⑩ 查询“张旭“教师任课的学生成绩。 +SELECT Degree FROM teacher +LEFT JOIN course ON teacher.Tno = course.Tno +LEFT JOIN score ON score.Cno = course.Cno +WHERE score.Cno = '6-166' +-- ⑪ 查询选修某课程的同学人数多于5人的教师姓名。? +SELECT COUNT(Cno) FROM score GROUP BY Cno; +SELECT * FROM teacher +LEFT JOIN course ON teacher.Tno = course.Tno +LEFT JOIN score ON course.Cno = score.Cno +WHERE score.Cno = '3-105' +-- ⑫ 查询出“计算机系“教师所教课程的成绩表。 +SELECT * FROM teacher WHERE Depart = '计算机系' +SELECT * FROM teacher +LEFT JOIN course ON teacher.Tno = course.Tno +LEFT JOIN score ON course.Cno = score.Cno +WHERE course.Cno IN('3-105','3-245') +-- ⑬ 查询“计算机系”与“电子工程系“不同职称的教师的Tname和Prof。 +SELECT Tname,Prof FROM teacher WHERE Depart IN('计算机系','电子工程系'); +-- ⑭ 查询选修编号为“3-105“课程且成绩至少高于选修编号为“3-245”的同学的Cno、Sno和Degree,并按Degree从高到低次序排序。 +SELECT Degree FROM score WHERE Cno = '3-105' +SELECT Degree FROM score WHERE Cno = '3-245' +SELECT * FROM score WHERE Cno in ('3-105','3-245'); +select Cno,Sno,Degree from score where cno='3-105' and degree >any(select degree from score where cno='3-245' ) order by degree DESC +-- ⑮ 查询选修编号为“3-105”且成绩高于选修编号为“3-245”课程的同学的Cno、Sno和Degree. +SELECT * FROM score WHERE Cno = '3-105' AND Degree > ALL(SELECT Degree FROM score WHERE Cno = '3-245') ORDER BY Degree DESC; +-- SELECT * FROM (SELECT * FROM score WHERE Cno IN ('3-105','3-245')) AS S WHERE Cno = '3-105' AND Degree > IN (SELECT Degree FROM score WHERE Cno = '3-245'); +-- SELECT * FROM score WHERE Degree > (SELECT Degree FROM score WHERE Cno = '3-245' || Cno = '3-105'); +-- -- ⑯ 查询成绩比该课程平均成绩低的同学的成绩表。 +SELECT * FROM score WHERE Cno = '3-105'; +SELECT AVG(Degree) FROM score WHERE Cno = '3-105'; +SELECT * FROM (SELECT * FROM score WHERE Cno = '3-105') AS S WHERE Degree<(SELECT AVG(Degree) FROM score WHERE Cno = '3-105'); +-- ⑰ 查询所有任课教师的Tname和Depart. +SELECT DISTINCT Tname,Depart FROM teacher +LEFT JOIN course ON teacher.Tno = course.Tno +LEFT JOIN score ON score.Cno = course.Cno +WHERE score.Cno is NOT NULL +-- ⑱ 查询所有未讲课的教师的Tname和Depart. +SELECT DISTINCT Tname,Depart FROM teacher +LEFT JOIN course ON teacher.Tno = course.Tno +LEFT JOIN score ON score.Cno = course.Cno +WHERE score.Cno is NULL +-- ⑲ 查询“男”教师及其所上的课程。 +SELECT * FROM teacher +LEFT JOIN course ON teacher.Tno = course.Tno +LEFT JOIN score ON score.Cno = course.Cno +WHERE score.Cno is NOT NULL AND Tsex = '男' +-- ⑳ 查询最高分同学的Sno、Cno和Degree列。 +SELECT Sno,Cno,Degree FROM score WHERE Degree = (SELECT MAX(Degree)FROM score) +-- 21 查询和“李军”同性别的所有同学的Sname. +SELECT `name` FROM student WHERE Seax = '男' AND `name` != '李军' +-- 22 查询和“李军”同性别并同班的同学Sname. +SELECT Class FROM student WHERE `name` = '李军' +SELECT * FROM student WHERE Seax = '男' AND Class = (SELECT Class FROM student WHERE `name` = '李军') +-- 23 查询所有选修“计算机导论”课程的“男”同学的成绩表。 +SELECT * FROM score +LEFT JOIN student ON score.Sno = student.Sno +LEFT JOIN course ON score.Cno = course.Cno +WHERE student.Seax = '男' AND course.Cname = '计算机导论' +``` + + + -- Gitee