教材给的三条方法——多练、看文档、多总结——都对,但太笼统。 这一篇把它们落到具体动作:造错、看计划、写小项目。 最实用的一条是「学会读报错」:错误码的号段本身就告诉你该查哪一层。
这是第 2 章最后一节,教材在这一节收尾,讲方法。 它是后面 22 章的用法说明书。 第 4 章到第 21 章全是语法和操作,学得快不快,取决于这一篇说的三个动作做没做。 和第 1 章的关系:第 1 章说「为什么要有数据库」,这一篇说「怎么把它学进手」。
一、把笼统的方法拆成动作
教材讲方法,讲了三条:多动手、多看官方文档、多总结。 这三条都对,问题是听完还是不知道明天该干什么。
| 阶段 | 该干什么 | 别干什么 |
|---|---|---|
| 第一周 | 把安装、建库建表、增删改查跑通 | 别急着看索引原理 |
| 第二周 | 把第 9 章的 join 写法全部手敲一遍 | 别复制粘贴 |
| 第三周 | 给自己造二十个错,逐个读报错 | 别一看到错就搜 |
「手敲」不是仪式感。 复制粘贴跳过的是「语法在你脑子里过一遍」这一步,而这一步才是学习发生的时刻。 第三周那条最反直觉,也最有用。 别人踩过的坑你未必踩得到,自己造出来的错一定记得住。
二、把报错当老师
报错信息里其实全给了:错误码、位置、对象名。 只要会看号段,八成的报错当场能定位,根本不用搜。
| 号段 | 谁报的 | 先查什么 |
|---|---|---|
| 2xxx | 客户端,还没连上服务端 | 服务起没起、端口通不通、账号对不对 |
| 1xxx | 服务端,SQL、数据、权限、锁 | 报错里点名的表名、列名、关键字 |
| 12xx | 服务端,锁与事务 | 谁在持锁、事务是不是太长 |
ERRS = [
"ERROR 2003 (HY000): Can't connect to MySQL server on '127.0.0.1' (10061)",
"ERROR 1064 (42000): You have an error in your SQL syntax near 'SELEC'",
"ERROR 1146 (42S02): Table 'db.tb' doesn't exist",
"ERROR 1045 (28000): Access denied for user 'root'@'localhost'",
"ERROR 1213 (40001): Deadlock found when trying to get lock",
]
def triage(msg):
code = int(msg.split()[1])
if code >= 2000 and code < 3000:
return code, "连接层", "先查服务、端口、账号,别动 SQL"
if code in (1213, 1205):
return code, "锁与事务", "看谁持有锁,缩短事务,别加大超时"
if code in (1045, 1044):
return code, "权限", "查用户、主机、授权范围"
return code, "SQL 与对象", "先看报错里点名的表、列、关键字"
def wid(s):
return sum(2 if ord(ch) > 127 else 1 for ch in s)
def pad(s, w):
return s + " " * (w - wid(s))
print(pad("错误码", 8) + pad("类别", 12) + "第一条动作")
print("-" * 60)
for m in ERRS:
code, kind, act = triage(m)
print(pad("%d" % code, 8) + pad(kind, 12) + act)
这个分类器只有二十来行,但它把「先查什么」这件事定死了。
看到 2003 还去改 SQL,就是在错的地方使劲。
读报错的四步,按顺序来:
先看号段,再看对象名,然后看位置(at line、near),最后才去搜。
搜的时候带上错误码,别只搜「MySQL 报错」四个字。
三、三条练习路线
语法是背不完的,能背下来的那部分恰恰最不值钱——查手册就有。 值钱的是「知道什么时候该用什么」。
| 路线 | 具体做法 | 练到什么 |
|---|---|---|
| 造错 | 关键字拼错、列名拼错、主键插重 | 一眼看出报错指向哪 |
| 看计划 | 每条 SQL 前面加 EXPLAIN | 知道它走没走索引 |
| 写小项目 | 一个带登录、分页、统计的小站 | 把第 1 到 20 章串起来 |
三条里最值钱的是第三条。 一个真实的小项目会把「必须记住的那 20%」逼出来—— 分页要 LIMIT、统计要 GROUP BY、去重要 DISTINCT、慢要加索引。 第二条最容易被跳过,因为它没有即时反馈。 但「这条 SQL 走没走索引」是 MySQL 里最值钱的一个判断。 它决定了你的系统是十毫秒还是十秒。
四、动手 Demo:五个最常见的错误码,真造出来看
这一课把五个最常见的错真造出来,对照着看它的结构。 沙箱里没有 MySQL,报错文本是 SQLite 的;MySQL 的措辞不同,结构一样。 认识结构比背原文有用。
import sqlite3
c = sqlite3.connect(":memory:")
c.execute("CREATE TABLE t (id INTEGER PRIMARY KEY, name TEXT NOT NULL)")
c.execute("INSERT INTO t VALUES (1, 'a')")
CASES = [
("1064 语法错", "SELEC * FROM t"),
("1054 未知列", "SELECT nmae FROM t"),
("1146 表不存在", "SELECT * FROM tt"),
("1062 主键重复", "INSERT INTO t VALUES (1, 'b')"),
("1048 列不能为空", "INSERT INTO t (id) VALUES (2)"),
]
for name, sql in CASES:
try:
c.execute(sql)
except sqlite3.Error as e:
print("%-16s %s" % (name, e))
实跑输出:
--------------------------------------------------------------------
第八课:报错怎么读——五个最常见的错误码
--------------------------------------------------------------------
**教材最后一节叫「如何学好 MySQL」,讲的是方法。**
**方法里最实用的一条是:先学会读报错。**
**报错信息里其实全给了:错误码、位置、原因。**
**这一课把最常见的五个错真造出来,对照着看。**
一、五个错,逐个造
--------------------------------------------------------------------
**1064 语法错**
**SQL:`SELEC * FROM t`**
**沙箱报:`near "SELEC": syntax error`**
**常见原因:关键字拼错、少了引号、少了逗号。**
**1054 未知列**
**SQL:`SELECT nmae FROM t`**
**沙箱报:`no such column: nmae`**
**常见原因:列名拼错,或者表里真没这列。**
**1146 表不存在**
**SQL:`SELECT * FROM tt`**
**沙箱报:`no such table: tt`**
**常见原因:表名错、或者没选库。**
**1062 主键重复**
**SQL:`INSERT INTO t VALUES (1, 'b')`**
**沙箱报:`UNIQUE constraint failed: t.id`**
**常见原因:主键或唯一索引撞了已有值。**
**1048 列不能为空**
**SQL:`INSERT INTO t (id) VALUES (2)`**
**沙箱报:`NOT NULL constraint failed: t.name`**
**常见原因:该列是 NOT NULL 且没有默认值。**
**注意:上面是 SQLite 的报错文本,MySQL 的措辞不一样,但结构一样。**
**都带错误码、都指出位置或对象名。**
**MySQL 里对应长这样:**
[教材原码 · 本沙箱不执行]
| ERROR 1064 (42000): You have an error in your SQL syntax;
| check the manual ... near 'SELEC * FROM t' at line 1
| ERROR 1054 (42S22): Unknown column 'nmae' in 'field list'
| ERROR 1146 (42S02): Table 'db_database28.tt' doesn't exist
| ERROR 1062 (23000): Duplicate entry '1' for key 't.PRIMARY'
| ERROR 1048 (23000): Column 'name' cannot be null
二、错误码里藏着的规律
--------------------------------------------------------------------
号段 谁报的 本课的例子
------------------------------------------------------------------
1xxx 服务端:SQL、数据、权限、锁 1064、1054、1146、1062、1048
2xxx 客户端:还没摸到服务端就失败了 2003 连不上、2013 连接中断
12xx 服务端:锁与事务 1213 死锁、1205 锁等待超时
**第二行最好记:`2xxx` 一定是「没连上」,跟 SQL 一点关系都没有。**
**看到 2003 就别去改 SQL 了,先查服务起没起。**
三、读报错的四步
--------------------------------------------------------------------
**① 先看号段。**2xxx 查连接,1xxx 查 SQL 和数据。
**② 再看对象名。**报错里带出来的表名、列名、键名,先拿去核对拼写。
**③ 然后看位置。**`at line 1`、`near 'xxx'` 直接指到你写错的地方。
**④ 最后才去搜。**把错误码加报错原文一起搜,别只搜「MySQL 报错」四个字。
**这四步能解决日常八成的报错。剩下两成才是真需要查文档的。**
**学好 MySQL 的方法说到底就这一条:每次报错都读到底,别直接复制去搜。**
**读多了你会发现,报错其实是在教你它怎么想。**
五条报错里,前三条是「写错了」,后两条是「数据不合规矩」。
这两类的处理方式完全不同:前一类改 SQL,后一类改数据或者加默认值。
注意 1062 和 1048 的报错文本里都带了对象名——一个是键名 t.id,一个是列名 t.name。
报错点名的东西,就是你要去看的东西。
最后那张号段表比记五个错误码有用得多。
2 开头的错误码一定跟连接有关,跟 SQL 一个字都没关系。
知道这一条,能省掉大量「改 SQL 改到怀疑人生」的时间。
本节说的四步读法,顺序是有讲究的:先大势(号段),再具体(对象名),再看位置,最后才搜。
反过来的顺序——先搜——最容易搜到别人的环境、别人的版本,绕一大圈。
30 秒实验
- 把 CODE251 里的 1045 改成 1044,看分类会不会变。不变,两个都在「权限」这一类。 号段和清单这两套规则是怎么配合的,一眼就看出来了。
- 再给自己造一个错:给一个不存在的库建表。 会报 1049(未知数据库),属于 1xxx,归「SQL 与对象」那一类。
- 翻一条你以前搜过的报错,按四步读一遍。多数时候你会发现:号段和对象名已经把答案说完了。
常见误区
- 「报错看不懂就搜。」 先读号段和对象名,八成的错自己就能定位。 搜是最后一步,不是第一步。
- 「把报错整段贴进搜索框。」 带上错误码搜,命中的才是同一个问题。 整段贴进去,搜索引擎会去匹配那些无关的变量名。
- 「学 MySQL 就是背语法。」 语法查手册就有,值钱的是「知道什么时候该用什么」。 这个只能靠写小项目练出来。
自测三问(点开对答案)
1. 2003 和 1045 差在哪? —— **2003 是客户端根本没连上服务端,1045 是连上了但账号被拒。** **前者查服务、端口、网络;后者查用户名、密码、授权主机。** 2. 1062 属于哪一类错?该动什么? —— **属于「数据不合规矩」:主键或唯一索引撞了已有值。** **要么改插入的值,要么用 `ON DUPLICATE KEY UPDATE` 改成「有就更新」。** 3. 为什么「看计划」比「猜快慢」可靠? —— **因为 `EXPLAIN` 直接告诉你走没走索引、扫了多少行、要不要临时表。** **猜的依据通常只有「感觉」,而感觉在数据量变化后会完全失效。**第 2 章到这儿结束。下一篇进第 3 章,讲图形化管理工具—— phpMyAdmin 怎么装、怎么用它建库建表和管理数据。
- ☐ (本篇完,下一篇 3.1 安装 phpMyAdmin 图形化管理工具)