🦏 第8章 表的操作
📄 一句话概括

drop table 表名, 表和数据一起消失,索引、触发器也一起没。 没有撤销,也没有「回收站」。 if exists 让「删不存在的表」不报错,和建表时的 if not exists 正好配成一对。 有外键指着就删不掉(students 指着 classes,报 FOREIGN KEY constraint failed)。 更稳的做法是改名加日期、观察一周再删。

📝 本章和前面几章的关系

第 8.1 篇说「先改名、后删除」,这一篇就是它的落地。 删表是这一章唯一一个「完全不可逆」的动作——改表还能重建,删表连表都没了。 所以这一篇的一半篇幅在讲「怎么不删」。

一、先列一遍表

删之前先 show tables 看一眼,是这一步的唯一保险。 Demo 里先列六张表,建一张 demo_tmp, 确认它真的多出来了。 名字打错一位,删掉的就是另一张。 这种事故听起来很低级,但真实发生过很多次—— 尤其是 orders 和 order_detail、user 和 users 这种只差一两个字母的表名。

二、drop table

drop table demo_tmp,完成。 再查一次系统表,still_there 是 0——已经没了。 表和数据一起消失,索引、触发器也一起没。 没有撤销,也没有「回收站」。 备份是唯一的后悔药。 它和 delete from 表 差一个层级: delete 删行、表还在;drop 把整个容器端走。 两个词只差两个字母,后果差一个量级。

三、if exists:删不存在的表不报错

同一句 drop table demo_tmp 再跑一遍,报 no such table: demo_tmp。 加 if exists 就不报: 「存在才删」,不存在就静默跳过。 和建表时的 if not exists 正好配成一对。 初始化/清理脚本里几乎都要带它, 否则脚本跑第二遍就会在「表不存在」这一步中断,后面的语句全不执行。 一个常被忽略的细节:if exists 只避免「不存在」这一种报错, 权限不够、被外键拦住,它一样报——它不是万能的兜底。

四、有外键指着,删不掉

DROP TABLE classes 报 FOREIGN KEY constraint failed。 因为 students 表里还有学生指着它。 MySQL 那边报 Cannot delete or update a parent row,理由一样。 这就是外键的价值:它不让你留下「指向不存在的班」的学生。 正确顺序是先删子表、再删父表: 先 drop table students、再 drop table classes。 但删之前要想清楚:那两个表的数据都不要了吗? 顺序对了不等于决定对了。

五、更稳的做法:改名 + 观察

ALTER TABLE classes RENAME TO classes_old_20261030,表还在,只是改了个带日期的名字。 业务立刻就不能用了——效果和删掉差不多, 但数据还在,随时能改回来。 观察一周没人喊,再 DROP。 这个习惯的价值在于:删表是不可逆的,改名是可逆的。 能先用可逆的方式验证,就别一上来用不可逆的。 这条原则不只适用于删表——改类型、改字符集、 换索引,都能先做一次「可逆版本」。

六、动手 Demo:删一张临时表,然后撞三次删不掉的情况

Demo 六段:先列一遍表、建一张临时表把它删掉,再撞「表不存在」「外键拦住」,最后用改名代替删除。 第三段和第四段都是「删不掉」——它们说明在真实库里,删除从来不是一个自由动作。

import _q46 as Q
c = Q.con()

# 删之前先列一遍
Q.show(c, "SELECT name FROM sqlite_master WHERE type = 'table' ORDER BY name")

Q.run(c, "CREATE TABLE demo_tmp (id INTEGER PRIMARY KEY)")
Q.run(c, "DROP TABLE demo_tmp")

# 表不存在时删会报错,IF EXISTS 不报
c.execute("DROP TABLE demo_tmp")
Q.run(c, "DROP TABLE IF EXISTS demo_tmp")

# 有外键指着,删不掉
c.execute("DROP TABLE classes")

# 更稳的做法:改名、观察、再删
Q.run(c, "ALTER TABLE classes RENAME TO classes_old_20261030")

实跑输出:

--------------------------------------------------------------------
第四十一课:删表——最该慢一步的动作
--------------------------------------------------------------------

  先看清楚要删的是哪张
  SQL> SELECT name FROM sqlite_master WHERE type = 'table' ORDER BY name
  name
  -------------
  classes
  goods
  order_detail
  orders
  scores
  students
  (6 行)

  SQL> CREATE TABLE demo_tmp (id INTEGER PRIMARY KEY)
  完成

  **多了一张 `demo_tmp`。** **删之前先列一遍表,是这一步的唯一保险**——
  **名字打错一位,删掉的就是另一张。**

  删掉它
  SQL> DROP TABLE demo_tmp
  完成

  SQL> SELECT COUNT(*) AS still_there FROM sqlite_master
       WHERE type = 'table' AND name = 'demo_tmp'
  still_there
  ------------
  0
  (1 行)

  **`drop table 表名`**,给 0 表示已经没了。
  **表和数据一起消失,索引、触发器也一起没。** **没有撤销,也没有「回收站」。**

  IF EXISTS:删不存在的表不报错
  SQL> DROP TABLE demo_tmp
  报错:no such table: demo_tmp

  SQL> DROP TABLE IF EXISTS demo_tmp
  完成

  **第二句不报错。** **`IF EXISTS` 的意思是「存在才删」**,
  **和建表时的 `IF NOT EXISTS` 正好配成一对。**
  **初始化/清理脚本里几乎都要带它**,**否则脚本跑第二遍就会在「表不存在」这一步中断。**

  有外键指着,删不掉
  SQL> DROP TABLE classes
  报错:FOREIGN KEY constraint failed

  **`students` 还指着 `classes`,所以删不掉。**
  **MySQL 那边报 `Cannot delete or update a parent row`,理由一样。**
  **正确顺序是先删子表、再删父表**:
  **先 `drop table students`、再 `drop table classes`。**
  **但删之前要想清楚:那两个表的数据都不要了吗?**

  更稳的做法:改名 + 观察
  SQL> ALTER TABLE classes RENAME TO classes_old_20261030
  完成

  SQL> SELECT name FROM sqlite_master WHERE type = 'table' ORDER BY name
  name
  ---------------------
  classes_old_20261030
  goods
  order_detail
  orders
  scores
  students
  (6 行)

  **表还在,只是改了个带日期的名字。** **业务立刻就不能用了——效果和删掉差不多**,
  **但数据还在,随时能改回来。**
  **观察一周没人喊,再 `DROP`。** **这个习惯的价值在于:删表是不可逆的,改名是可逆的。**
  **能先用可逆的方式验证,就别一上来用不可逆的。**

第一段列六张表,建了 demo_tmp 之后变七张——这一步就是「确认要删的是哪张」。 第二段删掉它,查 sqlite_master 里 still_there 是 0。 第三段再删一次,报 no such table;if exists 版本不报。 第四段撞外键,classes 删不掉——students 还指着它。 第五段改名,classes_old_20261030 出现在表清单里,表还在、数据还在。

30 秒实验

  1. DROP TABLE students 能成功吗?为什么和 DROP TABLE classes 不一样?
  2. DROP TABLE IF EXISTS t 在 t 不存在时会报错吗?如果权限不够呢?
  3. 要给一张还在用的表「留后路」,比 drop 更好的第一步是什么?

常见误区

  1. 删表前不列一遍表名。 名字打错一位删掉的是另一张,而且没有回收站。
  2. 以为 if exists 能兜住所有报错。 它只管「表不存在」,权限不够、外键拦住照样报。
  3. 直接 drop 父表。 有外键指着根本删不掉, 正确顺序是先删子表——但顺序对了要先想清楚数据要不要。
自测三问(点开对答案) 1. `drop table` 和 `delete from 表` 差在哪? —— `delete` 删行,表还在;`drop` 把表连同数据、索引、触发器一起删掉。 2. 为什么 `DROP TABLE classes` 会失败?怎么才能删掉? —— `students` 表有外键指向它。要先删 `students` 或先清空它的引用,再删 `classes`。 3. 为什么「改名 + 观察一周」比直接删更稳? —— 改名是可逆的,删表不可逆。业务出问题时一个 `RENAME` 就能改回来,删了只能靠备份。

第 8 章到这儿完了:建、看、改、复制、删,五件事都会了。 下一篇进第 9 章——列的属性,把这章只用了一下的那五个约束讲透。

相关笔记