今天上午前两节课的内容拖到晚上才整理。两节课主题都挺大一节叫“建造数据库”从口号变成实操一节叫“合理运用豆包”把AI工具真正用进MySQL学习里。这种笔记最大的问题是容易写成流水账——建了库、建了表、查了几条数据当时感觉全会了第二天全忘。所以我这次刻意把课上敲过的每一条语句、每一个报错、豆包给出的每一个带坑的答案全部原样留档顺带补上自己的复盘给需要补MySQL基础的人一份能照着走的记录。1. 从空库开始把第一张表立起来1.1 建库语句里的字符集玄机“建造数据库”的第一步不是急着敲CREATE DATABASE而是先想清楚字符集。这节课开篇就踩了个闷坑我按习惯写了一句最简的建库语句CREATE DATABASE school_db;结果往里导入一张带emoji备注的学生名单CSV时中文全变成了问号。问题就出在没显式指定字符集。MySQL 8.0虽然默认是utf8mb4但5.7时代经常要靠建库语句自己指定否则很容易落到latin1的坑里。latin1对中文不友好对emoji更是直接罢工。老师给的标准写法是这样的CREATE DATABASE IF NOT EXISTS school_db DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;这里的utf8mb4才是真正能存下所有Unicode字符的编码一个emoji占4字节。MySQL里的utf8其实只是utf8mb3最多3字节碰到生僻字或者表情符号就抓瞎。COLLATE选utf8mb4_unicode_ci排序更符合Unicode规范utf8mb4_general_ci虽然快一点但排序精度糙学习阶段无脑用unicode_ci就行。顺带记一个教训DROP DATABASE这条命令没有确认提示手一抖整个库就没了。课上有人演示删除全班都看见那行命令从屏幕上滑过去库瞬间清空连恢复的时间都没有。所以建库前先CREATE删库前先深呼吸最好再备份一次。1.2 字段类型的取舍别让一次选择变成长期返工建表才是“建造”两个字的真正重心。我们课上做了一套学生选课系统的demo第一张表是学生表。老师让我们先列字段再敲SQL每个人交上来的类型五花八门最后统一成下面这版CREATE TABLE student ( id INT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT 学生ID, student_no VARCHAR(20) NOT NULL COMMENT 学号, name VARCHAR(50) NOT NULL COMMENT 姓名, gender TINYINT NOT NULL DEFAULT 0 COMMENT 性别 0未知 1男 2女, birth_date DATE DEFAULT NULL COMMENT 出生日期, phone VARCHAR(20) DEFAULT NULL COMMENT 手机号, create_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT 创建时间, update_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 更新时间, PRIMARY KEY (id), UNIQUE KEY uk_student_no (student_no) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT学生表;这里面的门道比表面看起来多得多。字段类型选错了后面改表结构就是一场返工。课堂上我们总结了一张常用字段对照表用途推荐类型理由主键、表IDINT UNSIGNED / BIGINT UNSIGNED无符号能多存一倍正数BIGINT给大表留余地性别、状态TINYINT0/1/2够用别用VARCHAR存这类枚举价格、金额DECIMAL(10,2)别用FLOAT/DOUBLE二进制浮点算钱会出精度问题生日、日期DATE只用日期不存时间创建/更新时间TIMESTAMP 或 DATETIMETIMESTAMP自带时区转换但范围到2038年DATETIME范围更大学号、手机号VARCHAR虽然有长度限制但灵活不会出现前导零被吞的问题固定长度编码CHAR身份证这类定长数据可以省点存储上课时有人问id为什么不用BIGINT老师说INT UNSIGNED上限21亿多对99%的业务都够用BIGINT白白多吃4字节。等真到21亿了通常也不是加INT能解决的而是该分库分表了。另一处细节是gender用了DEFAULT 0这就是热搜里“mysql设置默认值为0”的出处。设置默认值不是为了省事是为了避免插入数据时漏填字段导致NULL混进业务逻辑。NULL是个很麻烦的东西WHERE里要专门写IS NULL才能查聚合函数也会忽略它。能用NOT NULL DEFAULT写死的就别放NULL进去。1.3 主键、外键与默认值建表时的“家规”主键和唯一键就是表的基本家规。id设成AUTO_INCREMENT自增主键学号设成UNIQUE KEY保证同一个学号不能出现两次。这两条约束在数据库层面挡住脏数据比在应用层写if判断可靠得多。建完学生表和课程表第三张是选课表。老师要求必须加外键理由是“数据库要管得住数据之间的关系”CREATE TABLE student_course ( id INT UNSIGNED NOT NULL AUTO_INCREMENT, student_id INT UNSIGNED NOT NULL, course_id INT UNSIGNED NOT NULL, score DECIMAL(5,2) DEFAULT NULL COMMENT 成绩, PRIMARY KEY (id), UNIQUE KEY uk_student_course (student_id, course_id), CONSTRAINT fk_sc_student FOREIGN KEY (student_id) REFERENCES student(id) ON DELETE CASCADE, CONSTRAINT fk_sc_course FOREIGN KEY (course_id) REFERENCES course(id) ON DELETE CASCADE ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT选课表;外键的意义在于引用完整性如果没有外键删掉一个学生他的选课记录就成了无人认领的孤儿数据有了外键加ON DELETE CASCADE学生没了选课记录自动跟着删。不过老师也补了一句大实话生产环境很多团队会故意不用外键只用程序逻辑保证关联因为外键在高并发下会带来性能开销而且分库分表之后外键基本没法用。课堂学外键是为了理解数据库的约束思维到了真实项目里再决定用不用。2. 增删改查四板斧课堂上反复敲的那几条语句2.1 INSERT 与 UPDATE写数据时的三处细节数据库建好之后桌面上就该有数据了。INSERT语句第一节课就敲了无数遍但三个细节是反复翻车才记住的。第一多行插入别一条一条写。用一条语句插多行效率高很多INSERT INTO student (student_no, name, gender, birth_date, phone) VALUES (2026001, 王小明, 1, 2008-05-12, 13800001111), (2026002, 李思思, 2, 2008-08-20, 13800002222), (2026003, 赵磊, 1, 2008-01-30, 13800003333);第二插入中文之前先确认连接字符集。命令行里忘了执行SET NAMES utf8mb4;INSERT进去的中文在SELECT时全变乱码。这个坑和建库时的字符集坑是同一根藤上结出的瓜库是utf8mb4连接不是照样白搭。第三UPDATE不带WHERE是无数新手的第一场灾难。课上有人想更新某个学生的手机号SQL写成UPDATE student SET phone 13900000000;少了WHERE条件的后果是全表所有人的手机号全部变成13900000000。那一刻教室里响起一片“卧槽”。老师很平静地说这就是为什么不让你在生产环境直接连数据库改数据。UPDATE和DELETE语句写WHERE之前先别按回车把WHERE先写完再回去补UPDATE或DELETE的部分。2.2 SELECT 查询排序、过滤与分页的组合拳SELECT是整节课练得最多的语句毕竟建库建表最终都是为了把数据查出来。排序这个动作看着简单实际上很容易出岔子。普通写法是SELECT name, score FROM student_course WHERE course_id 1 ORDER BY score DESC, id ASC;这里有个我们全班都栽过的细节ORDER BY里有多个字段时先按score倒序排score相同再按id正序排。而如果成绩字段里有NULL默认排序时NULL会被当作最小值升序排在最前面。想让NULL排到末尾就得写ORDER BY score IS NULL, score ASC;这相当于先把“是不是NULL”当成一个排序键NULL的记录排到最后再按成绩升序。这类细节在豆包第一次给我答案时完全没提到是执行之后发现NULL记录位置不对才反应过来的。过滤条件也一样。WHERE里多个条件用AND连接字符串比较注意引号。日期范围用BETWEEN 2008-01-01 AND 2008-12-31需要注意BETWEEN是闭区间两端都包含。分页是另一个看起来简单、数据量一上去就出问题的点。常规写法SELECT id, name FROM student ORDER BY id LIMIT 20, 10;表示跳过20条取10条也就是第3页。LIMIT和OFFSET在数据量几万条时还算能看一旦到百万级OFFSET越大查询越慢因为MySQL要先把前N行全部扫出来再丢弃。生产环境的分页通常要改用“游标分页”——WHERE id 上一页最后一条的id再LIMIT这个我们后面课程才展开。2.3 DELETE 与 TRUNCATE删除的边界感删除数据这节课往后是重点因为操作不可逆。DELETE和TRUNCATE外表上都是清空表机制完全不同对比项DELETETRUNCATE作用范围按WHERE条件删行可精确控制整体清空全表事务可以回滚InnoDB下隐式提交无法回滚AUTO_INCREMENT不会重置重置回1逐行删除是产生大量日志直接释放表空间速度快课堂演示时我们建了一张临时表先DELETE再TRUNCATE然后看自增IDDELETE之后再插入记录ID从原来的最大值继续走TRUNCATE之后再插入ID从1重新开始。这个差异很实用——想保留ID连续增长要用TRUNCATE但前提是你确认整个表都不要了。还有一个和关联表有关的删除细节因为student_course的外键带了ON DELETE CASCADE删除学生表里的某条记录选课表里的对应成绩记录会自动消失。课上演示了删除一个叫“王小明”的学生然后SELECT选课表发现他的选课记录确实没了。这个机制用起来很爽但生产环境要格外小心——一个DELETE下去被级联删除的数据可能比你想象的多得多。3. 翻车现场记录连接报错与版本差异的真实教训3.1 SSL连接错误一条让人头疼的报错信息上午第二节课前半段大家开始尝试用客户端工具连数据库结果连着翻车。“MySQL SSL连接错误”这个报错占据了大半个屏幕报错长这样Loading class com.mysql.jdbc.Driver. This is deprecated. Establishing SSL connection without servers identity verification is not recommended. ... The server has rejected the connection: SSL connection error原因倒是不复杂MySQL 8.0默认开启SSL服务器会生成自签名的证书客户端连上去之后要处理SSL握手而本地开发环境哪有什么正式证书于是各种协议版本不匹配、证书验证失败就冒出来了。课堂上的解决方法是给连接串加两个参数跳过本地环境的SSL验证jdbc:mysql://localhost:3306/school_db?useSSLfalseallowPublicKeyRetrievaltrueuseSSLfalse表示不使用SSLallowPublicKeyRetrievaltrue允许客户端从服务器端获取公钥这两个参数组合是MySQL 8.0本地联调最常见的解法。Python的pymysql一般不报这个错但真要连也是同理能配SSL就配本地快速联调就用skip-ssl。必须说清楚本地学习跳过SSL完全没问题生产环境千万别这么做。生产库的连接务必使用SSL/TLS加密不然数据在网络上裸奔谁都能抓包看到你的SQL内容。3.2 5.7 与 8.0 的隐形差异密码插件和密码策略课上有同学电脑装的是MySQL 5.7有的是8.0于是出现了同一个SQL语句在一台机器上执行成功、在另一台上报错的情况。最典型的版本差异是密码认证插件。MySQL 5.7默认的认证插件是mysql_native_passwordMySQL 8.0默认换成caching_sha2_password。老客户端、老驱动连接8.0时经常会报Authentication plugin caching_sha2_password cannot be loaded这是版本问而不是写法问题。两种处理思路升级客户端驱动或者在MySQL里把用户的认证方式改回去ALTER USER rootlocalhost IDENTIFIED WITH mysql_native_password BY 你的密码;另一个差异是密码策略。8.0默认装了validate_password插件密码太简单直接不给过。第一次设密码设成123456被拒提示“Failed to check password”要同时包含大小写、数字和符号。我在旁边记了一句8.0对安全的要求比5.7高出一截这既是好事也是新手初期最容易卡住的配置项。还有排序规则的默认值。8.0默认utf8mb4_0900_ai_ci5.7默认是utf8mb4_general_ci。如果要把5.7的库迁移到8.0排序规则不一致会影响字符串比较和索引导出导入时容易报“Unknown collation”之类的错。我整理了一张课堂对照表维度MySQL 5.7MySQL 8.0默认认证插件mysql_native_passwordcaching_sha2_password默认字符集常需显式指定utf8mb4默认utf8mb4默认排序规则utf8mb4_general_ciutf8mb4_0900_ai_ciGROUP BY严格模式5.7.5后默认开启默认开启查询缓存有移除3.3 课堂上的小坑清单分号、保留字、乱码与严格模式这节后半段大家开始自由练习各种小坑集中爆发我当场记了一份“坑清单”现象原因解决命令行敲完SQL没反应出现-等待输入忘了写分号补上分号或者输入\g回车建表报“order”关键词错误表名或字段名用了保留字用反引号包起来order插入中文后SELECT显示乱码连接字符集和库字符集不一致执行SET NAMES utf8mb4;GROUP BY查询报错涉及ONLY_FULL_GROUP_BYMySQL 5.7默认严格模式SELECT的列必须出现在GROUP BY里或聚合函数中把非聚合列包进MAX/MIN等函数或加入GROUP BY插入超长字符串报错而非自动截断严格模式把“静默截断”变成了“直接报错”设计字段时长度留足别靠MySQL兜底其中ONLY_FULL_GROUP_BY这个报错是“AI幻觉”最容易引爆的一个点后面讲豆包会具体展开。4. 豆包的正确打开方式助教、翻译官和审稿人4.1 豆包最擅长的三件事第二节课的主题是“合理运用豆包”老师一上来先问大家你们平时都用豆包干嘛有人回答写周报、有人回答做菜谱、有人回答生成PPT大纲。老师说用在MySQL学习上豆包最值得干的是三件事。第一件是概念翻译。数据库里有大量抽象名词——事务、索引、连接池、隔离级别。这些概念看官方文档很容易绕进去但让豆包用大白话解释一遍就顺多了。我问过它“索引为什么能加快查询”它打了个比方说不建索引查数据像在一本没有目录的书里逐页翻建了索引等于先看目录直接翻到对应页码。这个比喻不精确但作为第一遍理解的抓手非常管用。第二件是报错解读。MySQL的英文报错对新手很不友好贴进豆包让它解释是哪一步出了问题它能把报错拆成人话甚至顺手给你一段修正后的语句。注意这里说的是“解读”不是“照抄”。第三件是代码审查。把自己写好的SQL丢给豆包让它找写得不好的地方——包括性能隐患和语法风险。它确实能找到不少问题比如WHERE条件里用了函数导致索引失效或者子查询写得冗余。但审查完必须自己跑一遍别直接采纳。4.2 豆包也会一本正经地胡说八道课上有一次实战翻车把“AI工具”四个字的教育意义拉满了。有人让豆包写一条查询“统计每门课程的最高分并显示课程名称”。豆包给了一版SELECT course_id, MAX(score), course.name FROM student_course JOIN course ON student_course.course_id course.id GROUP BY course_id;这条SQL在MySQL 8.0里直接报了ONLY_FULL_GROUP_BY错误——course.name既不在GROUP BY子句里也没包进聚合函数严格模式下不允许select出来。豆包给的答案漏了这一步引导也没提示要调整GROUP BY。类似的翻车还有一次豆包在MySQL环境里给出了SQL Server才有的GETDATE()函数执行直接报错。这说明什么问题豆包的知识来自海量历史语料里面既有MySQL 5.7的旧习惯也有其他数据库的语法甚至还有网上错误的示例。它生成答案时是“按概率组合”不是“按数据库引擎验证”。它看起来自信满满其实并不知道自己有没有错。所以AI给代码必须加验证环节。4.3 验证AI答案的三步法经过这两次翻车老师让我们把“验证”变成肌肉记忆。我总结的三步法第一步在测试库里跑一遍。单独建一个test库想怎么折腾都行把豆包的答案原样执行看报错还是出结果。第二步看执行计划。用EXPLAIN加在SQL前面查看是否走了索引EXPLAIN SELECT course_id, MAX(score) FROM student_course GROUP BY course_id;多花十秒钟看有没有“type: ALL”全表扫描能省掉后续无数的性能坑。第三步与官方文档对照。豆包适合给方向但最终定论要看dev.mysql.com/doc里的语法说明。你不需要全读只查自己关心的那段就够。课堂上多数人嫌查文档麻烦但老师说得对文档是裁判AI只是陪练。5. 把豆包做成个人工具从问答到自动化学习5.1 豆包API请求格式里的门道input 而不是 messages课上有同学问能不能把豆包接到自己的小工具里老师顺势讲了API调用。这里出现了一个很有意思的分叉网上搜到的豆包API示例请求体有的用input字段有的用messages数组把人搞懵了。原因很简单不同接入方提供的接口风格不一样。有些封装是把整段文本作为一个字段传进去字段名叫input另一些则采用OpenAI风格的“多轮消息数组”每轮消息用role来区分user和assistant字段名叫messages。老师建议我们统一走messages风格因为生态最成熟、理解成本最低。一个最小调用大概是这样的import requests url 你的服务端地址/chat/completions headers { Authorization: Bearer 你的API_KEY, Content-Type: application/json } payload { model: 你的模型ID, messages: [ {role: system, content: 你是MySQL DBA请只回答MySQL 8.0相关问题}, {role: user, content: 请解释NULL在ORDER BY排序时的默认位置} ] } resp requests.post(url, jsonpayload, headersheaders, timeout30) print(resp.json()[choices][0][message][content])这段代码最大的意义不是让你立刻做出产品而是让人明白AI能力是可以被程序调用的学MySQL的同时对自己常用工具做一次“技术解剖”比单纯点网页版聊天更能理解它。5.2 用豆包建一个SQL错题本光会调用API还不够第二节课的高潮是用豆包的知识库能力搭“错题本”。思路是这样的把课堂翻车的所有SQL、报错、解决方案整理成Markdown文件# 错题DELETE 没写 WHERE 错误SQL: DELETE FROM student; 报错: 无报错但全表数据清空 原因: 缺少WHERE条件误删全表 解决: DELETE 之前先写 WHERE确认后再补 DELETE 部分然后把这份Markdown作为知识库文件上传到豆包的知识库/智能体配置里。这样以后问它“我上次DELETE踩了什么坑”它能基于你的错题本内容回答而不是泛泛而谈。豆包的“知识库”本质上是给模型外挂了一份属于你自己的资料这个功能用来沉淀学习笔记非常顺手。这个做法让我意识到一件事豆包不是只能做即时问答它更像一个检索增强的私人助教。你喂给它的资料质量直接决定它回答你的质量。把笔记整理清楚再投喂等于逼着自己梳理知识结构。5.3 那些“指令类”玩法的迁移清理命令要小心课间聊天时有人提到用豆包生成“一键清理电脑C盘”的系统指令还有什么“优化电脑”的CMD命令听起来挺酷。老师顺着这个话题提醒了一句同样的玩法迁移到数据库上风险就完全不一样了。比如我拿豆包生成“清理MySQL binlog日志”的命令它确实能列出PURGE BINARY LOGS BEFORE ...这类语句。但同样一句话用错时间点、用错库就是生产事故。AI生成运维指令的底线是每个命令都要搞明白副作用再执行。让学生去理解“AI不是权威”这件事比背下来任何一条清理命令都有价值。6. 课后扩展事务与多语言连接下一步学什么6.1 为什么事务是数据库最值钱的能力上午两节课刚撞到事务的门槛老师就用了一个经典场景解释从A账户转100块到B账户两步操作——扣A的钱、加B的钱。如果第二步失败第一步必须能撤回来否则钱就凭空消失了。这就是事务的原子性。MySQL里InnoDB引擎支持事务一条转账操作包在事务里START TRANSACTION; UPDATE account SET balance balance - 100 WHERE id 1; UPDATE account SET balance balance 100 WHERE id 2; COMMIT;如果中间某一步出错执行ROLLBACK两个表的改动全部回滚。ACID这四个字母——原子性、一致性、隔离性、持久性——下一节课才展开但光是“能回滚”这个能力就足以解释为什么银行系统绝不用MyISAM。6.2 让MySQL和更多语言对话以Python为例课后作业是用Python连一次MySQL写一个简单的查询脚本。Python这边最常用的是pymysqlimport pymysql conn pymysql.connect( host127.0.0.1, port3306, userroot, password你的密码, databaseschool_db, charsetutf8mb4 ) try: with conn.cursor() as cursor: cursor.execute(SELECT name, score FROM student_course LIMIT 5) for row in cursor.fetchall(): print(row) finally: conn.close()注意charset参数写utf8mb4不再是小写的utf8——这是从第一节的字符集坑里得来的教训。同样的逻辑Java用JDBC也就是换个连接串C用MySQL Connector/C封装得更底层一点。掌握了SQL本身语言只是外壳。课到这里就散了但真正的作业才刚刚开始把今天建好的库、踩过的坑、豆包给过的错误答案全部整理成一篇文档作为下周开始学事务与隔离级别的入场券。我这篇笔记就是照着这个要求写的。写的过程中最深的体会是豆包能替我省下大量查资料的时间但判断力省不掉。SQL跑不跑得通、报错怎么解、字段类型选什么最终还得靠自己在键盘上敲一遍才知道。如果你也在用AI辅助学MySQL记住一句话让豆包当陪练别让它当枪手。