前言:
查询语句的书写顺序
select ===> from ===> where ===> group by ===> having ===> order by ===> limit
查询语句的执行顺序
from ===> where ===> group by ===> having ===> select ===> order by ===> limit
这个很重要,提前再复习一下。
这里如果有一些基础,但是语句不是很熟悉的同学,可以去看另一位大佬的博客:MySQL 有这一篇就够(呕心狂敲37k字,只为博君一点赞!!!)_mysql有这一篇幅就够了-CSDN博客
本篇在其基础上做了一些补充(作者抄了一遍大佬的博客,但还未经博主同意所以没有发布)。希望各位谅解。
一、分组查询:
在此之前我们先准备数据:
sql
drop table if exists emp;
create table emp (
id int primary key auto_increment,
name varchar(20),
role varchar(20),
salary int
);
insert into emp values(null, '张三', '程序员',10000), (null, '李四', '程序员', 1100), (null, '王五', '程序员', 1200);
insert into emp values(null, '赵六', '产品经理', 9000), (null, '田七','产品经理', 9500),(null, '周八', '老板',100000);
此时我们直接进行分组查询:
sql
select role, salary from emp group by role;
运行结果及分析:
所以分组查询往往还是要搭配聚合函数使用的,否则这里的查询结果就没有意义。
使用group by的时候,还可以搭配条件,需要区分清楚,该条件是分组之前的条件,还是分组之后的条件。
1. 搭配 where 使用
查询每个岗位的平均薪资,但排除张三
sql
select role, avg(salary) from emp where name != '张三' group by role;
运行结果:
2. 搭配 having 使用
查询每个岗位的平均薪资,但是排除平均薪资超过2w的结果
sql
select role, avg(salary) from emp group by role having avg(salary) > 20000;
运行结果:
二、联合查询(多表查询)
我们先准备数据:
sql
drop table if exists classes;
drop table if exists student;
drop table if exists course;
drop table if exists score;
create table classes (
id int primary key auto_increment,
name varchar(20),
`desc` varchar(100)
);
create table student (
id int primary key auto_increment,
sn varchar(20),
name varchar(20),
qq_mail varchar(20),
classes_id int
);
create table course (
id int primary key auto_increment,
name varchar(20)
);
create table score (
score decimal(3,1),
student_id int,
course_id int
);
insert into classes(name, `desc`) values
('计算机系2019级1班', '学习了计算机原理、C和Java语言、数据结构和算法'),
('中文系2019级3班','学习了中国传统文学'),
('自动化2019级5班','学习了机械自动化');
insert into student(sn, name, qq_mail, classes_id) values
('09982','黑旋风李逵','xuanfeng@qq.com',1),
('00835','菩提老祖',null,1),
('00391','白素贞',null,1),
('00031','许仙','xuxian@qq.com',1),
('00054','不想毕业',null,1),
('51234','好好说话','say@qq.com',2),
('83223','tellme',null,2),
('09527','老外学中文','foreigner@qq.com',2);
insert into course(name) values
('Java'),('中国传统文化'),('计算机原理'),('语文'),('高阶数学'),('英文');
insert into score(score, student_id, course_id) values
-- 黑旋风李逵
(70.5, 1, 1),(98.5, 1, 3),(33, 1, 5),(98, 1, 6),
-- 菩提老祖
(60, 2, 1),(59.5, 2, 5),
-- 白素贞
(33, 3, 1),(68, 3, 3),(99, 3, 5),
-- 许仙
(67, 4, 1),(23, 4, 3),(56, 4, 5),(72, 4, 6),
-- 不想毕业
(81, 5, 1),(37, 5, 5),
-- 好好说话
(56, 6, 2),(43, 6, 4),(79, 6, 6),
-- tellme
(80, 7, 2),(92, 7, 6);
此时要查询"许仙"的各科成绩:
我们先分析条件:"许仙"是学生表(student)中,而成绩是成绩表(score)中的,所以我们要联合这两张表,也就是将这两张表进行笛卡尔积;之后需要筛选条件,也就是学生表中的id对应成绩表中的id。
sql
select * from student, score where id = student_id;
运行结果:
此时我们可以精简列,之后再加上学生表中姓名为"许仙"同学的条件即可。
sql
select student.name, score.score from student, score where student.id = score.student_id and student.name = '许仙';
1. 基于多表查询和聚合查询的综合使用:
查询所有同学的总成绩及同学的个人信息:
sql
# select * from student, score where student.id = score.student_id;
# select * from student, score where student.id = score.student_id group by student.name;
select student.name, sum(score.score) from student, score where student.id = score.student_id group by student.name;
运行结果:
2. 3张表的联合查询
列出每个同学的每门课程课程名字和分数:
这里会用到3张表,学生表、分数表、课程表。我们可以先利用两张表得到结果,之后再逐步筛选最终结果:
sql
# select * from student, course, score where student.id = score.student_id;
# select * from student, course, score where student.id = score.student_id and score.course_id = course.id;
# select student.name, course.name, score.score from student, course, score where student.id = score.student_id and score.course_id = course.id;
# 此时列名含义模糊,我们可以起别名
select student.id, student.name as student_name, course.name as course_name, score.score from student, course, score where student.id = score.student
_id and score.course_id = course.id;
运行结果:
三、内连接和外连接
上述操作都是内连接,对MySQL而言,进行多表查询还可以使用"外连接"。
如果这两个表里面的记录都是存在对应关系的,内连接和外连接的结果都是一致的;如果存在不对应关系,内连接和外连接就会出现差别。
此时我们先准备数据:
sql
drop table if exists student;
drop table if exists score;
create table student (
id int,
name varchar(20)
);
create table score (
id int,
score int
);
insert into student values (1, '张三'), (2, '李四'), (3, '王五');
insert into score values (1, 90), (2, 80), (4, 70);
注意:此时两张表中的内容并没有一一对应,若一一对应使用内外连接结果都是一样的。
此时就可以发现student表中没有4号同学,而score表中没有3号同学成绩。
1. 内连接
和之前的多表查询一样:
查询学生和学生成绩:
sql
select * from student, score where student.id = score.id;
运行结果:
2. 外连接
我们也是和内连接做相同操作,但是语法格式不一样:
sql
select * from student join score on student.id = score.id;
# 以下方式与上述方式等价
select * from student inner join score on student.id = score.id;
# inner 可以省略
运行结果:
这就是外连接,join 代表逗号,where 换成 on,目前这两种写法等价。
2.1 左外连接
左外连接使用left表示:
sql
select * from student left join score on student.id = score.id;
运行结果:
左外连接:以左侧表为基准,保证左侧表每个数据都会出现在最终结果中,若在右侧表中不存在,对应的列就会替换为null。
2.2 右外连接
右外连接使用right表示:
sql
select * from student right join score on student.id = score.id;
运行结果:
右外连接:以右侧表为基准,保证右侧表每个数据都会出现在最终结果中,若在左侧表中不存在,对应的列就会替换为null。
2.3 总结外连接
如下图:
其实有全外连接,但是MySQL不支持。
四、自连接:
一张表,自己和自己进行笛卡尔积。
有的时候,我们需要进行 行和行 之间的比较,而sql只能进行 列和列 之间的比较。有的时候,可能会涉及到行和行比较的需求,就可以使用自连接,把 行关系 转换为 列关系。
数据准备:
sql
drop table if exists classes;
drop table if exists student;
drop table if exists course;
drop table if exists score;
create table classes (
id int primary key auto_increment,
name varchar(20),
`desc` varchar(100)
);
create table student (
id int primary key auto_increment,
sn varchar(20),
name varchar(20),
qq_mail varchar(20),
classes_id int
);
create table course (
id int primary key auto_increment,
name varchar(20)
);
create table score (
score decimal(3,1),
student_id int,
course_id int
);
insert into classes(name, `desc`) values
('计算机系2019级1班', '学习了计算机原理、C和Java语言、数据结构和算法'),
('中文系2019级3班','学习了中国传统文学'),
('自动化2019级5班','学习了机械自动化');
insert into student(sn, name, qq_mail, classes_id) values
('09982','黑旋风李逵','xuanfeng@qq.com',1),
('00835','菩提老祖',null,1),
('00391','白素贞',null,1),
('00031','许仙','xuxian@qq.com',1),
('00054','不想毕业',null,1),
('51234','好好说话','say@qq.com',2),
('83223','tellme',null,2),
('09527','老外学中文','foreigner@qq.com',2);
insert into course(name) values
('Java'),('中国传统文化'),('计算机原理'),('语文'),('高阶数学'),('英文');
insert into score(score, student_id, course_id) values
-- 黑旋风李逵
(70.5, 1, 1),(98.5, 1, 3),(33, 1, 5),(98, 1, 6),
-- 菩提老祖
(60, 2, 1),(59.5, 2, 5),
-- 白素贞
(33, 3, 1),(68, 3, 3),(99, 3, 5),
-- 许仙
(67, 4, 1),(23, 4, 3),(56, 4, 5),(72, 4, 6),
-- 不想毕业
(81, 5, 1),(37, 5, 5),
-- 好好说话
(56, 6, 2),(43, 6, 4),(79, 6, 6),
-- tellme
(80, 7, 2),(92, 7, 6);
显示所有"计算机原理"成绩比"Java"成绩高的同学成绩信息:
根据观察:也就是找课程3的成绩比课程1的成绩更高的同学信息(course id 3 比 course id 1 成绩高的同学)。
此时我们直接进行自连接:
sql
select * from score, score;
运行结果:
可以发现报错,此时要起一个别名。
sql
select * from score as s1, score as s2;
此时我们关注学生信息,所以可以确定筛选条件:
sql
select * from score as s1, score as s2 where s1.student_id = s2.student_id;
运行结果:
此时就可以确定最终查询语句:
sql
# select * from score as s1, score as s2 where s1.student_id = s2.student_id and s1.course_id = 3 and s2.course_id
= 1 and s1.score > s2.score;
# 最后精简一下列
select s1.student_id, s1.score as F3, s2.score as F1 from score as s1, score as s2 where s1.student_id = s2.stude
nt_id and s1.course_id = 3 and s2.course_id = 1 and s1.score > s2.score;
运行结果:
如果此时想要学生名字,就可以拿这个表和学生表做笛卡尔积;如果此时想要课程名字,就可以拿这个表和课程表做笛卡尔积。
五、子查询
这个在前面提到的文章讲到过,我们大概说一下,因为用处很小。
我们可以通过这两句SQL来完成,当然也可以通过合并SQL语句完成,也就是子查询。
1. 单行子查询
只返回一行查询记录。
查询与"不想毕业"同学的同班同学:
sql
# 先利用第一个查询语句缩小范围
select classes_id from student where name = '不想毕业';
# 之后利用其结果确定最终结果
select name from student where classes_id = 1 and name != '不想毕业';
运行结果:
通过合并SQL语句来完成:
sql
select name from student where classes_id = (select classes_id from student where name = '不想毕业') and name != '不想毕业';
2. 多行子查询
返回多行记录的子查询。尝尝搭配in使用。
查询"语文"或"英文"课程的成绩信息:
应该在course和score中进行查询,联合查询也是没有问题的。这里先展示联合查询:
sql
select * from course, score where course.id = score.course_id and (course.name = '英文' or course.name = '语文');
运行结果:
分步查询步骤为:
1.先通过课程名字找到课程id
2.再通过课程id在分数表中进行查询
sql
select id from course where name = '语文' or name = '英文';
select score.student_id, score.course_id, score.score from score where score.course_id in (4, 6);
运行结果:
子查询使用in关键字:
sql
select score.student_id, score.course_id, score.score from score where score.course_id in (select id from course where name = '语文' or name = '英文');
运行结果:
六、合并查询(union关键字)
把多个sql查询结果集合合并到一起成为合并查询。这里我们主要了解union关键字。
查询id小于3,或者名字为"英文"的课程:
sql
select * from course where id < 3 or name = '英文';
# 等价于
select * from course where id < 3 union select * from course where name = '英文';
运行结果:
union可以充当or的作用,但是union允许把两个不同的表合并在一起。
合并的两个sql结果集的列,需要匹配。
这里我们在准备其他的表:
sql
drop table if exists score;
drop table if exists student;
create table student1(
id int,
name varchar(20)
);
create table student2(
studentId int,
studentName varchar(20)
);
insert into student1 values(1, '张三'), (2, '李四');
insert into student2 values(1, '张三'), (3, '王五');
查询两张表的学生信息:
sql
select * from student1 union select * from student2;
运行结果:
可以发现:使用union进行合并的时候,是会对结果进行去重的,若不想去重,可以使用union all。
sql
select * from student1 union all select * from student2;
运行结果:
总结:
查询语句的书写顺序
select ===> from ===> where ===> group by ===> having ===> order by ===> limit
查询语句的执行顺序
from ===> where ===> group by ===> having ===> select ===> order by ===> limit
重要的事情说三遍,虽然这些东西可能在实际上运用的很少,但是我们也必须了解,毕竟技多不压身。