而本文的[sql操作],统一采用的是这里的数据。
(如果点击无效,可以直接翻到文章末尾,具体代码在文末)
DML-插入
1、一句话总结:在什么场景下,解决了什么问题:
在新增业务数据 (如注册用户、创建订单、写日志)场景下,insert 用来把新记录[持久化]到数据库,解决"数据从业务产生到落库保存"的问题。
2、2~4个关键词:
insert into / values
3、2个基础,并直接能用的命令:
sql
-- 更新状态
-- 1) 单行插入:新增一个用户(注意:显式写列名,最稳)
insert into `user` (id, username, nickname, email, city, created_at, updated_at)
values (2002, 'mike', null, 'mike@example.com', 'Beijing', now(), now());
-- 2) 多行插入:一次插入多条日志(演示批量 values)
insert into `log` (id, level, message, created_at)
values
(60010, 'info', 'new log 1', now()),
(60011, 'warn', 'new log 2', now());
AI写代码sql
12345678910
4、1个坑,最容易翻车的点:
不写列名导致列顺序一变就插错;最好永远写上列名。
DML-更新和删除
1、一句话总结:在什么场景下,解决了什么问题:
改/删数据,用于修改业务状态。通过where 进行精确定位,避免误伤。
2、2~4个关键词:
delete / update / where/ limit
3、2个基础,并直接能用的命令:
sql
-- 更新状态
update `orders`
set status = 'PAID'
where id = 50002;
-- 删除日志
delete from `log`
where created_at<NOW()-interval 30 day
AI写代码sql
1234567
4、1个坑,最容易翻车的点:
delete中,漏写where,导致全表数据删除。
DQL-基础查询
1、一句话总结:在什么场景下,解决了什么问题:
查数据并做列表,只拿需要的列 ,有条件,有分页。
2、2~4个关键词:
select / distinct / 列投影 / as
3、2个基础,并直接能用的命令:
sql
select id,username from user;
select distinct city from user;
AI写代码sql
12
4、1个坑,最容易翻车的点:
select * 在查询大表/宽表时很慢,并且容易触发回表 与网络IO
DQL-条件查询
1、一句话总结:在什么场景下,解决了什么问题:
用where 筛选,筛选出"我需要的数据"
2、2~4个关键词:
where / like / or / between
3、2个基础,并直接能用的命令:
sql
-- 查今天已支付订单
SELECT * FROM `orders`
WHERE status='PAID' AND pay_time >= CURDATE();
-- 查 username 以 a 开头 或 指定 id 集合
SELECT * FROM `user`
WHERE username LIKE 'a%' OR id IN (1001,1002,1003);
AI写代码sql
1234567
4、1个坑,最容易翻车的点:
null 不能用 " =null " ,而应该用 is null / is not null
DQL-聚合函数
1、一句话总结:在什么场景下,解决了什么问题:
统计报表:总数,平均数,最大最小值
2、2~4个关键词:
sum / avg / min / max
3、2个基础,并直接能用的命令:
sql
-- 总数
select count(*) from orders where status = 'PAID';
-- 分组+平均数
select dept_id,avg(salary) as avg_salary from emp group by dept_id;
AI写代码sql
1234
4、1个坑,最容易翻车的点:
count(col) ,会忽略掉NULL值,所以用count(*),是最保底的选择。
DQL-分组查询
1、一句话总结:在什么场景下,解决了什么问题:
分组聚合,并按 "维度查询 "
2、2~4个关键词:
group by / having / 维度聚合
3、2个基础,并直接能用的命令:
sql
select city , count(*) from user group by city ;
select city , count(*) as c from user group by city having c>2;
AI写代码sql
123
4、1个坑,最容易翻车的点:
where vs having:其中,where过滤的是行,而having过滤的是聚合后的结果
DQL-排序查询
1、一句话总结:在什么场景下,解决了什么问题:
列表,按照时间、分数...排序
2、2~4个关键词:
order by / desc / asc
3、2个基础,并直接能用的命令:
sql
select * from `post` order by created_at desc ;
select * from `post` order by score desc , created_at desc ;
AI写代码sql
123
4、1个坑,最容易翻车的点:
大 OFFSET 排序很慢。(可以通过索引进行优化)
DQL-分页查询
1、一句话总结:在什么场景下,解决了什么问题:
列表分页,解决 "只取一页的问题" 。
2、2~4个关键词:
limit / offset / 深分页
3、2个基础,并直接能用的命令:
sql
select * from `post` order by id desc limit 5 offset 0;
-- 游标分页(直接确定id,然后往前翻)
select * from `post` where id<100000 order by id desc limit 3;
AI写代码sql
123
4、1个坑,最容易翻车的点:
深分页,如offset=10000会抛弃大量行,所以推荐使用游标分页。
DQL-案例联系
1、一句话总结:在什么场景下,解决了什么问题:
综合使用
2、2~4个关键词:
组合查询 / 可读性 / 逐步验证
3、2个基础,并直接能用的命令:
sql
-- 例:查北京用户的已支付订单列表
SELECT o.id, o.amount, o.created_at, u.username
FROM `orders` o
JOIN `user` u ON o.user_id=u.id
WHERE u.city='Beijing' AND o.status='PAID'
ORDER BY o.created_at DESC
LIMIT 5;
EXPLAIN
SELECT o.id, o.amount, o.created_at, u.username
FROM `orders` o
JOIN `user` u ON o.user_id=u.id
WHERE u.city='Beijing' AND o.status='PAID'
ORDER BY o.created_at DESC
LIMIT 5;
AI写代码sql
123456789101112131415
4、1个坑,最容易翻车的点:
不 explain 就上生产,就很容易写出慢查询
DQL-执行顺序
1、一句话总结:在什么场景下,解决了什么问题:
解释,为什么别名能够在having 中用,而不能在where 中用。
2、2~4个关键词:
from / where / limit / select / group by / having
3、2个基础,并直接能用的命令:
sql
select dept_id, count(*) as c
from emp
group by dept_id
having c>1;
AI写代码sql
1234
4、1个坑,最容易翻车的点:
执行顺序
from > where > group by > having > select > order by > limit
原本按顺序,having应该也无法看到别名,但是mysql做了拓展。
DQL-小结
1、一句话总结:在什么场景下,解决了什么问题:
查询 = "筛选(where)+聚合(group)+排序(order)+分页(limit)+索引(explain)"
2、2~4个关键词:
explain 回表 索引
3、2个基础,并直接能用的命令:
sql
EXPLAIN SELECT ...;
--暂时,还没怎么用过
SHOW WARNINGS;(看优化器重写/警告)
AI写代码sql
123
4、1个坑,最容易翻车的点:
只会写代码,而不会检查,非常掉分。
DCL-用户管理
1、一句话总结:在什么场景下,解决了什么问题:
管理账号与登录来源,用最小的权限,隔离环境
2、2~4个关键词:
create user / alter user / drop user
3、2个基础,并直接能用的命令:
sql
create user 'user'@'localhost' identified by '123456';
alter user 'user'@'localhost' identified by '1234';
AI写代码sql
123
4、1个坑,最容易翻车的点:
'user'@'localhost' 与 'user'@'%'是两条不同的账户,经常配错,出现能连/不能连的问题。(需注意优先级)
DCL-权限控制
1、一句话总结:在什么场景下,解决了什么问题:
控制谁,能对某些表,进行特定的权限操作。避免越权 与误删 。
2、2~4个关键词:
grant / revoke / show grants
3、2个基础,并直接能用的命令:
sql
grant select,alter on interview_mysql.dept to 'user'@'localhost';
--展示权限
show grants for 'user'@'localhost'
AI写代码sql
123
4、1个坑,最容易翻车的点:
直接给 grant all 是管理上的惰性,会给未来增加不可预测的风险。
DCL-小结
1、一句话总结:在什么场景下,解决了什么问题:
账号="主题", 权限="动作+资源范围",原则="最小权限"
2、2~4个关键词:
最小权限 / 审计 / 账号隔离
3、2个基础,并直接能用的命令:
sql
-- 删除
revoke select on interview_mysql.dept from 'user'@'localhost';
-- 刷新,并清空
flush privileges;
AI写代码sql
1234
4、1个坑,最容易翻车的点:
误以为改了权限就会马上生效:alter/revoke在mysql中自动刷新,通常不需要flush。
函数-字符串函数
1、一句话总结:在什么场景下,解决了什么问题:
清洗/格式化文本,拼接展示
2、2~4个关键词:
concat / replace / substring / length
3、2个基础,并直接能用的命令:
sql
-- concat(..)组合
-- coalesce(..)选取第一个非空
select concat(username,'(',coalesce(nickname,'-'),')') as showName
from user;
--替换
select replace(email,'@example.com','@demo.com')
from user;
AI写代码sql
1234567
4、1个坑,最容易翻车的点:
length()统计长度,中文可能会变长。用char_length()更合适。
函数-数据函数
1、一句话总结:在什么场景下,解决了什么问题:
做 金额/评分/统计 时,需要用到的数值处理
2、2~4个关键词:
round / ceil/floor / abs / decimal
3、2个基础,并直接能用的命令:
python
select id,round(amount,2) as money
from orders;
select id,floor(amount),ceil(amount) as money
from orders;
AI写代码sql
12345
4、1个坑,最容易翻车的点:
金额计算时别用浮点数存储,用decimal(10,2)-定点数,否则会有精度的坑。
函数-日期函数
1、一句话总结:在什么场景下,解决了什么问题:
解决了数据分析 与时间报表 中,对时间的分析与处理。
2、2~4个关键词:
interval / date / date_add / timestampdiff
3、2个基础,并直接能用的命令:
sql
select *
from orders
where created_at>now()-interval 7 day;
select date(created_at) as d ,count(*) as c
from orders
group by d
order by c
--计算时间差值(时间单位,time1,time2)
select timestampdiff(day,date(created_at),'2025-11-20') as diff
from orders
AI写代码sql
1234567891011
4、1个坑,最容易翻车的点:
应用/DB 时区不一致会导致时间错位。可以统一时区,或者用UTC。
函数-流程函数
1、一句话总结:在什么场景下,解决了什么问题:
在sql层,做简单的分支控制 / 空值处理 / 分类统计
2、2~4个关键词:
case / coalesce / if / when
3、2个基础,并直接能用的命令:
sql
SELECT id, COALESCE(nickname, username) AS show_name
FROM `user`;
select
sum(case when status = 'PAID' then 1 else 0 end) as paid_status,
sum(case when status = 'CREATED' then 1 else 0 end) as created_status
from orders;
AI写代码sql
1234567
4、1个坑,最容易翻车的点:
case太复杂,会导致难以维护,应放在应用层。
约束-概述
1、一句话总结:在什么场景下,解决了什么问题:
用约束保证数据的正确性:唯一/非空/范围...等等
2、2~4个关键词:
primary key / unique / not null / check
3、2个基础,并直接能用的命令:
sql
show create table `user`;
show index from `user`;
AI写代码sql
123
4、1个坑,最容易翻车的点:
只靠代码约束不保底;并发下需要数据库进行保底。
约束-外键约束
1、一句话总结:在什么场景下,解决了什么问题:
保证 "子表引用父亲合理化" ,避免孤儿数据。
2、2~4个关键词:
foreign key / 父子表 / 引用完整性
3、2个基础,并直接能用的命令:
sql
show create table `order_item`;
-- 触发外键:order_id 不存在(会报 cannot add or update a child row)
insert into `order_item`(id, order_id, product_name, price, qty, created_at)
values (79999, 99999, 'baditem', 1.00, 1, now());
AI写代码sql
123456
4、1个坑,最容易翻车的点:
外键可能会带来写入约束 与锁影响。因此在高并发场景下可能会替换 掉约束。
约束-小结
1、一句话总结:在什么场景下,解决了什么问题:
约束=数据库的最后一道防线,尤其主键/唯一/非空 这些是高频应用,而外键则需要另外考虑。
2、2~4个关键词:
并发一致性 / 数据质量 / 兜底
3、2个基础,并直接能用的命令:
sql
show index from `orders`;
show create table `orders`;
AI写代码sql
123
4、1个坑,最容易翻车的点:
没有约束,仅靠业务查重,在并发场景下必翻车(典型:"先查后插"竞态)
多表关系介绍
1、一句话总结:在什么场景下,解决了什么问题:
关系:一对一/一对多/多对多,决定表结构与连接方式。
2、2~4个关键词:
1:1 / 1:n / n:m / 中间表
3、2个基础,并直接能用的命令:
sql
-- 多对多 连表查询
select u.username, r.name as role_name
from `user_role` ur
join `user` u on ur.user_id = u.id
join `role` r on ur.role_id = r.id
where u.id = 1001;
-- 查看中间表
show create table `user_role`;
AI写代码sql
12345678910
4、1个坑,最容易翻车的点:
多对多表格中,需要建立中间表。如果硬塞逗号隔开 = 灾难(无法索引,无法约束,难以维护),
多表查询概述
1、一句话总结:在什么场景下,解决了什么问题:
把多个表拼接成一个结果集
2、2~4个关键词:
join连接表 / on筛选条件
3、2个基础,并直接能用的命令:
sql
select o.id, u.username, o.status, o.amount
from `orders` o
join `user` u on o.user_id=u.id;
AI写代码sql
123
4、1个坑,最容易翻车的点:
如果忘写on 或者条件写错,会导致笛卡尔积,结果爆炸且超慢。
内连接
1、一句话总结:在什么场景下,解决了什么问题:
只要两边都匹配的记录:最常见的业务联表(inner join = join)
2、2~4个关键词:
inner join / 等值连接
3、2个基础,并直接能用的命令:
sql
select o.id, u.username
from `orders` o
inner join `user` u on o.user_id = u.id;
select o.id, u.username
from `orders` o
join `user` u on o.user_id = u.id;
AI写代码sql
1234567
4、1个坑,最容易翻车的点:
列名冲突,一定要用o.id,否则会造成不同表格之间的列名冲突。
外连接
1、一句话总结:在什么场景下,解决了什么问题:
保留一侧:"用户列表+是否下过订单"
2、2~4个关键词:
left join / right join
3、2个基础,并直接能用的命令:
sql
-- 主表为:user,主表
select u.id, u.username, o.id as order_id
from `user` u
left join `orders` o on u.id = o.user_id;
-- 只保留那些「没有任何订单匹配」的用户
select u.*
from `user` u
left join `orders` o on u.id = o.user_id
where o.id is null;
AI写代码sql
1234567891011
4、1个坑,最容易翻车的点:
把右表on内的 匹配条件,写到where中,会把left join 变成 inner join。
因为on决定是如何匹配 ,而where决定的是那些行能活下来。
自连接
1、一句话总结:在什么场景下,解决了什么问题:
一张表自己连自己:一般用于 组织关系/上下级/...
2、2~4个关键词:
层级关系 / 别名
3、2个基础,并直接能用的命令:
csharp
select e.name as emp_name, m.name as manager_name
from `emp` e
left join `emp` m on e.manager_id = m.id;
select *
from `emp`
where manager_id is null;
AI写代码sql
1234567
4、1个坑,最容易翻车的点:
别名起的不好,就会错列;自连接必须全程通过别名区分。
联合查询 union
1、一句话总结:在什么场景下,解决了什么问题:
把两条查询上下拼接成一张结果(字段数与类型要对齐)
2、2~4个关键词:
union 去重 / union all 不去重
3、2个基础,并直接能用的命令:
sql
-- 去重
select id from `t_a`
union
select id from `t_b`;
-- 不去重
select id from `t_a`
union all
select id from `t_b`;
AI写代码sql
12345678
4、1个坑,最容易翻车的点:
默认union会去重,稍慢。所以能用union all尽量就用这个。
子查询
1、一句话总结:在什么场景下,解决了什么问题:
把一个查询的结果当成另一个查询的条件/表;常用于"先算出集合再过滤"
2、2~4个关键词:
in / exists
3、2个基础,并直接能用的命令:
sql
-- r&d 部门的员工
select * from `emp`
where dept_id in (select id from `dept` where name = 'r&d');
-- exists 写法
select * from `emp` e
where exists (
select 1
from `dept` d
where d.id = e.dept_id
and d.name = 'r&d'
);
AI写代码sql
123456789101112
4、1个坑,最容易翻车的点:
子查询不一定慢,但相关子查询写不好会重复执行;能 JOIN 时也要会解释取舍。
标量子查询
1、一句话总结:在什么场景下,解决了什么问题:
子查询返回单个值:用于"和某个统计值比较"。
2、2~4个关键词:
scalar / 标量 / 聚合比较
3、2个基础,并直接能用的命令:
sql
SELECT * FROM `emp`
WHERE salary > (SELECT AVG(salary) FROM `emp`);
SELECT * FROM `orders`
WHERE amount = (SELECT MAX(amount) FROM `orders`);
AI写代码sql
12345
4、1个坑,最容易翻车的点:
子查询返回多行会报错:Subquery returns more than 1 row,必须保证唯一 或加聚合 / limit。
列子查询
1、一句话总结:在什么场景下,解决了什么问题:
子查询返回一列多行:用于 IN/ANY/ALL。
2、2~4个关键词:
in / any / all
3、2个基础,并直接能用的命令:
sql
select * from `emp`
where dept_id in(select id from `dept` where city = 'beijing');
select * from `emp`
where salary > all(select salary from `emp` where dept_id = 10);
AI写代码sql
123456
4、1个坑,最容易翻车的点:
in 遇到 null 可能让逻辑变怪;必要时用 exists 更稳。
因为 sql 不是"二值逻辑",而是"三值逻辑":true / false / unknown(未知)。
可以动手看看,这个简单的例子:
csharp
--这样没啥问题
select* from `emp`
where id in (null,1,2);
真正会把你坑惨的,是 not in + null
select* from `emp`
where id not in (null,1,2);
明明有好几行符合条件的,却一行都不会返回!!
AI写代码sql
12345678
行子查询
1、一句话总结:在什么场景下,解决了什么问题:
自查询返回一行多列
2、2~4个关键词:
row constructor / 多列比较
3、2个基础,并直接能用的命令:
sql
select * from `emp`
where (dept_id, job) = (select dept_id, job from `emp` where id = 2);
AI写代码sql
12
4、1个坑,最容易翻车的点:
复合列要有合适联合索引,不然容易全表扫。
表子查询
1、一句话总结:在什么场景下,解决了什么问题:
把子查询当作临时表 (派生表),在 join / 过滤
2、2~4个关键词:
临时表 / derived table
3、2个基础,并直接能用的命令:
vbnet
select x.dept_id, x.c
from (select dept_id, count(*) as c from emp group by dept_id) x
where x.c > 2;
select y.user_id, y.total_amount
from (select user_id, sum(amount) as total_amount from orders group by user_id) y
order by y.total_amount desc
limit 5;
AI写代码sql
12345678
4、1个坑,最容易翻车的点:
派生表可能物化到临时表,数据量大很慢;尽量让派生表可被优化(字段少、条件早过滤)。
事务简介
1、一句话总结:在什么场景下,解决了什么问题:
事务,用于把多步操作,变成"一同成功,或一同失败"
2、2~4个关键词:
transaction / commit / rollback
3、2个基础,并直接能用的命令:
sql
start transaction;
commit;
-- 或
rollback;
AI写代码sql
1234
4、1个坑,最容易翻车的点:
MySQL 默认 autocommit=1,你以为在事务里其实不是;要么显式开启,要么在框架里确认。
事务演示
1、一句话总结:在什么场景下,解决了什么问题:
必知步骤:开启事务 -> 更新 -> 异常回滚 -> 保证了一致性
2、2~4个关键词:
锁 / 回滚 / 一致性
3、2个基础,并直接能用的命令:
sql
--开启事务
start transaction;
update `account` set balance=balance-100, updated_at=now() where id=9001;
update `account` set balance=balance+100, updated_at=now() where id=9002;
--提交
commit;
AI写代码sql
1234567
4、1个坑,最容易翻车的点:
表引擎不是 InnoDB(如 MyISAM)不支持事务;面试一问就中招。
ACID四大特性
1、一句话总结:在什么场景下,解决了什么问题:
用 ACID 解释"为什么事务可靠":原子/一致/隔离/持久。
2、2~4个关键词:
Atomic / Consistent / Isolated / Durable
3、2个基础,并直接能用的命令:
sql
-- 查看是否开启自动提交
select @@autocommit;
-- 查看隔离程度
show variables like 'transaction_isolation';
AI写代码sql
1234
4、1个坑,最容易翻车的点:
你说"有事务就绝对一致"不严谨:一致性还依赖约束、正确的业务逻辑与隔离级别。
并发事务问题
1、一句话总结:在什么场景下,解决了什么问题:
并发下会出现:脏读、不可重复读、幻读。
2、2~4个关键词:
dirty read / non-repeatable / phantom
3、2个基础,并直接能用的命令:
arduino
set session transaction isolation level read committed;
set session transaction isolation level repeatable read;
AI写代码sql
123
4、1个坑,最容易翻车的点:
MySQL InnoDB 在 RR 隔离级别下,普通查询通过 MVCC 保证可重复读并避免幻读;而在范围更新或锁定读中,会通过行锁和间隙锁(Next-Key Lock)防止幻读,但这也可能导致范围锁定,从而引发意料之外的锁等待。
隔离级别
1、一句话总结:在什么场景下,解决了什么问题:
不同的隔离级别,解决了什么问题?代价是什么?
2、2~4个关键词:
RU / RC / RR / SERIALIZABLE / MVCC
3、2个基础,并直接能用的命令:
sql
start transaction;
select * from `account` where id=9001 for update;
-- 另一个会话可用共享锁读(mysql8 推荐 for share)
select * from `account` where id=9001 for share;
-- 提交/回滚释放锁
commit;
AI写代码sql
1234567
4、1个坑,最容易翻车的点:
for update 在无索引条件下可能锁全表/大量行;一定要让 where 走索引。
实验数据,可直接插入mysql
sql
-- =========================================================
MySQL 8(InnoDB + utf8mb4)
-- =========================================================
DROP DATABASE IF EXISTS interview_mysql;
CREATE DATABASE interview_mysql
DEFAULT CHARACTER SET utf8mb4
COLLATE utf8mb4_0900_ai_ci;
USE interview_mysql;
-- 关闭安全更新(练习 UPDATE/DELETE 时更方便;生产不建议)
SET SESSION sql_safe_updates = 0;
-- -----------------------
-- 清理旧表(按外键依赖顺序)
-- -----------------------
DROP TABLE IF EXISTS order_item;
DROP TABLE IF EXISTS orders;
DROP TABLE IF EXISTS post;
DROP TABLE IF EXISTS user_role;
DROP TABLE IF EXISTS role;
DROP TABLE IF EXISTS account;
DROP TABLE IF EXISTS emp;
DROP TABLE IF EXISTS dept;
DROP TABLE IF EXISTS log;
DROP TABLE IF EXISTS t_a;
DROP TABLE IF EXISTS t_b;
DROP TABLE IF EXISTS user;
-- -----------------------
-- 1) 用户表(DQL / NULL / 字符串函数)
-- -----------------------
CREATE TABLE user (
id BIGINT PRIMARY KEY,
username VARCHAR(32) NOT NULL UNIQUE,
nickname VARCHAR(32) NULL,
email VARCHAR(64) NOT NULL UNIQUE,
city VARCHAR(32) NOT NULL,
created_at DATETIME NOT NULL,
updated_at DATETIME NOT NULL,
INDEX idx_user_city (city),
INDEX idx_user_created (created_at)
) ENGINE=InnoDB;
-- -----------------------
-- 2) 角色表 + 多对多中间表(多表/约束)
-- -----------------------
CREATE TABLE role (
id INT PRIMARY KEY,
name VARCHAR(32) NOT NULL UNIQUE
) ENGINE=InnoDB;
CREATE TABLE user_role (
user_id BIGINT NOT NULL,
role_id INT NOT NULL,
PRIMARY KEY (user_id, role_id),
CONSTRAINT fk_ur_user FOREIGN KEY (user_id) REFERENCES user(id),
CONSTRAINT fk_ur_role FOREIGN KEY (role_id) REFERENCES role(id)
) ENGINE=InnoDB;
-- -----------------------
-- 3) 部门/员工(JOIN + 自连接 + 聚合)
-- -----------------------
CREATE TABLE dept (
id INT PRIMARY KEY,
name VARCHAR(32) NOT NULL UNIQUE,
city VARCHAR(32) NOT NULL
) ENGINE=InnoDB;
CREATE TABLE emp (
id INT PRIMARY KEY,
name VARCHAR(32) NOT NULL,
dept_id INT NOT NULL,
manager_id INT NULL,
job VARCHAR(32) NOT NULL,
salary DECIMAL(10,2) NOT NULL,
hired_at DATE NOT NULL,
CONSTRAINT fk_emp_dept FOREIGN KEY (dept_id) REFERENCES dept(id),
CONSTRAINT fk_emp_mgr FOREIGN KEY (manager_id) REFERENCES emp(id),
INDEX idx_emp_dept (dept_id),
INDEX idx_emp_mgr (manager_id),
INDEX idx_emp_salary (salary)
) ENGINE=InnoDB;
-- -----------------------
-- 4) 账户表(事务转账练习)
-- -----------------------
CREATE TABLE account (
id BIGINT PRIMARY KEY,
user_id BIGINT NOT NULL UNIQUE,
balance DECIMAL(12,2) NOT NULL,
updated_at DATETIME NOT NULL,
CONSTRAINT fk_acc_user FOREIGN KEY (user_id) REFERENCES user(id),
INDEX idx_acc_balance (balance)
) ENGINE=InnoDB;
-- -----------------------
-- 5) 订单/明细(JOIN / 外键 / 分组统计 / NULL pay_time)
-- -----------------------
CREATE TABLE orders (
id BIGINT PRIMARY KEY,
user_id BIGINT NOT NULL,
status VARCHAR(16) NOT NULL, -- CREATED/PAID/CANCELLED/REFUNDED
amount DECIMAL(12,2) NOT NULL,
created_at DATETIME NOT NULL,
pay_time DATETIME NULL,
updated_at DATETIME NOT NULL,
CONSTRAINT fk_orders_user FOREIGN KEY (user_id) REFERENCES user(id),
INDEX idx_orders_user (user_id),
INDEX idx_orders_status_time (status, created_at),
INDEX idx_orders_paytime (pay_time)
) ENGINE=InnoDB;
CREATE TABLE order_item (
id BIGINT PRIMARY KEY,
order_id BIGINT NOT NULL,
product_name VARCHAR(64) NOT NULL,
price DECIMAL(12,2) NOT NULL,
qty INT NOT NULL,
created_at DATETIME NOT NULL,
CONSTRAINT fk_item_order FOREIGN KEY (order_id) REFERENCES orders(id),
INDEX idx_item_order (order_id)
) ENGINE=InnoDB;
-- -----------------------
-- 6) 帖子表(排序/分页/统计)
-- -----------------------
CREATE TABLE post (
id BIGINT PRIMARY KEY,
user_id BIGINT NOT NULL,
title VARCHAR(128) NOT NULL,
score INT NOT NULL,
created_at DATETIME NOT NULL,
CONSTRAINT fk_post_user FOREIGN KEY (user_id) REFERENCES user(id),
INDEX idx_post_score_time (score, created_at),
INDEX idx_post_user (user_id)
) ENGINE=InnoDB;
-- -----------------------
-- 7) 日志表(DML 删除练习:清理 30 天前)
-- -----------------------
CREATE TABLE log (
id BIGINT PRIMARY KEY,
level VARCHAR(8) NOT NULL,
message VARCHAR(255) NOT NULL,
created_at DATETIME NOT NULL,
INDEX idx_log_time (created_at),
INDEX idx_log_level (level)
) ENGINE=InnoDB;
-- -----------------------
-- 8) UNION 练习用表
-- -----------------------
CREATE TABLE t_a (
id INT PRIMARY KEY
) ENGINE=InnoDB;
CREATE TABLE t_b (
id INT PRIMARY KEY
) ENGINE=InnoDB;
-- =========================================================
-- 插入数据
-- =========================================================
-- users(含 nickname NULL、不同城市、不同时间)
INSERT INTO user (id, username, nickname, email, city, created_at, updated_at) VALUES
(1001,'alice', '阿丽', 'alice@example.com', 'Beijing', '2025-11-01 10:00:00','2025-12-10 09:10:00'),
(1002,'bob', NULL, 'bob@example.com', 'Beijing', '2025-11-05 11:00:00','2025-12-11 12:00:00'),
(1003,'carol', '小C', 'carol@example.com', 'Shanghai', '2025-10-20 09:30:00','2025-12-09 08:00:00'),
(1004,'dave', NULL, 'dave@example.com', 'Shanghai', '2025-11-12 16:20:00','2025-12-12 19:00:00'),
(1005,'erin', 'E宝', 'erin@example.com', 'Shenzhen', '2025-11-18 14:00:00','2025-12-13 10:01:00'),
(1006,'frank', NULL, 'frank@example.com', 'Shenzhen', '2025-11-21 17:45:00','2025-12-13 17:45:00'),
(1007,'grace', '小G', 'grace@example.com', 'Guangzhou', '2025-10-01 08:00:00','2025-12-01 08:00:00'),
(1008,'henry', NULL, 'henry@example.com', 'Guangzhou', '2025-09-15 09:00:00','2025-11-30 10:00:00'),
(1009,'ivy', 'Ivy', 'ivy@example.com', 'Hangzhou', '2025-11-30 20:00:00','2025-12-12 20:00:00'),
(1010,'jack', NULL, 'jack@example.com', 'Hangzhou', '2025-12-01 09:00:00','2025-12-13 09:00:00'),
(1011,'kate', 'K', 'kate@example.com', 'Beijing', '2025-10-10 10:10:10','2025-12-13 23:59:00'),
(1012,'leo', NULL, 'leo@example.com', 'Shanghai', '2025-12-05 12:12:12','2025-12-12 12:12:12');
-- roles
INSERT INTO role (id, name) VALUES
(1,'admin'),
(2,'merchant'),
(3,'customer');
-- user_role(多对多)
INSERT INTO user_role (user_id, role_id) VALUES
(1001,1),(1001,3),
(1002,3),
(1003,2),(1003,3),
(1004,3),
(1005,2),
(1006,3),
(1007,3),
(1008,3),
(1009,3),
(1010,3),
(1011,1),
(1012,3);
-- dept
INSERT INTO dept (id, name, city) VALUES
(10,'R&D','Beijing'),
(20,'Sales','Shanghai'),
(30,'Ops','Shenzhen');
-- emp(自连接:先插 manager,再插下属)
INSERT INTO emp (id, name, dept_id, manager_id, job, salary, hired_at) VALUES
(1,'ZhangBoss', 10, NULL,'Director', 35000.00,'2022-01-01'),
(2,'LiDev', 10, 1, 'Backend', 22000.00,'2023-03-15'),
(3,'WangDev', 10, 1, 'Backend', 21000.00,'2023-06-01'),
(4,'ChenQA', 10, 1, 'QA', 18000.00,'2024-02-20'),
(5,'ZhaoSales', 20, NULL,'Manager', 26000.00,'2021-09-10'),
(6,'SunSales', 20, 5, 'Sales', 16000.00,'2024-05-01'),
(7,'WuOps', 30, NULL,'Manager', 24000.00,'2022-11-11'),
(8,'HeSRE', 30, 7, 'SRE', 20000.00,'2024-08-08');
-- account(事务练习:转账)
INSERT INTO account (id, user_id, balance, updated_at) VALUES
(9001,1001,1000.00,'2025-12-13 10:00:00'),
(9002,1002, 250.50,'2025-12-13 10:00:00'),
(9003,1003, 88.80,'2025-12-13 10:00:00'),
(9004,1004, 500.00,'2025-12-13 10:00:00'),
(9005,1005, 999.99,'2025-12-13 10:00:00'),
(9006,1006, 10.00,'2025-12-13 10:00:00'),
(9007,1007, 66.66,'2025-12-13 10:00:00'),
(9008,1008, 77.77,'2025-12-13 10:00:00'),
(9009,1009, 300.00,'2025-12-13 10:00:00'),
(9010,1010, 120.00,'2025-12-13 10:00:00'),
(9011,1011,8888.88,'2025-12-13 10:00:00'),
(9012,1012, 15.00,'2025-12-13 10:00:00');
-- orders(包含:已支付/未支付/取消/退款;也包含"无明细订单"用于 LEFT JOIN)
INSERT INTO orders (id, user_id, status, amount, created_at, pay_time, updated_at) VALUES
(50001,1001,'PAID', 128.00,'2025-12-01 10:00:00','2025-12-01 10:02:00','2025-12-01 10:02:00'),
(50002,1001,'CREATED', 66.60,'2025-12-10 12:00:00',NULL, '2025-12-10 12:00:00'),
(50003,1002,'PAID', 19.90,'2025-12-02 09:00:00','2025-12-02 09:01:00','2025-12-02 09:01:00'),
(50004,1003,'CANCELLED', 58.00,'2025-11-20 18:30:00',NULL, '2025-11-20 18:45:00'),
(50005,1003,'PAID', 199.00,'2025-12-05 20:00:00','2025-12-05 20:10:00','2025-12-05 20:10:00'),
(50006,1004,'REFUNDED', 88.00,'2025-11-28 13:00:00','2025-11-28 13:05:00','2025-11-30 09:00:00'),
(50007,1005,'PAID', 520.00,'2025-12-11 11:11:11','2025-12-11 11:12:00','2025-12-11 11:12:00'),
(50008,1006,'CREATED', 39.90,'2025-12-12 15:00:00',NULL, '2025-12-12 15:00:00'),
(50009,1009,'PAID', 15.00,'2025-12-03 08:00:00','2025-12-03 08:00:30','2025-12-03 08:00:30'),
(50010,1010,'PAID', 88.88,'2025-12-13 09:09:09','2025-12-13 09:10:00','2025-12-13 09:10:00');
-- order_item(50002/50008 故意不给明细;练习"找无明细订单")
INSERT INTO order_item (id, order_id, product_name, price, qty, created_at) VALUES
(70001,50001,'Keyboard', 128.00,1,'2025-12-01 10:00:10'),
(70002,50003,'Notebook', 19.90,1,'2025-12-02 09:00:10'),
(70003,50005,'Monitor', 199.00,1,'2025-12-05 20:00:10'),
(70004,50006,'Mouse', 88.00,1,'2025-11-28 13:00:10'),
(70005,50007,'Phone', 520.00,1,'2025-12-11 11:11:20'),
(70006,50009,'Cable', 15.00,1,'2025-12-03 08:00:10'),
(70007,50010,'Headset', 88.88,1,'2025-12-13 09:09:20');
-- posts(用于 ORDER BY / LIMIT / GROUP BY / HAVING)
INSERT INTO post (id, user_id, title, score, created_at) VALUES
(80001,1001,'MySQL 基础复盘', 98,'2025-12-01 09:00:00'),
(80002,1001,'JOIN 面试题整理', 88,'2025-12-06 09:00:00'),
(80003,1002,'分页优化:深分页到游标', 95,'2025-12-10 21:00:00'),
(80004,1003,'事务与锁:RR 是否幻读', 93,'2025-12-08 20:00:00'),
(80005,1003,'EXPLAIN 怎么看', 80,'2025-11-22 10:00:00'),
(80006,1004,'子查询与 EXISTS 对比', 70,'2025-12-02 19:00:00'),
(80007,1005,'外键要不要上生产', 85,'2025-12-03 13:00:00'),
(80008,1006,'字符串函数清洗数据', 60,'2025-12-04 14:00:00'),
(80009,1007,'GROUP BY + HAVING 实战', 92,'2025-12-05 16:00:00'),
(80010,1008,'慢查询排查流程', 99,'2025-12-07 18:00:00'),
(80011,1009,'NULL 处理:COALESCE', 75,'2025-12-09 08:00:00'),
(80012,1010,'UNION vs UNION ALL', 65,'2025-12-12 08:30:00');
-- log(包含 30 天前的记录,练习 DELETE)
INSERT INTO log (id, level, message, created_at) VALUES
(60001,'INFO','service started', '2025-12-13 10:00:00'),
(60002,'WARN','slow query detected', '2025-12-12 11:00:00'),
(60003,'ERROR','db connection timeout', '2025-12-11 12:00:00'),
(60004,'INFO','daily job finished', '2025-11-10 09:00:00'),
(60005,'INFO','old log for cleanup (40d ago)', '2025-11-01 00:00:00'),
(60006,'INFO','old log for cleanup (70d ago)', '2025-10-05 00:00:00');
-- UNION 练习数据
INSERT INTO t_a (id) VALUES (1),(2),(3),(10);
INSERT INTO t_b (id) VALUES (3),(4),(5),(10);
-- =========================================================
-- (可选)快速自检:确认数据量
-- =========================================================
-- SELECT COUNT(*) AS users FROM user;
-- SELECT status, COUNT(*) FROM orders GROUP BY status;
-- SELECT city, COUNT(*) FROM user GROUP BY city;
AI写代码
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291
本文为最小可复用笔记。