MySQL基础
两张表怎么进⾏连接?
可以通过内连接 inner join、外连接 outer join、交叉连接 cross join 来合并多个表的查询结果
内连接可以用来找出两个表中共同的记录,相当于两个数据集的交集
定义数据库 A 为
| id | name |
|---|---|
| 1 | 张三 |
| 2 | 李四 |
| 3 | 王五 |
定义数据库 B 为:
| id | score |
|---|---|
| 1 | 90 |
| 2 | 80 |
SELECT *
FROM A
INNER JOIN B ON A.id = B.id;只有两表中都存在 user_id 的记录才会出现在查询结果中
| id | name | score |
|---|---|---|
| 1 | 张三 | 90 |
| 2 | 李四 | 80 |
左连接和右连接可以用来找出两个表中不同的记录,相当于两个数据集的并集。两者的区别是,左连接会保留左表中符合条件的所有记录,右连接则刚好相反
SELECT *
FROM A
LEFT JOIN B ON A.id = B.id;| id | name | score |
|---|---|---|
| 1 | 张三 | 90 |
| 2 | 李四 | 80 |
| 3 | 王五 | NULL |
说一下数据库的三大范式?
第一范式,确保表中的每个字段都是不可再分的原子值
| 学生 ID | 姓名 | 电话 |
|---|---|---|
| 1 | 张三 | 138xxx, 139xxx |
第二范式,要求非主键字段必须完全依赖主键,不能只依赖主键的一部分
选课表:
| 学生 ID | 课程 ID | 学生姓名 | 课程名称 | 成绩 |
|---|
主键: (学生 ID, 课程 ID)
- 学生姓名 只依赖 学生 ID
- 课程名称 只依赖 课程 ID
第三范式,非主键字段之间不能存在依赖关系,如
| 学生 ID | 姓名 | 系 ID | 系名称 |
|---|
建表的时候需要考虑哪些问题?
首先需要考虑表是否符合数据库的三大范式,确保字段不可再分,消除非主键依赖,确保字段仅依赖于主键等。
然后在选择字段类型时,应该尽量选择合适的数据类型。
在字符集上,尽量选择 utf8mb4,这样不仅可以支持中文和英文,还可以支持表情符号等。
当数据量较大时,比如上千万行数据,需要考虑分表。比如订单表,可以采用水平分表的方式来分散单表存储压力。
为什么需要三大范式?
核心目标是 减少数据冗余,避免三种异常:
- 更新异常:修改一处数据需改多处(如改产品名称,所有订单都要改)。
- 插入异常:某些信息无法单独插入(如新增产品但暂无订单,产品信息插不进订单表)。
- 删除异常:删除一条记录意外删除其他信息(如删除最后一个订单,产品信息也被删)。
不遵循三大范式的场景
例子:电商订单列表页
- 需求:快速展示订单中的 「商品名称」「商品单价」(用户高频访问,性能要求高)。
- 按 3NF 设计:订单表(订单 ID,商品 ID)+ 商品表(商品 ID,名称,单价),查询时需关联两张表。
- 实际优化:直接在订单表中冗余 「商品名称」「商品单价」,避免关联查询,牺牲少量冗余换取查询速度。
原因:高并发场景下,关联查询(JOIN)耗时,冗余存储可减少数据库 IO,提升响应速度。此时 「违反范式」 是权衡性能后的合理选择。
varchar 和 char 的区别
varchar 是可变长度的字符串类型,原则上最多可以容纳 65535 个字符,但考虑字符集,以及 MySQL 需要 1 到 2 个字节来表示字符串长度,所以实际上最大可以设置到 65533
char 是固定长度的字符类型,当定义一个 CHAR(10) 字段时,不管实际存储的字符长度是多少,都只会占用 10 个字符的空间。如果插入的数据小于 10 个字符,剩余的部分会用空格填充
in 和 exists 的区别
IN 的执行逻辑:子查询优先,结果集匹配
- 第一步:先执行子查询,得到一个 「单列值列表」(如
(1,2,3)),并临时存储; - 第二步:主查询遍历每一条记录,判断 「主表字段值是否在子查询的列表中」(类似
value IN (a,b,c)的等值判断);
EXISTS 的执行逻辑:主查询驱动,关联判断
- 第一步:先执行主查询,取出一条记录;
- 第二步:将这条记录的字段(如
u.id)传入子查询,判断子查询是否能找到匹配记录(如o.user_id = u.id); - 第三步:若子查询返回至少一条记录(存在匹配),则主查询保留这条记录;否则丢弃,继续遍历主查询下一条;
IN: 如果子查询的结果集中包含NULL值,可能会导致意外的结果
EXISTS:只是检查子查询是否返回行,不关心行的具体值,因此不受NULL值的影响
记录货币用什么类型
不推荐使用float和double,再某些情况下会存在精度问题
如果是电商、交易、账单等涉及货币的场景,建议使用 DECIMAL 类型,因为 DECIMAL 类型是精确数值类型,不会出现浮点数计算误差
例如,DECIMAL(19,4) 可以存储最多 19 位数字,其中 4 位是小数
CREATE TABLE orders (
id INT AUTO_INCREMENT,
amount DECIMAL(19,4),
PRIMARY KEY (id)
);如果是银行,涉及到支付的场景,建议使用 BIGINT 类型。可以将货币金额乘以一个固定因子,比如 100,表示以“分”为单位,然后存储为 BIGINT。这种方式既避免了浮点数问题,同时也提供了不错的性能。但在展示的时候需要除以相应的因子
drop、delete 与 truncate 的区别
DROP 是物理删除,用来删除整张表,包括表结构,且不能回滚
DELETE 支持行级删除,可以带 WHERE 条件,可以回滚
TRUNCATE 用于清空表中的所有数据,但会保留表结构,不能回滚
查询语句的执行顺序
- 先执行 FROM 确定主表
- 再执行 JOIN 连接
- 然后 WHERE 进行过滤
- 接着 GROUP BY 进行分组
- HAVING 过滤聚合结果
- SELECT 选择最终列
- ORDER BY 排序
- 最后 LIMIT 限制返回行数
WHERE 先执行是为了减少数据量,HAVING 只能过滤聚合数据,ORDER BY 必须在 SELECT 之后排序最终结果,LIMIT 最后执行以减少数据传输。