【MySQL】MySQL表的增删改查(进阶)

news2024/11/13 13:31:52

请添加图片描述

✨个人主页:bit me👇
✨当前专栏:MySQL数据库👇
✨算法专栏:算法基础👇
✨每日一语:悟已往之不谏,知来者之可追。实迷途其未远,觉今是而昨非。

目 录

  • 🎄一. 数据库约束
  • 🌲二. 表的设计
  • 🌳三. 新增
  • 🌴四. 查询
    • 🏳️4.1 聚合函数(以下都是聚合查询)
    • 🏴4.2 分组查询
    • 🏁4.3 内连接(以下都是联合查询)
    • 🚩4.4 外连接
    • 🏳️‍🌈4.5 自连接
    • 🏴‍☠️4.6 子查询
    • 🏳️‍⚧️4.7 合并查询

🎄一. 数据库约束

  • 约束就是数据库可以让程序猿定义一些对数据的限制规则,数据库会在插入 / 修改数据的时候按照这些规则对数据进行校验,如果校验不通过,就直接报错。

约束的本质就是让我们及时发现数据中的错误,更好的保证数据的正确性

约束类型:

  • NOT NULL - 指示某列不能存储 NULL 值。(必填项)
  • UNIQUE - 保证某列的每行必须有唯一的值。
  • DEFAULT - 规定没有给列赋值时的默认值。(可以通过default修改这个默认值)
  • PRIMARY KEY - NOT NULL 和 UNIQUE 的结合。确保某列(或两个列多个列的结合)有唯一标识,有助于更容易更快速地找到表中的一个特定的记录。
  • FOREIGN KEY - 保证一个表中的数据匹配另一个表中的值的参照完整性。
  • CHECK - 保证列中的值符合指定的条件。对于MySQL数据库,对CHECK子句进行分析,但是忽略CHECK子句

①:NOT NULL

  • 初始情况下,一个表允许为NULL。
mysql> desc student;
+-------+-------------+------+-----+---------+-------+
| Field | Type        | Null | Key | Default | Extra |
+-------+-------------+------+-----+---------+-------+
| id    | int(11)     | YES  |     | NULL    |       |
| name  | varchar(20) | YES  |     | NULL    |       |
+-------+-------------+------+-----+---------+-------+
2 rows in set (0.00 sec)

mysql> insert into student values(NULL, NULL);
Query OK, 1 row affected (0.00 sec)

加上 NOT NULL 约束之后,就不再允许插入空值

mysql> drop table student;
Query OK, 0 rows affected (0.01 sec)

mysql> create table student (id int not null, name varchar(20));
Query OK, 0 rows affected (0.01 sec)

mysql> desc student;
+-------+-------------+------+-----+---------+-------+
| Field | Type        | Null | Key | Default | Extra |
+-------+-------------+------+-----+---------+-------+
| id    | int(11)     | NO   |     | NULL    |       |
| name  | varchar(20) | YES  |     | NULL    |       |
+-------+-------------+------+-----+---------+-------+
2 rows in set (0.00 sec)

再尝试插入空值就会报错!

mysql> insert into student values (NULL, NULL);
ERROR 1048 (23000): Column 'id' cannot be null

②:UNIQUE 唯一性

  • 默认情况下,表里的数据都是可以重复的
mysql> drop table student;
Query OK, 0 rows affected (0.01 sec)

mysql> create table student (id int, name varchar(20));
Query OK, 0 rows affected (0.01 sec)

mysql> insert into student values(1, '张三');
Query OK, 1 row affected (0.00 sec)

mysql> insert into student values(1, '张三');
Query OK, 1 row affected (0.00 sec)

mysql> insert into student values(1, '张三');
Query OK, 1 row affected (0.00 sec)

mysql> select * from student;
+------+--------+
| id   | name   |
+------+--------+
|    1 | 张三   |
|    1 | 张三   |
|    1 | 张三   |
+------+--------+
3 rows in set (0.00 sec)

没有约束的表和有约束的表对比

mysql> desc student;
+-------+-------------+------+-----+---------+-------+
| Field | Type        | Null | Key | Default | Extra |
+-------+-------------+------+-----+---------+-------+
| id    | int(11)     | YES  |     | NULL    |       |
| name  | varchar(20) | YES  |     | NULL    |       |
+-------+-------------+------+-----+---------+-------+
2 rows in set (0.00 sec)

mysql> drop table student;
Query OK, 0 rows affected (0.01 sec)

mysql> create table student (id int unique, name varchar(20));
Query OK, 0 rows affected (0.01 sec)

mysql> desc student;
+-------+-------------+------+-----+---------+-------+
| Field | Type        | Null | Key | Default | Extra |
+-------+-------------+------+-----+---------+-------+
| id    | int(11)     | YES  | UNI | NULL    |       |
| name  | varchar(20) | YES  |     | NULL    |       |
+-------+-------------+------+-----+---------+-------+
2 rows in set (0.00 sec)

当我们想再次输入同样的数据的时候发现报错

mysql> insert into student values(1, '张三');
Query OK, 1 row affected (0.00 sec)

mysql> insert into student values(1, '张三');
ERROR 1062 (23000): Duplicate entry '1' for key 'id'

Duplicate --> 重复, entry --> 条目(不是入口) 触发了 UNIQUE 约束

③:DEFAULT

  • 设定默认值

默认的默认值是NULL,可以通过 default 约束来修改这里的默认值的取值

mysql> drop table student;
Query OK, 0 rows affected (0.01 sec)

mysql> create table student (id int, name varchar(20));
Query OK, 0 rows affected (0.01 sec)

mysql> desc student;
+-------+-------------+------+-----+---------+-------+
| Field | Type        | Null | Key | Default | Extra |
+-------+-------------+------+-----+---------+-------+
| id    | int(11)     | YES  |     | NULL    |       |
| name  | varchar(20) | YES  |     | NULL    |       |
+-------+-------------+------+-----+---------+-------+
2 rows in set (0.00 sec)

对表进行指定列插入的时候,会涉及到默认值的情况的

mysql> insert into student (id) values (1);
Query OK, 1 row affected (0.00 sec)

指定了 id 这一列插入,此时的 name 就是按照默认值来走的

mysql> select * from student;
+------+------+
| id   | name |
+------+------+
|    1 | NULL |
+------+------+
1 row in set (0.00 sec)

通过 default 约束来修改默认值

mysql> drop table student;
Query OK, 0 rows affected (0.00 sec)

mysql> create table student (id int default 0, name varchar(20) default '未命名');
Query OK, 0 rows affected (0.01 sec)

mysql> desc student;
+-------+-------------+------+-----+-----------+-------+
| Field | Type        | Null | Key | Default   | Extra |
+-------+-------------+------+-----+-----------+-------+
| id    | int(11)     | YES  |     | 0         |       |
| name  | varchar(20) | YES  |     | 未命名    |       |
+-------+-------------+------+-----+-----------+-------+
2 rows in set (0.00 sec)

mysql> insert into student (id) values (1);
Query OK, 1 row affected (0.00 sec)

mysql> select * from student;
+------+-----------+
| id   | name      |
+------+-----------+
|    1 | 未命名    |
+------+-----------+
1 row in set (0.00 sec)

mysql> insert into student (name) values ('张三');
Query OK, 1 row affected (0.00 sec)

mysql> select * from student;
+------+-----------+
| id   | name      |
+------+-----------+
|    1 | 未命名    |
|    0 | 张三      |
+------+-----------+
2 rows in set (0.00 sec)

④:PRIMARY KEY --> 主键

  • 在设计一个表的时候,非常重要的一个列,表示一条记录的身份标识,用来区分这条记录和别的记录的

注:

  • 不能为空,相当于 NOT NULL
  • 不能重复,相当于UNIQUE
  • 一个表里只能有一个主键
mysql> drop table student;
Query OK, 0 rows affected (0.00 sec)

mysql>  create table student (id int primary key,name varchar(20));
Query OK, 0 rows affected (0.01 sec)

mysql> desc student;
+-------+-------------+------+-----+---------+-------+
| Field | Type        | Null | Key | Default | Extra |
+-------+-------------+------+-----+---------+-------+
| id    | int(11)     | NO   | PRI | NULL    |       |
| name  | varchar(20) | YES  |     | NULL    |       |
+-------+-------------+------+-----+---------+-------+
2 rows in set (0.00 sec)

添加元素:

mysql> insert into student values(null, null);
ERROR 1048 (23000): Column 'id' cannot be null
mysql> insert into student values(1,'张三');
Query OK, 1 row affected (0.00 sec)

mysql> insert into student values(1,'张三');
ERROR 1062 (23000): Duplicate entry '1' for key 'PRIMARY'

主键不能为空,主键的值也不能重复

由于主键必须要填,还不能重复,MySQL为了方便大家填写主键,内置了一个功能 ”自增主键“ ,帮助我们自动生成主键的值,就不用程序猿自己来保证了

mysql> drop table student;
Query OK, 0 rows affected (0.01 sec)

mysql> create table student (id int primary key auto_increment, name varchar(20));
Query OK, 0 rows affected (0.01 sec)

mysql> desc student;
+-------+-------------+------+-----+---------+----------------+
| Field | Type        | Null | Key | Default | Extra          |
+-------+-------------+------+-----+---------+----------------+
| id    | int(11)     | NO   | PRI | NULL    | auto_increment |
| name  | varchar(20) | YES  |     | NULL    |                |
+-------+-------------+------+-----+---------+----------------+
2 rows in set (0.00 sec)

当前 id 是自增主键,不同于主键,可以使用 null

mysql> insert into student values (null,'张三');
Query OK, 1 row affected (0.00 sec)
  • 如果写的是具体数值,就是手动指定 id
  • 如果写的是 null ,就是让 MySQL 按照自增主键自动生成
mysql> select * from student;
+----+--------+
| id | name   |
+----+--------+
|  1 | 张三   |
+----+--------+
1 row in set (0.00 sec)

mysql> insert into student values (null,'张三');
Query OK, 1 row affected (0.00 sec)

mysql> insert into student values (null,'张三');
Query OK, 1 row affected (0.00 sec)

mysql> insert into student values (null,'张三');
Query OK, 1 row affected (0.00 sec)

mysql> select * from student;
+----+--------+
| id | name   |
+----+--------+
|  1 | 张三   |
|  2 | 张三   |
|  3 | 张三   |
|  4 | 张三   |
+----+--------+
4 rows in set (0.00 sec)

中间插入手动增加之后自增从当前的数字自增

mysql> insert into student values (100,'张三');
Query OK, 1 row affected (0.00 sec)

mysql>  select * from student;
+-----+--------+
| id  | name   |
+-----+--------+
|   1 | 张三   |
|   2 | 张三   |
|   3 | 张三   |
|   4 | 张三   |
| 100 | 张三   |
+-----+--------+
5 rows in set (0.00 sec)

mysql> insert into student values (null,'张三');
Query OK, 1 row affected (0.00 sec)

mysql> select * from student;
+-----+--------+
| id  | name   |
+-----+--------+
|   1 | 张三   |
|   2 | 张三   |
|   3 | 张三   |
|   4 | 张三   |
| 100 | 张三   |
| 101 | 张三   |
+-----+--------+
6 rows in set (0.00 sec)
  • MySQL 想要自增,必须要能够记录下来当前 id 已经到哪了,还要保证自增后,得是不能重复的。
  • MySQL里简单粗暴的做法,直接就是记录当前 自增主键 里的最大值,这就能保证自增速度很快,并且一定是不重复的
  • 可能会觉得有数据库浪费了,但是数据库一般而言是很大的,一般不考虑这个浪费

自增主键,主要就是用来生成一个唯一的 id ,来保证不重复,如果数据库是分布式部署,这个时候自增主键就要带来问题。

在一个MySQL集群里,有许多的MySQL节点(多个节点上的数据放在一起才是一个完整的数据集合),MySQL生成自增主键的时候可以保证在自己这个节点上生成的 id 是唯一的,但是无法保证这个 id 在其他节点上也是唯一的。

生成一个 ID 这样的需求,是客观存在的,但是 MySQL 自增主键,已经难以满足要求了

一个典型的解决方法:在生成 id 的时候,让这些节点相互协商一下,彼此了解了对方的情况之后,就能生成唯一的 id 了,但是代价有点大。为了轻量,高效,又出了另外一个方法:唯一 id = 时间戳(ms) + 机房编号/主机编号 + 随机因子 (这里的 + 不是算术运算 而是字符串拼接),进入数据库的数据有先有后形成时间戳,并且会分摊到不同的主机上,就算是一个主机,又会生成随机数的!(也有极端情况三种一模一样,理论上存在但是工程上是忽略不计的!)

⑤:FOREIGN KEY

  • 描述了两张表之间的关联关系

举例:

  • 班级表是负责约束的一方,称为父表
  • 学生表是被约束的一方,称为子表
mysql> drop table student;
Query OK, 0 rows affected (0.01 sec)

mysql> create table class (classId int primary key,className varchar(20));
Query OK, 0 rows affected (0.01 sec)

mysql> create table student (studentId int primary key, name varchar(20), classId int, foreign key (classId) references class (classId));
Query OK, 0 rows affected (0.01 sec)

mysql>
mysql> desc student;
+-----------+-------------+------+-----+---------+-------+
| Field     | Type        | Null | Key | Default | Extra |
+-----------+-------------+------+-----+---------+-------+
| studentId | int(11)     | NO   | PRI | NULL    |       |
| name      | varchar(20) | YES  |     | NULL    |       |
| classId   | int(11)     | YES  | MUL | NULL    |       |
+-----------+-------------+------+-----+---------+-------+
3 rows in set (0.00 sec)
  • 第一句为父表,第二句为子表
  • 先把所有的列都定义完,逗号,然后再写外键约束

在父表为空的情况下,直接尝试往子表插入,就会报错!!!

mysql> insert into student values(1,'张三',1);
ERROR 1452 (23000): Cannot add or update a child row: a foreign key constraint fails (`java`.`student`, CONSTRAINT `student_ibfk_1` FOREIGN KEY (`classId`) REFERENCES `class` (`classId`))

在父表里面添加一些数据:

mysql> insert into class values (1,'java101');
Query OK, 1 row affected (0.00 sec)

mysql>  insert into class values (2,'java102');
Query OK, 1 row affected (0.00 sec)

mysql>  insert into class values (3,'java103');
Query OK, 1 row affected (0.00 sec)

mysql> select * from class;
+---------+-----------+
| classId | className |
+---------+-----------+
|       1 | java101   |
|       2 | java102   |
|       3 | java103   |
+---------+-----------+
3 rows in set (0.00 sec)

往子表插入数据

mysql> insert into student values(1,'张三',4);
ERROR 1452 (23000): Cannot add or update a child row: a foreign key constraint fails (`java`.`student`, CONSTRAINT `student_ibfk_1` FOREIGN KEY (`classId`) REFERENCES `class` (`classId`))

依旧报错,插入子表的数据没有在父表的 classId 中,同样也是报的刚才的错误

在外键的约束下,每次你插入 / 修改操作,都会先触发在父表中的查询,父表中存在才能插入 / 修改成功,否则就会失败。

mysql> insert into student values(1,'张三',1);
Query OK, 1 row affected (0.00 sec)

每次插入都要先查询会拖慢执行效率,但是也没有特别拖慢
如果查询操作触发了遍历表,低效的;如果触发了索引,相对于快不少

建立外键约束的时候,MySQL 就要求,引用的父表的列,必须是主键或者UNIQUE(自带索引,查询速度就会快一些)

父表对子表产生了限制,但是反过来子表也会对父表产生限制

父表对子表的限制是不能随意插入 / 修改 子表对于父表的限制就是不能随意修改 / 删除

mysql> select * from student;
+-----------+--------+---------+
| studentId | name   | classId |
+-----------+--------+---------+
|         1 | 张三   |       1 |
+-----------+--------+---------+
1 row in set (0.00 sec)

mysql> delete from class where classId = 1;
ERROR 1451 (23000): Cannot delete or update a parent row: a foreign key constraint fails (`java`.`student`, CONSTRAINT `student_ibfk_1` FOREIGN KEY (`classId`) REFERENCES `class` (`classId`))

由于在子表中,引入了 classId = 1 的记录,尝试删除父表中的对应记录,发现,这个就难以删除

⑥: CHECK(了解)

  • 直接对表中的值做出限制
check (sex ='男' or sex='女')

例如限制性别这一列只能是 男 或 女

 

🌲二. 表的设计

  • 根据一些实际的业务场景,来设计表,主要就是确定有几个表,每个表干啥,每个表有多少个字段。

面试官:你的项目里的数据库是如何设计的?

  • 其实是让你回答,你的数据库里有几个表,每个表是干啥的,以及表里有哪些字段

那我们该如何设计它呢?

我们需要明确需求场景,提取出需求中的 " 实体 " ,实体可以认为是关键性名词,一般来说,每个实体都会分配一个表来进行表示,除了实体之外,还需要理清楚,实体和实体之间的关系。

1. 一对一
2. 一对多
3. 多对多
4. 没关系

例如一个学校的教务系统:
有学生,账号,课程,班级

  • 一个学生只能有一个账号,一个账号只能分配给一个学生(一对一)
  • 一个同学只属于一个班级,一个班级包含多个学生(一对多)
  • 一个学生可以选择多门课程,一个课程可以包含多个学生(多对多)

不同的关系,在设计表的时候有不同的套路,对应的设计表的套路

  • 一对一:

1.把学生和账号,直接放在一个表里!

student (id, name, account, password…)

这种设计方式是下册,尤其是一个系统中包含不同身份角色的时候

2.把学生和账号各自放到一个表里,使用一个额外的 id 来关联

student (studentId, name, accountId)
account (accountId, password)

student (studentId, name)
account (accountId, password,studentId)

  • 一对多:

方法1:

   student (id, name)
       1,  张三
       2,  李四
       3,  王五
class (classId, className, studentList)
     1   Java101    1,2
     2  Java102     3

方法2:

class (classId, className)
     1,   java101
     2,   java102

student (id, name, classId)
    1  张三 1
    2  李四 1
    3  王五 2

因为 MySQL 中没有 " 数组 " 这样的类型,第一种方案是无法实现的(可以用字符串拼接的方式凑合着来实现,但是实际上是不好设计的,比较低效,也失去了数据库对于数据校验的一些能力)

  • 多对多:

一般就是采用个中间表,俩表示多对多的关系

student (studentId, name)
      1   张三
      2   李四
      3   王五

course (courseId, courseName)
      1    语文
      2    数学
      3    英语

student_course (studentId, courseId)
          1   1    (张三选了语文课)
          1   2    (张三选了数学课)
          2   1    (李四选了语文课)

数据库设计要点:

  • 找出实体
  • 描述清楚实体之间的关系
  • 代入固定套路即可

如果需求场景比较简单,很容易理清其中的实体关系
如果需求场景比较复杂,可能涉及到很多实体,会很乱,因此程序猿发明了一个工具 – ER 图(实体 – 关系图)

关联之后,字段名相同是不会有影响的!

mysql> select * from class;
+---------+-----------+
| classId | className |
+---------+-----------+
|       1 | java101   |
|       2 | java102   |
|       3 | java103   |
+---------+-----------+
3 rows in set (0.00 sec)

mysql> select * from student;
+-----------+--------+---------+
| studentId | name   | classId |
+-----------+--------+---------+
|         1 | 张三   |       1 |
+-----------+--------+---------+
1 row in set (0.00 sec)

例如这俩张表里都有 classId 但是详细讲就是 class.classId 和 student.classId

 

🌳三. 新增

  • 把查询结果作为新增的数据

insert into 表名1 select 列名 from 表名2;

先执行查询操作,查询出来的结果插入到另外一个表里,保证查询结果的临时表的列要和插入的表的列匹配!!!

  • 要求从表名 2 中查询出来的结果的列数和类型 和表名 1 匹配,不要求列名匹配
mysql> create table student (id int, name varchar(20));
Query OK, 0 rows affected (0.01 sec)

mysql> insert into student values (1,'张三'),(2,'李四'),(3,'王五');
Query OK, 3 rows affected (0.00 sec)
Records: 3  Duplicates: 0  Warnings: 0

mysql> create table student2 (id int, name varchar(20));
Query OK, 0 rows affected (0.01 sec)

mysql> select * from student;
+------+--------+
| id   | name   |
+------+--------+
|    1 | 张三   |
|    2 | 李四   |
|    3 | 王五   |
+------+--------+
3 rows in set (0.00 sec)

mysql> select * from student2;
Empty set (0.00 sec)

mysql> insert into student2 select * from student;
Query OK, 3 rows affected (0.00 sec)
Records: 3  Duplicates: 0  Warnings: 0

mysql> select * from student2;
+------+--------+
| id   | name   |
+------+--------+
|    1 | 张三   |
|    2 | 李四   |
|    3 | 王五   |
+------+--------+
3 rows in set (0.00 sec)

 

🌴四. 查询

  • 聚合查询:

  • 通过行和行之间进行的运算处理

表达式查询是列和列之间

🏳️4.1 聚合函数(以下都是聚合查询)

函数说明
COUNT([DISTINCT] expr)返回查询到的数据的 数量
SUM([DISTINCT] expr)返回查询到的数据的 总和,不是数字没有意义
AVG([DISTINCT] expr)返回查询到的数据的 平均值,不是数字没有意义
MAX([DISTINCT] expr)返回查询到的数据的 最大值,不是数字没有意义
MIN([DISTINCT] expr)返回查询到的数据的 最小值,不是数字没有意义

这些都属于 SQL 内置函数,SQL 作为一个编程语言,也是内置了一些库函数供我们来使用的

  • COUNT函数的使用:
mysql> select * from exam_result;
+------+-----------+---------+------+---------+
| id   | name      | chinese | math | english |
+------+-----------+---------+------+---------+
|    1 | 唐三藏    |    67.0 | 98.0 |    56.0 |
|    2 | 孙悟空    |    87.5 | 78.0 |    77.0 |
|    3 | 猪悟能    |    88.0 | 98.5 |    90.0 |
|    4 | 曹孟德    |    82.0 | 84.0 |    67.0 |
|    5 | 刘玄德    |    55.5 | 85.0 |    45.0 |
|    6 | 孙权      |    70.0 | 73.0 |    78.5 |
|    7 | 宋公明    |    75.0 | 65.0 |    30.0 |
+------+-----------+---------+------+---------+
7 rows in set (0.00 sec)

mysql> select count(*) from exam_result;
+----------+
| count(*) |
+----------+
|        7 |
+----------+
1 row in set (0.00 sec)

mysql> select count(name) from exam_result;
+-------------+
| count(name) |
+-------------+
|           7 |
+-------------+
1 row in set (0.00 sec)

那么其中使用 * 和 name(列名) 有什么区别呢?

mysql> insert into exam_result values(null, null, null, null, null);
Query OK, 1 row affected (0.00 sec)

mysql> select * from exam_result;
+------+-----------+---------+------+---------+
| id   | name      | chinese | math | english |
+------+-----------+---------+------+---------+
|    1 | 唐三藏    |    67.0 | 98.0 |    56.0 |
|    2 | 孙悟空    |    87.5 | 78.0 |    77.0 |
|    3 | 猪悟能    |    88.0 | 98.5 |    90.0 |
|    4 | 曹孟德    |    82.0 | 84.0 |    67.0 |
|    5 | 刘玄德    |    55.5 | 85.0 |    45.0 |
|    6 | 孙权      |    70.0 | 73.0 |    78.5 |
|    7 | 宋公明    |    75.0 | 65.0 |    30.0 |
| NULL | NULL      |    NULL | NULL |    NULL |
+------+-----------+---------+------+---------+
8 rows in set (0.00 sec)

mysql> select count(*) from exam_result;
+----------+
| count(*) |
+----------+
|        8 |
+----------+
1 row in set (0.00 sec)

mysql> select count(name) from exam_result;
+-------------+
| count(name) |
+-------------+
|           7 |
+-------------+
1 row in set (0.00 sec)

可以看到我们再添加一个全为空的信息,* 把 NULL 值也记录到行数中了,name(列名) 对于 NULL 不会计数。

注:

  • count(name) 中间不能带有空格,否则就会出错,SQL 不能被正常解析
  • SUM函数的使用:
mysql> select * from exam_result;
+------+-----------+---------+------+---------+
| id   | name      | chinese | math | english |
+------+-----------+---------+------+---------+
|    1 | 唐三藏    |    67.0 | 98.0 |    56.0 |
|    2 | 孙悟空    |    87.5 | 78.0 |    77.0 |
|    3 | 猪悟能    |    88.0 | 98.5 |    90.0 |
|    4 | 曹孟德    |    82.0 | 84.0 |    67.0 |
|    5 | 刘玄德    |    55.5 | 85.0 |    45.0 |
|    6 | 孙权      |    70.0 | 73.0 |    78.5 |
|    7 | 宋公明    |    75.0 | 65.0 |    30.0 |
| NULL | NULL      |    NULL | NULL |    NULL |
+------+-----------+---------+------+---------+
8 rows in set (0.01 sec)

mysql> select sum(math) from exam_result;
+-----------+
| sum(math) |
+-----------+
|     581.5 |
+-----------+
1 row in set (0.00 sec)

在我们之前学过,NULL 和其他数字进行算数运算,结果还是 NULL,在这里可以得出结论SUM函数中 NULL 没有参加运算

  • 此处聚合查询也是完全可以指定筛选条件的!
mysql> select sum(english) from exam_result where english < 60;
+--------------+
| sum(english) |
+--------------+
|        131.0 |
+--------------+
1 row in set (0.00 sec)

mysql>  select sum(english) from exam_result;
+--------------+
| sum(english) |
+--------------+
|        443.5 |
+--------------+
1 row in set (0.00 sec)

对于带有条件的聚合查询,先会按照条件进行筛选,筛选后得到的结果进行聚合

  • 对名字进行求和
mysql> select sum(name) from exam_result;
+-----------+
| sum(name) |
+-----------+
|         0 |
+-----------+
1 row in set, 7 warnings (0.00 sec)

名字是字符串无法求和,所以 sum 函数只能对数字进行求和

  • AVG函数的使用
mysql> select avg(math) from exam_result;
+-----------+
| avg(math) |
+-----------+
|  83.07143 |
+-----------+
1 row in set (0.00 sec)

此处通过计算也可以得出平均值的计算是不会计入 NULL 值的个数的

  • MAXMIN 函数的使用:
mysql> select max(math) from exam_result;
+-----------+
| max(math) |
+-----------+
|      98.5 |
+-----------+
1 row in set (0.00 sec)

mysql>  select min(math) from exam_result;
+-----------+
| min(math) |
+-----------+
|      65.0 |
+-----------+
1 row in set (0.00 sec)

 

🏴4.2 分组查询

  • 把表中的若干行,分成好几组,指定某一列作为分组的依据,分组依据的列值相同,则被归为一组,分成多个组之后,还可以针对每个组,分别使用聚合函数。

group by 列

建一张表:

mysql> create table student (id int, name varchar(20), gender varchar(20), score int);
Query OK, 0 rows affected (0.01 sec)

mysql> insert into student values (1, '张三', '男', 95);
Query OK, 1 row affected (0.00 sec)

mysql> insert into student values (2, '李四', '女', 75);
Query OK, 1 row affected (0.00 sec)

mysql> insert into student values (3, '王五', '男', 85);
Query OK, 1 row affected (0.00 sec)

mysql> insert into student values (4, '赵六', '女', 65);
Query OK, 1 row affected (0.00 sec)

mysql> select * from student;
+------+--------+--------+-------+
| id   | name   | gender | score |
+------+--------+--------+-------+
|    1 | 张三   ||    95 |
|    2 | 李四   ||    75 |
|    3 | 王五   ||    85 |
|    4 | 赵六   ||    65 |
+------+--------+--------+-------+
4 rows in set (0.00 sec)

需要统计,男生和女生分别各自的最高分,平均分,最低分

mysql> select gender, max(score), min(score), avg(score) from student group by gender;
+--------+------------+------------+------------+
| gender | max(score) | min(score) | avg(score) |
+--------+------------+------------+------------+
||         75 |         65 |    70.0000 |
||         95 |         85 |    90.0000 |
+--------+------------+------------+------------+
2 rows in set (0.00 sec)

分组规则,是把记录的值相同的行作为一组

在进行聚合查询的时候,也能指定条件筛选

  • 1. 在聚合之前,进行筛选,针对筛选后的结果,再聚合。(where 子句)
  • 2. 在聚合之后,进行筛选。(having 子句)
  1. 聚合之前查询:查询每个性别平均分(但是出去赵六同学)
mysql> select gender, avg(score) from student where name != '赵六' group by gender;
+--------+------------+
| gender | avg(score) |
+--------+------------+
||    75.0000 |
||    90.0000 |
+--------+------------+
2 rows in set (0.00 sec)
  1. 聚合之后筛选:查询平均分大于 80 的性别情况,需要先把平均分算出来(算平均分,需要先聚合)
mysql> select gender, avg(score) from student group by gender having avg(score) > 80;
+--------+------------+
| gender | avg(score) |
+--------+------------+
||    90.0000 |
+--------+------------+
1 row in set (0.00 sec)
  1. 同时在前面和后面都筛选:查询平均分大于 80 的性别情况(除去赵六)
mysql> select gender, avg(score) from student where name != '赵六' group by gender having avg(score) > 80;
+--------+------------+
| gender | avg(score) |
+--------+------------+
||    90.0000 |
+--------+------------+
1 row in set (0.00 sec)

聚合查询的执行过程:先按照 where 条件进行筛选记录,把筛选出来的结果按照 group by 来分组,分组之后按照 having 再进行筛选,最后再按照制定列中调用的聚合函数来显示计算结果。

 
联合查询

  • 也叫多表查询,是把多个表的记录合并到一起,综合进行查询

select * from 表1, 表2;

联合查询中的核心概念:笛卡尔积。

笛卡尔积就是把这两个表中的所有记录,进行排列组合,穷举出所有的可能情况

排列组合之后的结果就是笛卡尔积。笛卡尔积的列数就是原来俩张表的列数之和,笛卡尔积的行数就是原来俩张表的行数之和。

针对多个表进行联合查询,本质上就是先针对多个表进行笛卡尔积运算。万一原来的表就很大,再进行多个表的笛卡尔积,就会得到一个更复杂的表,这个过程就会很低效,因此,在实际开发中进行多表查询,一定要克制。

笛卡尔积中包含了大量无效数据,指定了合理的过滤条件,把有效的数据挑出来,这个时候就得到了一个非常有用的数据表,这个过程就是 " 联合查询 " 的过程。

多表查询的一般步骤:

  • 1. 先根据需求理清楚说想要的数据都在哪些表中
  • 2. 【核心操作】先针对多个表进行笛卡尔积
  • 3. 根据连接条件,筛选出合法数据,过滤掉非法数据
  • 4. 进一步增加条件,根据需求做更精细的筛选
  • 5. 去掉不必要的列,保留最关注的信息

此处创建四个表:

mysql>  create table classes (id int primary key auto_increment, name varchar(20), `desc` varchar(100));
Query OK, 0 rows affected (0.01 sec)

mysql>  create table student (id int primary key auto_increment, sn varchar(20), name varchar(20), qq_mail varchar(20), classes_id int);
Query OK, 0 rows affected (0.01 sec)

mysql> create table course (id int primary key auto_increment, name varchar(20));
Query OK, 0 rows affected (0.01 sec)

mysql>  create table score (score decimal(3,1),student_id int, course_id int);
Query OK, 0 rows affected (0.01 sec)

填入数据:

mysql> insert into classes(name, `desc`) values
    -> ('计算机系2019级1班', '学习了计算机原理、C和Java语言、数据结构和算法'),
    -> ('中文系2019级3班','学习了中国传统文学'),
    -> ('自动化2019级5班','学习了机械自动化');
Query OK, 3 rows affected (0.00 sec)
Records: 3  Duplicates: 0  Warnings: 0

mysql> 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);
Query OK, 8 rows affected (0.00 sec)
Records: 8  Duplicates: 0  Warnings: 0

mysql> insert into course(name) values
    -> ('Java'),('中国传统文化'),('计算机原理'),('语文'),('高阶数学'),('英文');
Query OK, 6 rows affected (0.00 sec)
Records: 6  Duplicates: 0  Warnings: 0

mysql> 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);
Query OK, 20 rows affected (0.00 sec)
Records: 20  Duplicates: 0  Warnings: 0

打印四个表:

mysql> select * from student;
+----+-------+-----------------+------------------+------------+
| id | sn    | name            | qq_mail          | classes_id |
+----+-------+-----------------+------------------+------------+
|  1 | 09982 | 黑旋风李逵      | xuanfeng@qq.com  |          1 |
|  2 | 00835 | 菩提老祖        | NULL             |          1 |
|  3 | 00391 | 白素贞          | NULL             |          1 |
|  4 | 00031 | 许仙            | xuxian@qq.com    |          1 |
|  5 | 00054 | 不想毕业        | NULL             |          1 |
|  6 | 51234 | 好好说话        | say@qq.com       |          2 |
|  7 | 83223 | tellme          | NULL             |          2 |
|  8 | 09527 | 老外学中文      | foreigner@qq.com |          2 |
+----+-------+-----------------+------------------+------------+
8 rows in set (0.00 sec)

mysql> select * from classes;
+----+-------------------------+-------------------------------------------------------------------+
| id | name                    | desc                                                              |
+----+-------------------------+-------------------------------------------------------------------+
|  1 | 计算机系20191| 学习了计算机原理、C和Java语言、数据结构和算法                     |
|  2 | 中文系20193| 学习了中国传统文学                                                |
|  3 | 自动化20195| 学习了机械自动化                                                  |
+----+-------------------------+-------------------------------------------------------------------+
3 rows in set (0.00 sec)

mysql> select * from score;
+-------+------------+-----------+
| score | student_id | course_id |
+-------+------------+-----------+
|  70.5 |          1 |         1 |
|  98.5 |          1 |         3 |
|  33.0 |          1 |         5 |
|  98.0 |          1 |         6 |
|  60.0 |          2 |         1 |
|  59.5 |          2 |         5 |
|  33.0 |          3 |         1 |
|  68.0 |          3 |         3 |
|  99.0 |          3 |         5 |
|  67.0 |          4 |         1 |
|  23.0 |          4 |         3 |
|  56.0 |          4 |         5 |
|  72.0 |          4 |         6 |
|  81.0 |          5 |         1 |
|  37.0 |          5 |         5 |
|  56.0 |          6 |         2 |
|  43.0 |          6 |         4 |
|  79.0 |          6 |         6 |
|  80.0 |          7 |         2 |
|  92.0 |          7 |         6 |
+-------+------------+-----------+
20 rows in set (0.00 sec)

mysql> select * from course;
+----+--------------------+
| id | name               |
+----+--------------------+
|  1 | Java               |
|  2 | 中国传统文化       |
|  3 | 计算机原理         |
|  4 | 语文               |
|  5 | 高阶数学           |
|  6 | 英文               |
+----+--------------------+
6 rows in set (0.00 sec)

 

🏁4.3 内连接(以下都是联合查询)

  • select 列名 from 表1,表2…;
  • select 列名 from 表1 inner join 表2…;

①:目标:查询 “ 许仙 ” 同学的 成绩:

在这里插入图片描述

在这里提及一下为什么不用代码的形式,根据笛卡尔积行数是由两个表的行数之积得来的,成绩表和学生表行数相乘 160 行代码太长,因此截图省略位置。

其中 160 行代码大部分是无用数据,清理掉即可

mysql> select * from student, score where student.id = score.student_id;
+----+-------+-----------------+-----------------+------------+-------+------------+-----------+
| id | sn    | name            | qq_mail         | classes_id | score | student_id | course_id |
+----+-------+-----------------+-----------------+------------+-------+------------+-----------+
|  1 | 09982 | 黑旋风李逵      | xuanfeng@qq.com |          1 |  70.5 |          1 |         1 |
|  1 | 09982 | 黑旋风李逵      | xuanfeng@qq.com |          1 |  98.5 |          1 |         3 |
|  1 | 09982 | 黑旋风李逵      | xuanfeng@qq.com |          1 |  33.0 |          1 |         5 |
|  1 | 09982 | 黑旋风李逵      | xuanfeng@qq.com |          1 |  98.0 |          1 |         6 |
|  2 | 00835 | 菩提老祖        | NULL            |          1 |  60.0 |          2 |         1 |
|  2 | 00835 | 菩提老祖        | NULL            |          1 |  59.5 |          2 |         5 |
|  3 | 00391 | 白素贞          | NULL            |          1 |  33.0 |          3 |         1 |
|  3 | 00391 | 白素贞          | NULL            |          1 |  68.0 |          3 |         3 |
|  3 | 00391 | 白素贞          | NULL            |          1 |  99.0 |          3 |         5 |
|  4 | 00031 | 许仙            | xuxian@qq.com   |          1 |  67.0 |          4 |         1 |
|  4 | 00031 | 许仙            | xuxian@qq.com   |          1 |  23.0 |          4 |         3 |
|  4 | 00031 | 许仙            | xuxian@qq.com   |          1 |  56.0 |          4 |         5 |
|  4 | 00031 | 许仙            | xuxian@qq.com   |          1 |  72.0 |          4 |         6 |
|  5 | 00054 | 不想毕业        | NULL            |          1 |  81.0 |          5 |         1 |
|  5 | 00054 | 不想毕业        | NULL            |          1 |  37.0 |          5 |         5 |
|  6 | 51234 | 好好说话        | say@qq.com      |          2 |  56.0 |          6 |         2 |
|  6 | 51234 | 好好说话        | say@qq.com      |          2 |  43.0 |          6 |         4 |
|  6 | 51234 | 好好说话        | say@qq.com      |          2 |  79.0 |          6 |         6 |
|  7 | 83223 | tellme          | NULL            |          2 |  80.0 |          7 |         2 |
|  7 | 83223 | tellme          | NULL            |          2 |  92.0 |          7 |         6 |
+----+-------+-----------------+-----------------+------------+-------+------------+-----------+
20 rows in set (0.00 sec)

就只剩下同学的各科成绩,再对 “ 许仙 ” 进行筛选:

mysql> select * from student, score where student.id = score.student_id and student.name = '许仙';
+----+-------+--------+---------------+------------+-------+------------+-----------+
| id | sn    | name   | qq_mail       | classes_id | score | student_id | course_id |
+----+-------+--------+---------------+------------+-------+------------+-----------+
|  4 | 00031 | 许仙   | xuxian@qq.com |          1 |  67.0 |          4 |         1 |
|  4 | 00031 | 许仙   | xuxian@qq.com |          1 |  23.0 |          4 |         3 |
|  4 | 00031 | 许仙   | xuxian@qq.com |          1 |  56.0 |          4 |         5 |
|  4 | 00031 | 许仙   | xuxian@qq.com |          1 |  72.0 |          4 |         6 |
+----+-------+--------+---------------+------------+-------+------------+-----------+
4 rows in set (0.00 sec)

最后只需要过滤掉不必要的列:

mysql>  select name, course_id, score from student, score where student.id = score.student_id and student.name = '许仙';
+--------+-----------+-------+
| name   | course_id | score |
+--------+-----------+-------+
| 许仙   |         1 |  67.0 |
| 许仙   |         3 |  23.0 |
| 许仙   |         5 |  56.0 |
| 许仙   |         6 |  72.0 |
+--------+-----------+-------+
4 rows in set (0.00 sec)

一张清晰完整的结果就呈现在我们眼前。

通过 select 列名 from 表1, 表2 这种方式可以进行多表查询,除此之外,还有另外一种写法: select 列名 from 表1 join 表2 on 连接条件; (当使用 join 计算笛卡尔积的时候,后面的条件使用 on 来表示,而不是 where ,这个相当于 " 固定搭配 ")

mysql> select * from student join score on student.id = score.student_id and student.name = '许仙';
+----+-------+--------+---------------+------------+-------+------------+-----------+
| id | sn    | name   | qq_mail       | classes_id | score | student_id | course_id |
+----+-------+--------+---------------+------------+-------+------------+-----------+
|  4 | 00031 | 许仙   | xuxian@qq.com |          1 |  67.0 |          4 |         1 |
|  4 | 00031 | 许仙   | xuxian@qq.com |          1 |  23.0 |          4 |         3 |
|  4 | 00031 | 许仙   | xuxian@qq.com |          1 |  56.0 |          4 |         5 |
|  4 | 00031 | 许仙   | xuxian@qq.com |          1 |  72.0 |          4 |         6 |
+----+-------+--------+---------------+------------+-------+------------+-----------+
4 rows in set (0.00 sec)

mysql> select student.name, score.course_id, score.score from student join score on student.id = score.student_id and student.name = '许仙';
+--------+-----------+-------+
| name   | course_id | score |
+--------+-----------+-------+
| 许仙   |         1 |  67.0 |
| 许仙   |         3 |  23.0 |
| 许仙   |         5 |  56.0 |
| 许仙   |         6 |  72.0 |
+--------+-----------+-------+
4 rows in set (0.00 sec)

既然存在了 from 多个表,那为什么还要引入 join on 出来呢?join on 默认情况下的行为和 from 多个表是一致的,但是 join on 还能延伸出一些其他的用法,功能比 from 多个表更广泛了

多张表的情况下:

select student.name score.course_id, score.score from student join score join classes;

join 多个表之后,最后来个 on 同意筛选条件就可以了

②:查询所有同学的总成绩,及同学的个人信息:

在我们上文已经写出了 160 行代码过滤后的每个人总成绩,在这里针对行和行之间的计算,就需要使用聚合查询,不能说一提到总成绩就想到表达式查询,要根据实际的表结构,见招拆招。

mysql> select * from student, score where student.id = score.student_id group by id;
+----+-------+-----------------+-----------------+------------+-------+------------+-----------+
| id | sn    | name            | qq_mail         | classes_id | score | student_id | course_id |
+----+-------+-----------------+-----------------+------------+-------+------------+-----------+
|  1 | 09982 | 黑旋风李逵      | xuanfeng@qq.com |          1 |  70.5 |          1 |         1 |
|  2 | 00835 | 菩提老祖        | NULL            |          1 |  60.0 |          2 |         1 |
|  3 | 00391 | 白素贞          | NULL            |          1 |  33.0 |          3 |         1 |
|  4 | 00031 | 许仙            | xuxian@qq.com   |          1 |  67.0 |          4 |         1 |
|  5 | 00054 | 不想毕业        | NULL            |          1 |  81.0 |          5 |         1 |
|  6 | 51234 | 好好说话        | say@qq.com      |          2 |  56.0 |          6 |         2 |
|  7 | 83223 | tellme          | NULL            |          2 |  80.0 |          7 |         2 |
+----+-------+-----------------+-----------------+------------+-------+------------+-----------+
7 rows in set (0.00 sec)

在不使用聚合函数的情况下,后面这部分显示出来的结果,相当于每个分组中的第一条记录

总成绩:

mysql> select student.name, sum(score.score) from student, score where student.id = score.student_id group by id;
+-----------------+------------------+
| name            | sum(score.score) |
+-----------------+------------------+
| 黑旋风李逵      |            300.0 |
| 菩提老祖        |            119.5 |
| 白素贞          |            200.0 |
| 许仙            |            218.0 |
| 不想毕业        |            118.0 |
| 好好说话        |            178.0 |
| tellme          |            172.0 |
+-----------------+------------------+
7 rows in set (0.00 sec)

还可以指定别名

mysql> select student.name, sum(score.score) as total from student, score where student.id = score.student_id group by id;
+-----------------+-------+
| name            | total |
+-----------------+-------+
| 黑旋风李逵      | 300.0 |
| 菩提老祖        | 119.5 |
| 白素贞          | 200.0 |
| 许仙            | 218.0 |
| 不想毕业        | 118.0 |
| 好好说话        | 178.0 |
| tellme          | 172.0 |
+-----------------+-------+
7 rows in set (0.00 sec)

③:查询每个同学姓名,课程名,分数

mysql> select * from student, course, score where student.id = score.student_id and course.id = score.course_id;
+----+-------+-----------------+-----------------+------------+----+--------------------+-------+------------+-----------+
| id | sn    | name            | qq_mail         | classes_id | id | name               | score | student_id | course_id |
+----+-------+-----------------+-----------------+------------+----+--------------------+-------+------------+-----------+
|  1 | 09982 | 黑旋风李逵      | xuanfeng@qq.com |          1 |  1 | Java               |  70.5 |          1 |         1 |
|  1 | 09982 | 黑旋风李逵      | xuanfeng@qq.com |          1 |  3 | 计算机原理         |  98.5 |          1 |         3 |
|  1 | 09982 | 黑旋风李逵      | xuanfeng@qq.com |          1 |  5 | 高阶数学           |  33.0 |          1 |         5 |
|  1 | 09982 | 黑旋风李逵      | xuanfeng@qq.com |          1 |  6 | 英文               |  98.0 |          1 |         6 |
|  2 | 00835 | 菩提老祖        | NULL            |          1 |  1 | Java               |  60.0 |          2 |         1 |
|  2 | 00835 | 菩提老祖        | NULL            |          1 |  5 | 高阶数学           |  59.5 |          2 |         5 |
|  3 | 00391 | 白素贞          | NULL            |          1 |  1 | Java               |  33.0 |          3 |         1 |
|  3 | 00391 | 白素贞          | NULL            |          1 |  3 | 计算机原理         |  68.0 |          3 |         3 |
|  3 | 00391 | 白素贞          | NULL            |          1 |  5 | 高阶数学           |  99.0 |          3 |         5 |
|  4 | 00031 | 许仙            | xuxian@qq.com   |          1 |  1 | Java               |  67.0 |          4 |         1 |
|  4 | 00031 | 许仙            | xuxian@qq.com   |          1 |  3 | 计算机原理         |  23.0 |          4 |         3 |
|  4 | 00031 | 许仙            | xuxian@qq.com   |          1 |  5 | 高阶数学           |  56.0 |          4 |         5 |
|  4 | 00031 | 许仙            | xuxian@qq.com   |          1 |  6 | 英文               |  72.0 |          4 |         6 |
|  5 | 00054 | 不想毕业        | NULL            |          1 |  1 | Java               |  81.0 |          5 |         1 |
|  5 | 00054 | 不想毕业        | NULL            |          1 |  5 | 高阶数学           |  37.0 |          5 |         5 |
|  6 | 51234 | 好好说话        | say@qq.com      |          2 |  2 | 中国传统文化       |  56.0 |          6 |         2 |
|  6 | 51234 | 好好说话        | say@qq.com      |          2 |  4 | 语文               |  43.0 |          6 |         4 |
|  6 | 51234 | 好好说话        | say@qq.com      |          2 |  6 | 英文               |  79.0 |          6 |         6 |
|  7 | 83223 | tellme          | NULL            |          2 |  2 | 中国传统文化       |  80.0 |          7 |         2 |
|  7 | 83223 | tellme          | NULL            |          2 |  6 | 英文               |  92.0 |          7 |         6 |
+----+-------+-----------------+-----------------+------------+----+--------------------+-------+------------+-----------+
20 rows in set (0.00 sec)
mysql> select student.name, course.name, score.score from student, course, score where student.id = score.student_id and course.id = score.course_id;
+-----------------+--------------------+-------+
| name            | name               | score |
+-----------------+--------------------+-------+
| 黑旋风李逵      | Java               |  70.5 |
| 黑旋风李逵      | 计算机原理          |  98.5 |
| 黑旋风李逵      | 高阶数学            |  33.0 |
| 黑旋风李逵      | 英文                |  98.0 |
| 菩提老祖        | Java               |  60.0 |
| 菩提老祖        | 高阶数学           |  59.5 |
| 白素贞          | Java               |  33.0 |
| 白素贞          | 计算机原理         |  68.0 |
| 白素贞          | 高阶数学           |  99.0 |
| 许仙            | Java               |  67.0 |
| 许仙            | 计算机原理         |  23.0 |
| 许仙            | 高阶数学           |  56.0 |
| 许仙            | 英文               |  72.0 |
| 不想毕业        | Java               |  81.0 |
| 不想毕业        | 高阶数学           |  37.0 |
| 好好说话        | 中国传统文化       |  56.0 |
| 好好说话        | 语文               |  43.0 |
| 好好说话        | 英文               |  79.0 |
| tellme         | 中国传统文化       |  80.0 |
| tellme         | 英文               |  92.0 |
+-----------------+--------------------+-------+
20 rows in set (0.00 sec)

 

🚩4.4 外连接

  • select 列名 from 表1 left join 表2;    左外连接
  • select 列名 from 表1 right join 表2;  右外连接

如果两张表里的数据是对应的,此时外连接和内连接看起来没区别,如果数据不对应,外连接和内连接的区别就明显了

制作一个表格:

mysql> create table student2(id int, name varchar(20));
Query OK, 0 rows affected (0.01 sec)

mysql> create table score2(id int, score int);
Query OK, 0 rows affected (0.01 sec)

mysql> insert into student2 values(1, '张三'), (2, '李四'), (3, '王五');
Query OK, 3 rows affected (0.00 sec)
Records: 3  Duplicates: 0  Warnings: 0

mysql>  insert into score2 values(1, 90), (2, 80), (3, 70);
Query OK, 3 rows affected (0.00 sec)
Records: 3  Duplicates: 0  Warnings: 0

mysql> select * from student2;
+------+--------+
| id   | name   |
+------+--------+
|    1 | 张三   |
|    2 | 李四   |
|    3 | 王五   |
+------+--------+
3 rows in set (0.00 sec)

mysql> select * from score2;
+------+-------+
| id   | score |
+------+-------+
|    1 |    90 |
|    2 |    80 |
|    3 |    70 |
+------+-------+
3 rows in set (0.00 sec)

mysql> select name, score from student2 join score2 on student2.id = score2.id;
+--------+-------+
| name   | score |
+--------+-------+
| 张三   |    90 |
| 李四   |    80 |
| 王五   |    70 |
+--------+-------+
3 rows in set (0.00 sec)

左右对比:(如果两个表数据记录都是一一对应的,此时内连接和外连接没有区别)

mysql> select name, score from student2 left join score2 on student2.id = score2.id;
+--------+-------+
| name   | score |
+--------+-------+
| 张三   |    90 |
| 李四   |    80 |
| 王五   |    70 |
+--------+-------+
3 rows in set (0.00 sec)

mysql> select name, score from student2 right join score2 on student2.id = score2.id;
+--------+-------+
| name   | score |
+--------+-------+
| 张三   |    90 |
| 李四   |    80 |
| 王五   |    70 |
+--------+-------+
3 rows in set (0.00 sec)

对成绩序号进行修改,出现 id 4 无人对应,王五没有对应的成绩

mysql> update score2 set id = 4 where score = 70;
Query OK, 1 row affected (0.00 sec)
Rows matched: 1  Changed: 1  Warnings: 0

mysql> select * from score2;
+------+-------+
| id   | score |
+------+-------+
|    1 |    90 |
|    2 |    80 |
|    4 |    70 |
+------+-------+
3 rows in set (0.00 sec)

mysql> select * from student2;
+------+--------+
| id   | name   |
+------+--------+
|    1 | 张三   |
|    2 | 李四   |
|    3 | 王五   |
+------+--------+
3 rows in set (0.00 sec)

从学生表的角度看,王五同学没有分数
从分数表的角度看,4 号同学没有身份信息

  • 内连接产生的结果,是两张表都包含的数据
mysql> select name, score from student2 join score2 on student2.id = score2.id;
+--------+-------+
| name   | score |
+--------+-------+
| 张三   |    90 |
| 李四   |    80 |
+--------+-------+
2 rows in set (0.00 sec)

在这里插入图片描述

  • 左外连接,就是以 join 左侧的表为主,保证左侧的表的每个记录都能体现在结果中,如果左侧的记录在右侧表中不存在,则填充 NULL。
mysql> select name, score from student2 left join score2 on student2.id = score2.id;
+--------+-------+
| name   | score |
+--------+-------+
| 张三   |    90 |
| 李四   |    80 |
| 王五   |  NULL |
+--------+-------+
3 rows in set (0.00 sec)

在这里插入图片描述

  • 右外连接,就是以 join 右侧的表为主,保证右侧的表每个记录都能体现在结果中,如果右侧的记录在左侧不存在,则填充 NULL。
mysql> select name, score from student2 right join score2 on student2.id = score2.id;
+--------+-------+
| name   | score |
+--------+-------+
| 张三   |    90 |
| 李四   |    80 |
| NULL   |    70 |
+--------+-------+
3 rows in set (0.00 sec)

在这里插入图片描述

综上来看,是否存在一种外连接,可以把整个全集都获取到呢?

答案是有的,全外连接,但是 MySQL 不支持全外连接

 

🏳️‍🌈4.5 自连接

  • 自己和自己进行连接,同一张表和自己进行笛卡尔积

SQL 里面指定条件筛选,都是按照列和列之间进行筛选,难以进行行和行之间筛选,自连接操作能把行转化为列

  • 显示所有 " 计算机原理 " 成绩比 " Java " 成绩高的成绩信息
mysql> select * from course;
+----+--------------------+
| id | name               |
+----+--------------------+
|  1 | Java               |
|  2 | 中国传统文化       |
|  3 | 计算机原理         |
|  4 | 语文               |
|  5 | 高阶数学           |
|  6 | 英文               |
+----+--------------------+
6 rows in set (0.00 sec)

course_id 1 和 course_id 3的比较

mysql> select * from score;
+-------+------------+-----------+
| score | student_id | course_id |
+-------+------------+-----------+
|  70.5 |          1 |         1 |
|  98.5 |          1 |         3 |
|  33.0 |          1 |         5 |
|  98.0 |          1 |         6 |
|  60.0 |          2 |         1 |
|  59.5 |          2 |         5 |
|  33.0 |          3 |         1 |
|  68.0 |          3 |         3 |
|  99.0 |          3 |         5 |
|  67.0 |          4 |         1 |
|  23.0 |          4 |         3 |
|  56.0 |          4 |         5 |
|  72.0 |          4 |         6 |
|  81.0 |          5 |         1 |
|  37.0 |          5 |         5 |
|  56.0 |          6 |         2 |
|  43.0 |          6 |         4 |
|  79.0 |          6 |         6 |
|  80.0 |          7 |         2 |
|  92.0 |          7 |         6 |
+-------+------------+-----------+
20 rows in set (0.00 sec)

SQL 中无法实现行和行之间的比较!行和行不能直接比较,转换成列和列,自连接就可以做到这一点

mysql> select * from score,score;
ERROR 1066 (42000): Not unique table/alias: 'score'

可以看到出现了错误,因为自连接的情况下,必须给表起别名了
在这里插入图片描述

但是此处笛卡尔积数据太多了,需要过滤掉不合理的数据,保留关注的合法数据

我们的需求是找到哪个同学的 3 号课程比 1 号课程高

在这里插入图片描述

当学生 id 对齐之后,发现课程 id 仍在排列组合

mysql> 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;
+-------+------------+-----------+-------+------------+-----------+
| score | student_id | course_id | score | student_id | course_id |
+-------+------------+-----------+-------+------------+-----------+
|  98.5 |          1 |         3 |  70.5 |          1 |         1 |
|  68.0 |          3 |         3 |  33.0 |          3 |         1 |
|  23.0 |          4 |         3 |  67.0 |          4 |         1 |
+-------+------------+-----------+-------+------------+-----------+
3 rows in set (0.00 sec)

此时表里剩下的数据就是左侧都为 3 的课程分数,右侧就都是为 1 的课程分数

mysql> 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;
+-------+------------+-----------+-------+------------+-----------+
| score | student_id | course_id | score | student_id | course_id |
+-------+------------+-----------+-------+------------+-----------+
|  98.5 |          1 |         3 |  70.5 |          1 |         1 |
|  68.0 |          3 |         3 |  33.0 |          3 |         1 |
+-------+------------+-----------+-------+------------+-----------+
2 rows in set (0.00 sec)

加个课程 3 大于课程 1 的条件

mysql> select s1.student_id 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;
+------------+
| student_id |
+------------+
|          1 |
|          3 |
+------------+
2 rows in set (0.00 sec)

更加精准把 id 列单独列出来,结果就是学生 1 和 学生 3 的成绩数据符合

 

🏴‍☠️4.6 子查询

  • 子查询是指嵌入在其他sql语句中的select语句,也叫嵌套查询

单行子查询:返回一行记录的子查询

查询与“不想毕业” 同学的同班同学:

  1. 先根据名字获取到班级 id
  2. 根据班级 id 查询 id 匹配同学
mysql> select * from student;
+----+-------+-----------------+------------------+------------+
| id | sn    | name            | qq_mail          | classes_id |
+----+-------+-----------------+------------------+------------+
|  1 | 09982 | 黑旋风李逵      | xuanfeng@qq.com  |          1 |
|  2 | 00835 | 菩提老祖        | NULL             |          1 |
|  3 | 00391 | 白素贞          | NULL             |          1 |
|  4 | 00031 | 许仙            | xuxian@qq.com    |          1 |
|  5 | 00054 | 不想毕业        | NULL             |          1 |
|  6 | 51234 | 好好说话        | say@qq.com       |          2 |
|  7 | 83223 | tellme          | NULL             |          2 |
|  8 | 09527 | 老外学中文      | foreigner@qq.com |          2 |
+----+-------+-----------------+------------------+------------+
8 rows in set (0.00 sec)

mysql> select classes_id from student where name = '不想毕业';
+------------+
| classes_id |
+------------+
|          1 |
+------------+
1 row in set (0.00 sec)

mysql> select name from student where classes_id = 1;
+-----------------+
| name            |
+-----------------+
| 黑旋风李逵      |
| 菩提老祖        |
| 白素贞          |
| 许仙            |
| 不想毕业        |
+-----------------+
5 rows in set (0.00 sec)

把这俩 SQL 一合并就成了一个子查询了

mysql> select name from student where classes_id = (select classes_id from student where name = '不想毕业');
+-----------------+
| name            |
+-----------------+
| 黑旋风李逵      |
| 菩提老祖        |
| 白素贞          |
| 许仙            |
| 不想毕业        |
+-----------------+
5 rows in set (0.00 sec)

此处的子查询,可以任意级别的嵌套,N 个 SQL 组合成了一个巨无霸 SQL

多行子查询:返回多行记录的子查询

查询“语文”或“英文”课程的成绩信息:

  1. 先根据课程名,直到课程的 id
  2. 拿着课程的 id 去分数表里进行查询
mysql> select * from course;
+----+--------------------+
| id | name               |
+----+--------------------+
|  1 | Java               |
|  2 | 中国传统文化       |
|  3 | 计算机原理         |
|  4 | 语文               |
|  5 | 高阶数学           |
|  6 | 英文               |
+----+--------------------+
6 rows in set (0.00 sec)

mysql> select id from course where name = '语文' or name =  '英文';
+----+
| id |
+----+
|  4 |
|  6 |
+----+
2 rows in set (0.00 sec)

mysql> select * from score where course_id in (select id from course where name = '语文' or name = '英文');
+-------+------------+-----------+
| score | student_id | course_id |
+-------+------------+-----------+
|  98.0 |          1 |         6 |
|  72.0 |          4 |         6 |
|  43.0 |          6 |         4 |
|  79.0 |          6 |         6 |
|  92.0 |          7 |         6 |
+-------+------------+-----------+
5 rows in set (0.00 sec)

 

🏳️‍⚧️4.7 合并查询

  • 把两个查询结果,结果集合,合并到一起

可以使用集合操作符 union,union all。使用 UNION 和 UNION ALL 时,前后查询的结果集中,字段需要一致。

  • union

该操作符用于取得两个结果集的并集。当使用该操作符时,会自动去掉结果集中的重复行

查询id小于3,或者名字为“英文”的课程:

mysql> select * from course where id < 3 or name = '英文';
+----+--------------------+
| id | name               |
+----+--------------------+
|  1 | Java               |
|  2 | 中国传统文化       |
|  6 | 英文               |
+----+--------------------+
3 rows in set (0.00 sec)

mysql> select * from course where id < 3 union select * from course where name = '英文';
+----+--------------------+
| id | name               |
+----+--------------------+
|  1 | Java               |
|  2 | 中国传统文化       |
|  6 | 英文               |
+----+--------------------+
3 rows in set (0.00 sec)
  • union all

和 union 的唯一区别就是 union 操作如果有重复记录,会去重,union all 不会去重

SQL查询中各个关键字的执行先后顺序: from > on> join > where > group by > with > having >select > distinct > order by > limit

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/358655.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

黑马点评项目总结(未完待续)

黑马点评项目总结0. 整体架构1. 短信登录模块1.1 基于session&#xff08;1&#xff09;后台发送验证码Code&#xff08;2&#xff09;登录、注册&#xff08;3&#xff09;校验登录状态1.2 基于Redis&#xff08;1&#xff09;后台发送验证码Code&#xff08;2&#xff09;登录…

ur3+robotiq ft sensor+robotiq 2f 140配置rviz仿真环境

ur3robotiq ft sensorrobotiq 2f 140配置rviz仿真环境 搭建环境&#xff1a; ubuntu: 20.04 ros: Nonetic sensor: robotiq_ft300 gripper: robotiq_2f_140_gripper UR: UR3 在安装sensor和gripper之前&#xff0c;先简单配置一下UR机械臂的仿真环境&#xff0c;可参考这篇博…

零入门kubernetes网络实战-16->使用golang给docker环境下某个容器里添加一个额外的网卡

《零入门kubernetes网络实战》视频专栏地址 https://www.ixigua.com/7193641905282875942 本篇文章视频地址(稍后上传) 上一篇文章&#xff0c;我们使用了golang在veth pair链接的网络命名空间里添加了网卡&#xff0c; 本篇文章&#xff0c;我尝试&#xff0c;在docker环境下…

新建idea项目

目录IDEA系列之创建各种项目 https://blog.csdn.net/LOVEQD123/article/details/105886077 idea 创建项目的三种方式 https://blog.csdn.net/weixin_50034122/article/details/118754521 创建空项目 https://blog.csdn.net/qq_44537956/article/details/123075134 创建 spri…

百度百科词条怎么做?百度百科词条创建攻略分享

只要是想要将自己宣传出去的企业或是个人&#xff0c;都建议创建属于自己的百度百科词条&#xff0c;因为百度百科词条流量大、权重高、排名靠前&#xff0c;创建百度百科词条可以提高企业或是个人的知名度和口碑。 百度百科词条怎么做&#xff1f;每天都有用户在百度上搜索这…

携手亚马逊云科技,大地量子高精度功率预测系统助力清洁能源消纳提速增效

近年来&#xff0c;我国光伏和风电并网装机容量持续增长&#xff0c;截至2021年底&#xff0c;全国可再生能源装机规模突破10亿千瓦&#xff0c;占总发电装机容量的44.8%。其中&#xff0c;风电装机3.28亿千瓦、光伏发电装机3.06亿千瓦。风光电总装机和新增装机规模多年来位居全…

图解 script 标签中的 async 和 defer 属性

图解 script 标签中的 async 和 defer 属性 我们在工作中经常会碰到 script 标签,一般会有以下三种形式 <script srcxxx></script> <script srcxxx async></script> <script srcxxx defer></script>那么这三种形式的 script 标签有什么区…

java 多线程

1.什么是进程&#xff1f;什么是线程&#xff1f; 进程是:一个应用程序&#xff08;1个进程是一个软件&#xff09;。 线程是&#xff1a;一个进程中的执行场景/执行单元。 注意&#xff1a;一个进程可以启动多个线程。 我们在启动java程序的时候&#xff0c;会先启动JVM&am…

【2】MYSQL数据的导入与导出

文章目录 MYSQL-库(相同库名称)的导入导出MYSQL-库(不同库名称)的导入导出MYSQL-表的导入导出MYSQL-表的指定查询记录导入导出前提: 客户端工具是:SQLyog MYSQL-库(相同库名称)的导入导出 1、选中指定库——右键,选择【将数据库复制到不同的主机/数据库】 2、选中指…

分布式之Raft共识算法分析

写在前面 在分布式之Paxos共识算法分析 一文中我们分析了paxos算法&#xff0c;知道了其包括basic paxos和multi paxos&#xff0c;并了解了multi paxos只是一种分布式共识算法的思想&#xff0c;而非具体算法&#xff0c;但可根据其设计具体的算法&#xff0c;本文就一起来看…

SORT与DeepSORT简介

一、MOT( mutil-object tracking)步骤 在《DEEP LEARNING IN VIDEO MUTIL-OBJECT TEACKING: A SURVEY》这篇基于深度学习多目标跟踪综述中&#xff0c;描绘了MOT问题的四个主要步骤 1.跟定视频原始帧 2.使用目标检测器如Faster-rcnn, YOLO, SSD等进行检测&#xff0c;获取目标…

vue 3.0 Vue Router导航守卫的使用

目录前言&#xff1a;安装路由快速使用1. 创建路由模块2.规定路由模式3.使用路由规则4.声明路由链接和占位符5.重定向路由6.嵌套路由7.路径参数8. 声明式和编程式导航8.1 导航到不同的位置8.2 替换当前位置8.3 路由历史9.导航守卫9.1 全局前置守卫9.2全局路由守卫的语法参数9.3…

初阶C语言——操作符【详解】

文章目录1.算术操作符2.移位操作符2.1 左移操作符2.2 右移操作符3.位操作符按位与按位或按位异或4.赋值操作符复合赋值符5.单目操作符5.1单目操作符介绍6.关系操作符7.逻辑操作符8.条件操作符9.逗号表达式10.下标引用、函数调用和结构成员11表达式求值11.1 隐式类型转换11.2算术…

关于Java的深拷贝和浅拷贝

文章目录1.拷贝的引入1.1引用拷贝1.2对象拷贝2.深拷贝与浅拷贝2.1浅拷贝2.2深拷贝1.拷贝的引入 1.1引用拷贝 创建一个指向对象的引用变量的拷贝 Teacher teacher new Teacher("Taylor",26); Teacher otherteacher teacher; System.out.println(teacher); System…

vim常用命令

vim常用三种模式 命令模式&#xff08;Command mode&#xff09; 插入模式&#xff08;Insert mode&#xff09; 末行模式&#xff08;Last line mode&#xff09; &#xff08;一&#xff09;进入命令模式 vi 或者 vim&#xff08;二&#xff09;命令模式 -> 插入模式 &…

服务器部署流程与经验记录

服务器部署流程1.项目部署1.1 重置实例密码1.2 配置安全组规则1.3 远程连接服务器1.4 安装所需软件1.5 安装Tomcat1.6 配置宝塔安全组1.7 导入数据库和项目2. 域名注册3. 网站备案1.项目部署 1.1 重置实例密码 1.2 配置安全组规则 1.3 远程连接服务器 使用VNC远程连接&#…

实用调试技巧——“C”

各位CSDN的uu们你们好呀&#xff0c;今天小雅兰的内容是实用调试技巧&#xff0c;其实小雅兰一开始&#xff0c;也不知道调试到底是什么&#xff0c;一遇到问题&#xff0c;首先就是观察程序&#xff0c;改改这里改改那里&#xff0c;最后导致bug越修越多&#xff0c;或者是问别…

51单片机——定时器中断实验,小白讲解,相互学习

定时器介绍 1&#xff0c;CPU时序的有关知识 震荡周期&#xff1a;为单片机提供定时信号的震荡源的周期&#xff08;晶振周期或外加震荡周期&#xff09;。状态周期&#xff1a;2个震荡周期为1个状态周期&#xff0c;用S表示。震荡周期又称S周期或时钟周期。机器周期&#xff…

Java-多线程并发-线程的实现、调度和状态转换

线程的实现 线程是比进程更轻量级的调度执行单位&#xff0c;线程的引入&#xff0c;可以把一个进程的资源分配和执行调度分开&#xff0c;各个线程既可以共享进程资源( 内存地址、文件I/O等 )&#xff0c;又可以独立调度( 线程是CPU调度的基本单位 )。 Java语言则提供了在不…

300行代码手写spring初体验v1.0版本

70%猜想30%验证 spring&#xff1a;IOC 、DI、AOP、MVC MVC作为入口 web.xml 内部依赖一个DispathcheServlet这样一个接口 先来说一下springMVC的一些基础知识 整体的一个思路&#xff1a; 在web.xml里面进行了一个核心servlet的一个配置 核心就是这个DispatcherServlet …