查询进阶
同结构不同表查询插入
- 给定两个相同结构表,内容可以由查询功能插入
- insert into student2 select * from student1;
-- 创建表1
mysql> create table student1(id int,name varchar(10));
Query OK, 0 rows affected (0.05 sec)
-- 创建表2
mysql> create table student2(id int,name varchar(10));
Query OK, 0 rows affected (0.04 sec)
-- 在表1中插入数据
mysql> insert into student1 values(1,"zhangsan"),(2,"lisi"),(3,"wangwu");
Query OK, 3 rows affected (0.01 sec)
Records: 3 Duplicates: 0 Warnings: 0
-- 查看表1数据
mysql> select * from student1;
+------+----------+
| id | name |
+------+----------+
| 1 | zhangsan |
| 2 | lisi |
| 3 | wangwu |
+------+----------+
3 rows in set (0.00 sec)
-- 写法->:直接以查询结果插入表2
mysql> insert into student2 select * from student1;
Query OK, 3 rows affected (0.01 sec)
Records: 3 Duplicates: 0 Warnings: 0
-- 查看表2数据
mysql> select * from student2;
+------+----------+
| id | name |
+------+----------+
| 1 | zhangsan |
| 2 | lisi |
| 3 | wangwu |
+------+----------+
3 rows in set (0.00 sec)
聚合查询
聚合函数
- 行与行之间需要运算,可以使用sql提供的函数;
函数 | 说明 |
---|---|
count([distinct] expr) | 返回查询到的数据的数量(DISTINCT 表示去重统计) |
sum([distinct] expr) | 返回查询到的数据的 总和,不是数字没有意义 |
avg([distinct] expr) | 返回查询到的数据的 平均值,不是数字没有意义 |
max([distinct] expr) | 返回查询到的数据的 最大值,不是数字没有意义 |
min([distinct] expr) | 返回查询到的数据的 最小值,不是数字没有意义 |
- DISTINCT 为可选参数,表示对数据进行去重后再执行聚合操作。
- 给出成绩表举例
mysql> select * from exam_result;
+---------+---------+------+---------+
| name | chinese | math | english |
+---------+---------+------+---------+
| 学生1 | 67.0 | 98.0 | 56.0 |
| 学生2 | 87.5 | 78.0 | 77.0 |
| 学生3 | 88.5 | 98.0 | 90.0 |
| 学生4 | 55.5 | 85.0 | 45.0 |
| NULL | NULL | NULL | NULL |
+---------+---------+------+---------+
5 rows in set (0.00 sec)
2.select count(*)...
-
先执行select*再进行count统计
-
select count(name)…先查询name再统计name有多少行
-
(distinct name)会进行名字去重
-
*号会统计null值 而 单列不计算null值
-
count() 括号要紧紧挨着count 不能有空格
-- 写法->:count(*)计算null值
mysql> select count(*) from exam_result;
+----------+
| count(*) |
+----------+
| 5 |
+----------+
1 row in set (0.00 sec)
-- 写法->:count(name)不计算null值
mysql> select count(name) from exam_result;
+-------------+
| count(name) |
+-------------+
| 4 |
+-------------+
1 row in set (0.01 sec)
3 select sum(*)...
- null 不会去参与计算
- sql会将数据转换成double类型计算,数字字符串也可以被转换,非数字字符串将转换失败
-- 写法->:语文列成绩相加
mysql> select sum(chinese) from exam_result;
+--------------+
| sum(chinese) |
+--------------+
| 298.5 |
+--------------+
1 row in set (0.00 sec)
-- 写法->:先将列数据相加,再去将行相加
mysql> select sum(chinese + math + english) from exam_result;
+-------------------------------+
| sum(chinese + math + english) |
+-------------------------------+
| 925.5 |
+-------------------------------+
1 row in set (0.00 sec)
4 select avg(chinese)... -- 和sum同理
-- 写法-> 平均值:
mysql> select avg(chinese) from exam_result;
+--------------+
| avg(chinese) |
+--------------+
| 74.62500 |
+--------------+
5 select max(chinese)... -- 和sum同理
-- 写法-> 最大值:
mysql> select max(chinese) from exam_result;
+--------------+
| max(chinese) |
+--------------+
| 88.5 |
+--------------+
1 row in set (0.00 sec)
6 select min(chinese)... -- 和sum同理
-- 写法-> 最小值:
mysql> select min(chinese) from exam_result;
+--------------+
| min(chinese) |
+--------------+
| 55.5 |
+--------------+
1 row in set (0.00 sec)
group by
- 假设给出一张员工薪资表
-- 创建表
mysql> create table company(id int primary key auto_increment,
name varchar(20),role varchar(20),salary int);
-- 插入后查看表
mysql> select * from company;
+----+--------+-----------+--------+
| id | name | role | salary |
+----+--------+-----------+--------+
| 1 | 张三 | 程序员 | 10000 |
| 2 | 李四 | 程序员 | 12000 |
| 3 | 王五 | 设计师 | 8000 |
| 4 | 赵六 | 设计师 | 8500 |
| 5 | 田七 | 设计师 | 9000 |
| 6 | 老八 | 老板 | 100000 |
+----+--------+-----------+--------+
6 rows in set (0.00 sec)
1.平均值的分组计算
-- 写法-> 所有人的平均值:
mysql> select avg(salary) from company;
+-------------+
| avg(salary) |
+-------------+
| 24583.3333 |
+-------------+
1 row in set (0.00 sec)
- 如果直接进行平均值计算,乍一看平均薪资竟然有两万多,显然这不合理;所以平均值需要进行分组计算:
select role, avg(salary) from company group by role;
- group by role 根据role来分组
-- 写法->分组平均值:
mysql> select role, avg(salary) from company group by role;
+-----------+-------------+
| role | avg(salary) |
+-----------+-------------+
| 程序员 | 11000.0000 |
| 老板 | 100000.0000 |
| 设计师 | 8500.0000 |
+-----------+-------------+
3 rows in set (0.00 sec)
- 可以在分组之前增加条件
-- 写法->:先排除张三,再分组查询
mysql> select role ,avg(salary) from company where name != "张三" group by role;
+-----------+-------------+
| role | avg(salary) |
+-----------+-------------+
| 程序员 | 12000.0000 |
| 老板 | 100000.0000 |
| 设计师 | 8500.0000 |
+-----------+-------------+
3 rows in set (0.01 sec)
- 可以在分组之后增加条件
- having
-- 写法->:先分组,再筛选平均小于20000
mysql> select role,avg(salary) from company group by role having avg(salary) < 20000;
+-----------+-------------+
| role | avg(salary) |
+-----------+-------------+
| 程序员 | 11000.0000 |
| 设计师 | 8500.0000 |
+-----------+-------------+
2 rows in set (0.01 sec)
-- 写法->:having count(role) 就可以得到各职位的人数;
- 当然,前后一起用也可以的
联合查询
笛卡尔积 “排列组合”
-- 给定班级表内容
mysql> select * from class;
+----+-----------------+
| id | name |
+----+-----------------+
| 1 | 一年级一班 |
| 2 | 一年级二班 |
+----+-----------------+
3 rows in set (0.00 sec)
-- 给定学生表内容
mysql> select * from student;
+----+--------+---------+
| id | name | classid |
+----+--------+---------+
| 1 | 张三 | 1 |
| 2 | 李四 | 2 |
| 3 | 王五 | 2 |
+----+--------+---------+
3 rows in set (0.00 sec)
-- 如果直接两表拼在一起,两表就会排列组合,把所有数据都组合一遍,
甚至生成一些无用的数据;因此我们应当过滤一些无意义数据;
mysql> select * from class,student;
+----+-----------------+----+--------+---------+
| id | name | id | name | classid |
+----+-----------------+----+--------+---------+
| 1 | 一年级一班 | 1 | 张三 | 1 |
| 2 | 一年级二班 | 1 | 张三 | 1 |
| 1 | 一年级一班 | 2 | 李四 | 2 |
| 2 | 一年级二班 | 2 | 李四 | 2 |
| 1 | 一年级一班 | 3 | 王五 | 2 |
| 2 | 一年级二班 | 3 | 王五 | 2 |
+----+-----------------+----+--------+---------+
9 rows in set (0.00 sec)
-- 筛选两表合法内容
内部关联
- where 表 . 列名 = 表 . 列名
-- 写法->:给定内部条件进行筛选
mysql> select * from class,student where
class.id = student.classid;
+----+-----------------+----+--------+---------+
| id | name | id | name | classid |
+----+-----------------+----+--------+---------+
| 1 | 一年级一班 | 1 | 张三 | 1 |
| 2 | 一年级二班 | 2 | 李四 | 2 |
| 2 | 一年级二班 | 3 | 王五 | 2 |
+----+-----------------+----+--------+---------+
3 rows in set (0.00 sec)
-- 写法->:可以对列再进行精简
mysql> select class.name, student.id ,student.name
from class,student where class.id = student.classid;
+-----------------+----+--------+
| name | id | name |
+-----------------+----+--------+
| 一年级一班 | 1 | 张三 |
| 一年级二班 | 2 | 李四 |
| 一年级二班 | 3 | 王五 |
+-----------------+----+--------+
3 rows in set (0.00 sec)
- 内部关联还有一种写法,只是把连接两表的 , 换成了join / inner join,把where换成了on。
select class.name, student.id ,student.name from class join student on class.id = student.classid;
外部关联
- 创建两个有一定关联的表,但是其中有特殊情况导致一些数据不存在关联
-- 学生表
mysql> select * from student;
+------+--------+
| id | name |
+------+--------+
| 1 | 张三 |
| 2 | 李四 |
| 3 | 王五 |
+------+--------+
3 rows in set (0.00 sec)
-- 分数表
mysql> select * from score;
+------+-------+
| id | score |
+------+-------+
| 1 | 90.0 |
| 2 | 78.0 |
| 4 | 64.0 |
+------+-------+
3 rows in set (0.00 sec)
- 如果直接以内部关联的方式查询,查询不到无关联的数据
-- 以内部关联方式查询
mysql> select * from student inner join
score on student.id = score.id;
+------+--------+------+-------+
| id | name | id | score |
+------+--------+------+-------+
| 1 | 张三 | 1 | 90.0 |
| 2 | 李四 | 2 | 78.0 |
+------+--------+------+-------+
2 rows in set (0.00 sec)
- 左外连接
- 确保左表student能够被完全表示,外部关联的score表数据用null填充。
-- 写法->:left join
mysql> select * from student left join
score on student.id = score.id;
+------+--------+------+-------+
| id | name | id | score |
+------+--------+------+-------+
| 1 | 张三 | 1 | 90.0 |
| 2 | 李四 | 2 | 78.0 |
| 3 | 王五 | NULL | NULL |
+------+--------+------+-------+
3 rows in set (0.00 sec)
- 右外连接
- 确保右表score能够被完全表示,外部关联的student表数据用null填充。
-- 写法->:right join
mysql> select * from student right join
score on student.id = score.id;
+------+--------+------+-------+
| id | name | id | score |
+------+--------+------+-------+
| 1 | 张三 | 1 | 90.0 |
| 2 | 李四 | 2 | 78.0 |
| NULL | NULL | 4 | 64.0 |
+------+--------+------+-------+
3 rows in set (0.00 sec)
自连接
- 行与行之间的比较
- 给出学生表和分数表
-- 学生表
mysql> select * from student;
+------+--------+
| id | name |
+------+--------+
| 1 | 张三 |
| 2 | 李四 |
| 3 | 王五 |
+------+--------+
3 rows in set (0.00 sec)
-- 分数表
mysql> select * from score;
+------------+-------+--------+
| student_id | score | course |
+------------+-------+--------+
| 1 | 88.0 | 语文 |
| 1 | 97.0 | 数学 |
| 1 | 65.5 | 英语 |
| 2 | 77.0 | 语文 |
| 2 | 55.0 | 数学 |
| 2 | 67.5 | 英语 |
| 3 | 49.0 | 语文 |
| 3 | 80.0 | 数学 |
| 3 | 40.0 | 英语 |
+------------+-------+--------+
9 rows in set (0.00 sec)
- 如果想要在行与行之间比较分数,需要自连接,将行转换成列的形式进行比较。
- 自连接由于表名重复,需要使用别名
select * from score as s1,score as s2;
mysql> 写法->:select * from score as s1,score as s2;
+------------+-------+--------+------------+-------+--------+
| student_id | score | course | student_id | score | course |
+------------+-------+--------+------------+-------+--------+
| 1 | 88.0 | 语文 | 1 | 88.0 | 语文 |
| 1 | 97.0 | 数学 | 1 | 88.0 | 语文 |
| 1 | 65.5 | 英语 | 1 | 88.0 | 语文 |
| 2 | 77.0 | 语文 | 1 | 88.0 | 语文 |
| 2 | 55.0 | 数学 | 1 | 88.0 | 语文 |
| 2 | 67.5 | 英语 | 1 | 88.0 | 语文 |
| 3 | 49.0 | 语文 | 1 | 88.0 | 语文 |
| 3 | 80.0 | 数学 | 1 | 88.0 | 语文 |
| 3 | 40.0 | 英语 | 1 | 88.0 | 语文 |
| 1 | 88.0 | 语文 | 1 | 97.0 | 数学 |
| 1 | 97.0 | 数学 | 1 | 97.0 | 数学 |
| 1 | 65.5 | 英语 | 1 | 97.0 | 数学 |
| 2 | 77.0 | 语文 | 1 | 97.0 | 数学 |
| 2 | 55.0 | 数学 | 1 | 97.0 | 数学 |
| 2 | 67.5 | 英语 | 1 | 97.0 | 数学 |
| 3 | 49.0 | 语文 | 1 | 97.0 | 数学 |
表太大简略了,共81
+------------+-------+--------+------------+-------+--------+
81 rows in set (0.01 sec)
- 明显可见,在排列组合的过程中,就会存在可进行比较的不同学科出现在了同一行当中,进而转化成了列的比较。
- 为了简化,不妨将同一个人的语文成绩和数学成绩进行比较
-- 查看同一个人的语文成绩和数学成绩
mysql> select * from score as s1,score as s2 where
s1.student_id = s2.student_id
and s1.course = '语文'
and s2.course = '数学';
+------------+-------+--------+------------+-------+--------+
| student_id | score | course | student_id | score | course |
+------------+-------+--------+------------+-------+--------+
| 1 | 88.0 | 语文 | 1 | 97.0 | 数学 |
| 2 | 77.0 | 语文 | 2 | 55.0 | 数学 |
| 3 | 49.0 | 语文 | 3 | 80.0 | 数学 |
+------------+-------+--------+------------+-------+--------+
3 rows in set (0.00 sec)
-- 写法->:只看同一个人的语文和数学成绩,其中语文成绩比数学大的
mysql> select * from score as s1,score as s2 where
s1.student_id = s2.student_id
and s1.course = '语文'
and s2.course = '数学'
and s1.score >s2.score;
+------------+-------+--------+------------+-------+--------+
| student_id | score | course | student_id | score | course |
+------------+-------+--------+------------+-------+--------+
| 2 | 77.0 | 语文 | 2 | 55.0 | 数学 |
+------------+-------+--------+------------+-------+--------+
1 row in set (0.00 sec)
-- 写法->:进行列化简
mysql> select s1.student_id,s1.course,s1.score,
s2.course,s2.score
from score as s1,score as s2 where
s1.student_id = s2.student_id
and s1.course = '语文'
and s2.course = '数学'
and s1.score >s2.score;
+------------+--------+-------+--------+-------+
| student_id | course | score | course | score |
+------------+--------+-------+--------+-------+
| 2 | 语文 | 77.0 | 数学 | 55.0 |
+------------+--------+-------+--------+-------+
1 row in set (0.00 sec)
子查询
- 单行子查询:返回一行记录的子查询
-- 一个学生表
mysql> select * from student;
+------+--------+---------+
| id | name | classid |
+------+--------+---------+
| 1 | 张三 | 1 |
| 2 | 李四 | 1 |
| 3 | 王五 | 1 |
| 4 | 赵四 | 1 |
| 5 | 田七 | 2 |
| 6 | 周八 | 2 |
+------+--------+---------+
6 rows in set (0.00 sec)
- 要想查询某一学生的同班学生有谁
-- 写法->:找出张三所在的班级
mysql> select classid from student where name = '张三';
+---------+
| classid |
+---------+
| 1 |
+---------+
1 row in set (0.00 sec)
-- 写法->:根据班级查询成员
mysql> select * from student where classid = 1 and name != '张三';
+------+--------+---------+
| id | name | classid |
+------+--------+---------+
| 2 | 李四 | 1 |
| 3 | 王五 | 1 |
| 4 | 赵四 | 1 |
+------+--------+---------+
3 rows in set (0.00 sec)
3.合并1和2;将得到1的语句替换2中的1
-- 写法->: 替换1
mysql> select * from student where classid =
( select classid from student where name = '张三')
and name != '张三';
+------+--------+---------+
| id | name | classid |
+------+--------+---------+
| 2 | 李四 | 1 |
| 3 | 王五 | 1 |
| 4 | 赵四 | 1 |
+------+--------+---------+
3 rows in set (0.01 sec)
- 多行子查询:
-- 给出3张表,分别记录学生,课程,分数;
mysql> select * from student;
+------+--------+
| id | name |
+------+--------+
| 1 | 张三 |
| 2 | 李四 |
| 3 | 王五 |
| 4 | 赵四 |
| 5 | 田七 |
| 6 | 周八 |
+------+--------+
6 rows in set (0.00 sec)
mysql> select * from course;
+------+--------------------------+
| id | courseName |
+------+--------------------------+
| 1 | 计算机科学与技术 |
| 2 | 大学英语 |
| 3 | 大学物理 |
| 4 | 软件工程 |
| 5 | 线性代数 |
| 6 | 军事理论 |
+------+--------------------------+
6 rows in set (0.00 sec)
mysql> select * from score;
+-----------+----------+-------+
| studentid | courseid | score |
+-----------+----------+-------+
| 1 | 1 | 88.0 |
| 1 | 2 | 70.0 |
| 1 | 5 | 78.0 |
| 2 | 1 | 68.0 |
| 2 | 2 | 54.0 |
| 2 | 5 | 90.0 |
| 3 | 2 | 68.0 |
| 3 | 3 | 54.0 |
| 3 | 4 | 90.0 |
| 4 | 4 | 68.0 |
| 4 | 5 | 54.0 |
| 4 | 6 | 90.0 |
| 5 | 3 | 49.0 |
| 5 | 5 | 54.0 |
| 5 | 2 | 60.0 |
| 6 | 1 | 49.0 |
| 6 | 6 | 74.0 |
| 6 | 2 | 88.0 |
+-----------+----------+-------+
18 rows in set (0.00 sec)
- 现需要找出指定课程所有学生分数
做法一,联合查询
先找到对应课程id,再通过课程id找学生和分数
-- 找出大学英语和大学物理对应的id
mysql> select id from course where courseName = '大学英语'
or courseName = '大学物理';
+------+
| id |
+------+
| 2 |
| 3 |
+------+
2 rows in set (0.00 sec)
-- 筛选得到对应学生 使用 -列 in()进行筛选
mysql> select studentid,courseid ,score from score where courseid in(2 , 3);
+-----------+----------+-------+
| studentid | courseid | score |
+-----------+----------+-------+
| 1 | 2 | 70.0 |
| 2 | 2 | 54.0 |
| 3 | 2 | 68.0 |
| 3 | 3 | 54.0 |
| 5 | 3 | 49.0 |
| 5 | 2 | 60.0 |
| 6 | 2 | 88.0 |
+-----------+----------+-------+
7 rows in set (0.01 sec)
做法二:将上方法进行套娃,替换 (2,3)
mysql> select studentid,courseid ,score from score
where courseid in
(select id from course where courseName = '大学英语' or courseName = '大学物理');
+-----------+----------+-------+
| studentid | courseid | score |
+-----------+----------+-------+
| 1 | 2 | 70.0 |
| 2 | 2 | 54.0 |
| 3 | 2 | 68.0 |
| 3 | 3 | 54.0 |
| 5 | 3 | 49.0 |
| 5 | 2 | 60.0 |
| 6 | 2 | 88.0 |
+-----------+----------+-------+
7 rows in set (0.00 sec)
合并查询
- 为了合并多个select的执行结果,可以使用集合操作符 union,union all。
-- or的同一表下查询使用
mysql> select * from course where id < 3 or coursename = "军事理论";
+------+--------------------------+
| id | courseName |
+------+--------------------------+
| 1 | 计算机科学与技术 |
| 2 | 大学英语 |
| 6 | 军事理论 |
+------+--------------------------+
3 rows in set (0.00 sec)
-- ->写法:使用union表达式
mysql> select * from course where id < 3 union select * from course where coursename = "军事理论";
+------+--------------------------+
| id | courseName |
+------+--------------------------+
| 1 | 计算机科学与技术 |
| 2 | 大学英语 |
| 6 | 军事理论 |
+------+--------------------------+
3 rows in set (0.01 sec)
- union允许多个表合并
- 如果类型是相同的,可以合并,但列名取其一
- 成功合并如果有相同行,则会进行去重!! union all不会去重
-- 相同类型合并
mysql> select * from course where id < 3 union select * from student where id < 4;
+------+--------------------------+
| id | courseName |
+------+--------------------------+
| 1 | 计算机科学与技术 |
| 2 | 大学英语 |
| 1 | 张三 |
| 2 | 李四 |
| 3 | 王五 |
+------+--------------------------+
5 rows in set (0.00 sec)
- 如果类型不同,会报错
-- 不同类型合并
mysql> select * from course where id < 3 union select * from score where studentid < 4;
ERROR 1222 (21000): The used SELECT statements have a different number of columns