SQL 语句
@[toc]
---
## join 基本语法
MySQL 中常见的有三种用法:
```sql
select * from table1 inner join table2 on condition
select * from table1 left join table2 on condition
select * from table1 right join table2 on condition
```
- inner join:内连接(等值连接)
- left join:左连接
- right join:右连接
下面用例子来解释这三种用法,假设有两张表,用户表 user 和部门表 depart:

### inner join
```sql
select user.name, user.age, depart.department
from user inner join depart
on user.name = depart.name;
```
inner join 获取同时符合两张表的数据并组合起来。
在这个例子中,就是在 user 表和 depart 表中找到 name 相同的行记录,并组合起来

来看实际的执行结果:

需要注意的是,如果不指定 on 条件进行过滤的话,取得的结果就是两张表的笛卡尔积
> 什么是笛卡尔积 ?
>
> 举个例子,两个集合 A{1, 2, 3} 和 B{a, b, c},则两个集合的笛卡尔积为 {1a, 1b, 1c, 2a, 2b, 2c, 3a, 3b, 3c}
```sql
select user.name, user.age, depart.department
from user inner join depart;
```

光看这个例子大伙儿可能觉得笛卡尔积没啥用,其实还是很常见的,举个例子:如果 A 表示某学校学生的集合,B 表示该学校所有课程的集合,则 A 与 B 的笛卡尔积就表示这个学校所有可能的选课情况(梦回大一被数据库支配的恐惧 🤯)。
另外,多提一嘴,可能有小伙伴还见过 cross join,在 MySQL 中 cross join 与 inner join 的作用是一样的。并且,可以直接省略掉 cross 或者 inner 关键字:

### left join
```sql
select user.name, user.age, depart.department
from user left join depart
on user.name = depart.name;
```
left join 取得左表的所有记录,即使右边这个表没有对应的匹配记录(如果没有相匹配的话,用 null 代替)。
在这个例子中,就是获取 user 表的所有记录,然后根据 on 条件去 depart 表中查询,如果有相同的 name,就组合起来,如果 depart 表中找不到和 user 表具有相同 name 的记录,就用 NULL 代替。

来看实际的执行结果:

### right join
```sql
select user.name, user.age, depart.department
from user right join depart
on user.name = depart.name;
```
和 left join 相反,right join 取得右表的所有记录,即使左边这个表没有对应的匹配记录(如果没有相匹配的话,用 null 代替)。
在这个例子中,就是获取 depart 表的所有记录,然后根据 on 条件去 user 表中查询,如果有相同的 name,就组合起来,如果 user 表中找不到和 depart 表具有相同 name 的记录,就用 NULL 代替。

来看实际的执行结果:

### full join
回顾下上面三张图,小伙伴们不知道有没有感觉缺了点什么,为啥没有把两个圆都填满的?

把两个圆都填满的学名叫作 **full join**,But,MySQL 中并没有 full join 的语法,需要借助 `union` 关键字来实现:
```sql
select user.name, user.age, depart.department
from user left join depart
on user.name = depart.name
union
select user.name, user.age, depart.department
from user right join depart
on user.name = depart.name;
```

## char 和 varchar 的区别,哪个索引效率更高?
**长度不同**:
- char 使用指定长度的**固定长度**表示字符串的一种字段类型;比如char(8),则数据库会使用固定的 1 个字节(八位)来存储数据,不足 8 位的字符串**在其后补空字符**,**读取的时候会默认截断掉末尾的空字符**。char 一般用来保存固定长度的表单提交数据存储,例如:身份证号,手机号,电话,密码等
- varchar(M) 是一种比 char 更加灵活的数据类型,同样用于表示字符数据,但是 varchar 可以保存**可变长度**的字符串。其中 M 代表该数据类型所允许保存的字符串的最大长度,只要长度小于该最大值的字符串都可以被保存在该数据类型中。因此,对于那些难以估计确切长度的数据对象来说,使用 varchar 数据类型更加明智。
MySQL4.1 以前,varchar数据类型所支持的最大长度 255,5.0 以上版本支持 65535 字节长度,utf8 编码下最多支持 21843个字符(不为空)
varchar 除了会保存需要的字符数,还**另加一个字节来记录长度** (如果列声明的长度超过 255,则使用两个字节记录长度)
**数据页分裂问题**:
- VARCHAR 因为行是变长的,在 UPDATE 时可能使行变得比原来更长,这可能会导致该行原本所处的页面已经没有更多的空间存储了,这种情况下 InnoDB 则需要进行页分裂使得该行可以放进页内
- 而 char 就不会出现这种页分裂、页空隙(碎片)问题
> 数据页分裂的更详细解释可以看这篇文章,写得挺好的:https://zhuanlan.zhihu.com/p/98818611
