🛢️ 第3章 使用MySQL图形化管理工具
📄 一句话概括

建库只需要一条语句,但「建之前想清楚字符集」这件事,事后改代价很大。 改库只改得动默认值,改不动已有的表。 删库没有回收站,也没有确认三次。

📝 本章和前面几章的关系

上一篇把工具装好了,这一篇开始建东西——从最外层的库开始。 它是第 4 章的地基:第 4 章会把这一篇的建库、查看、选择、修改、删除逐条展开讲语法, 这一篇讲的是「在图形工具里怎么点」。 记住一个顺序:库、表、字段、数据。 这一篇在第二层,下一篇再往下一层。

一、建库:三个写法,一个意思

CREATE DATABASE 库名; 和 CREATE SCHEMA 库名; 完全等价。 SQL 标准里这个词叫 schema,MySQL 两个都认。 第三个写法最实用:CREATE DATABASE IF NOT EXISTS 库名;。 库已经存在时不报错,只是什么都不做。 脚本要跑第二遍的时候,这一句能救命。

写法 库已存在时 什么时候用
CREATE DATABASE 库名 报错 1007 一次性手工建
CREATE SCHEMA 库名 报错 1007 同上,等价写法
CREATE DATABASE IF NOT EXISTS 库名 静默跳过 初始化脚本里必须这么写

二、建库时最该定的两件事

建库时可以什么都不写,让它用默认字符集。 但生产上建议显式写出来,因为默认值会随版本变。

要定的 写什么 不定的后果
字符集 CHARACTER SET utf8mb4 5.7 默认是三字节的 utf8,emoji 存不进
校对规则 COLLATE utf8mb4_0900_ai_ci 排序和大小写比较按默认走,可能不合业务

一句话:新建库一律 utf8mb4。 8.0 起默认就是它,但 5.7 建的库升上来还是旧的三字节 utf8,得手动改。

三、改库:只改得动「以后」

ALTER DATABASE 库名 CHARACTER SET utf8mb4; 改的是「这个库以后新建的表用什么字符集」。 已经存在的表,一动不动。 这一点是本章最容易误解的地方,Demo 里有实例: 改完库的字符集,老表的字符集还是老的,得一张张 ALTERTABLE ... CONVERT TO。 为什么不自动改? 因为改字符集要重写每一行数据,几百 G 的表能跑一整晚,还全程锁表。 数据库不敢替你决定这件事。

四、删库:一条语句,一个终点

DROP DATABASE 库名; 执行完,库里的表、数据、索引、视图,全没。 没有回收站,没有「确认三次」,没有撤销。

你想的 实际的
删错了能恢复 只能靠之前的备份
先删表再删库比较安全 一样,都不可逆
工具会拦一下 工具只弹一次确认框,你顺手就点了

所以真正的保护在流程里:删之前先导出一份。 3.5 节的导出功能,最大的用处不是搬家,是「删之前留个底」。

五、动手 Demo:建库、改库、删库,在图形工具里点三下

教材原码先贴上,再用 SQLite 演等价的动作。 差别在于:MySQL 的库是服务端里的一个命名空间,SQLite 的库就是一个文件。 五段:原码、建三个库、一个连接挂多个库、删库、以及工具在这一步帮了什么。

import sqlite3, os

# 「建库」在 SQLite 里就是建一个文件
for name in ("db_shop", "db_blog", "db_test"):
    p = os.path.join(WORK, name + ".db")
    c = sqlite3.connect(p)
    c.execute("CREATE TABLE t (id INTEGER PRIMARY KEY, v TEXT)")
    c.execute("INSERT INTO t VALUES (1, ?)", (name,))
    c.commit(); c.close()
    print(name, os.path.getsize(p), "字节")

# 「一个连接挂多个库」在 SQLite 里是 ATTACH
conn = sqlite3.connect(os.path.join(WORK, "main.db"))
conn.execute("ATTACH DATABASE ? AS db_shop",
             (os.path.join(WORK, "db_shop.db"),))
print([r[1] for r in conn.execute("PRAGMA database_list")])
print(conn.execute("SELECT * FROM db_shop.t").fetchall())

# 「删库」就是删文件
os.remove(os.path.join(WORK, "db_test.db"))
print("删完还在吗:", os.path.exists(os.path.join(WORK, "db_test.db")))

实跑输出:

--------------------------------------------------------------------
第十课:建库、改库、删库,在图形工具里点三下
--------------------------------------------------------------------

  **教材 3.2 节讲的是在 phpMyAdmin 里建库、改库、删库。**
  **点的那三下,背后是三条 SQL。**
  **沙箱里没有 MySQL,所以先贴教材原码,再用 SQLite 演等价的动作。**

  一、教材原码:四条语句
  --------------------------------------------------------------------

  [教材原码 · 本沙箱不执行]
  | CREATE DATABASE db_database28;
  | CREATE SCHEMA db_database28;                 -- 和 CREATE DATABASE 等价
  | CREATE DATABASE IF NOT EXISTS db_database28;  -- 不存在才建,不报错
  | ALTER DATABASE db_database28 CHARACTER SET utf8mb4;
  | DROP DATABASE db_database28;
    (建库、建库的另一种写法、带判断的建库、改库字符集、删库)


  二、SQLite 里「一个库」就是一个文件
  --------------------------------------------------------------------
  **MySQL 的库是服务端里的一个命名空间,SQLite 的库就是一个 `.db` 文件。**
  **这个差别决定了后面所有操作长得不一样。**

  建出来的库  落地成一个文件  文件字节数
  --------------------------------------
  db_shop     db_shop.db      8192
  db_blog     db_blog.db      8192
  db_test     db_test.db      8192

  三、一个连接里挂多个库:ATTACH
  --------------------------------------------------------------------
  ATTACH DATABASE 'db_shop.db' AS db_shop
  ATTACH DATABASE 'db_blog.db' AS db_blog
  **一个连接上现在挂着 3 个库:main、db_shop、db_blog。**
  SELECT *
  FROM db_shop.t

  mysql> SELECT * FROM db_shop.t
  id v
  ----------
  1  db_shop
  1 row in set
  SELECT *
  FROM db_blog.t

  mysql> SELECT * FROM db_blog.t
  id v
  ----------
  1  db_blog
  1 row in set

  四、删库:一条命令,一个文件
  --------------------------------------------------------------------

  [教材原码 · 本沙箱不执行]
  | DROP DATABASE db_test;
    (MySQL 里删完,库里的表跟着全没)

  **删之前文件在不在:True;删之后:False。**
  **注意:没有「回收站」,也没有「确认三次」。**
  **生产环境上 `DROP DATABASE` 敲错一个字母,就是一次事故。**

  五、图形工具在这一步帮了什么
  --------------------------------------------------------------------

  你担心的      工具能做              工具做不到
  ----------------------------------------------------
  库名打错      下拉框选,不给你手打  选错行还是删错
  删之前忘了看  弹一个「确认删除」    你顺手点了「是」
  删完想找回来  没有这个功能          只能靠备份
  **所以真正的保护不在工具里,在流程里:删之前先导出一份。**

第一段的三个库,每个都是一份 8192 字节的文件。 这个数不是巧合,它是 SQLite 的页大小×页数,空库一建出来就这么大。 第二段那个 ATTACH 是 SQLite 特有的动作:一个连接可以同时挂多个库文件。 MySQL 不用这一手——它天生就是一个服务端管很多库,USE 库名 就是切换。 第三段一行代码就删了库。 这就是这一节要你记住的:删库在任何数据库上都是「一条语句的事」。 代价全在事后。 最后那张表列了工具能帮和帮不了的。 最扎心的是第三行:它只能弹出一次确认框,而人是会顺手点「是」的。

30 秒实验

  1. 把 CREATE DATABASE 改成 CREATE DATABASE IF NOT EXISTS,连跑两遍。 第一遍建,第二遍静默跳过。 这就是初始化脚本里必须这么写的原因。
  2. 在 Demo 里再 ATTACH 一个库, 然后用 SELECT * FROM db_blog.t UNION ALL SELECT * FROM db_shop.t 把两个库的数据拼起来。 能拼,但生产上没人这么干——跨库查询一多,就该考虑合库或者分片了。
  3. 想一想:为什么数据库不做一个「删库回收站」? 因为几百 G 的数据放进回收站,等于占着两份空间,而真出事的概率很低。 成本恒定、收益偶发,工程上就不做。

常见误区

  1. 「改了库的字符集,整库就都变成 utf8mb4 了。」 不会。它只改「以后新建的表」的默认值。 老表得一张张 CONVERT TO CHARACTER SET,而且会重写数据、锁表。
  2. 「库名随便起,反正可以改。」 库名能改,但要 RENAME,而且改完所有连接串、脚本、备份计划都得跟着改。 起名的时候就按规范来,别指望事后改。
  3. 「删库之前先删表,多一道保险。」 没有这道保险。 DROP TABLE 和 DROP DATABASE 一样不可逆。 真正的保险是备份,而且是验证过能恢复的备份。
自测三问(点开对答案) 1. `CREATE DATABASE` 和 `CREATE SCHEMA` 有区别吗? —— **没有,完全等价。** **SQL 标准里叫 schema,MySQL 两个词都认。** 2. `ALTER DATABASE ... CHARACTER SET` 改的是什么? —— **改的是「以后新建的表」的默认字符集。** **已有表不动,要改得一张张 `CONVERT TO`。** 3. 删库之前最该做的一件事是什么? —— **导出一份,并且确认这份能导回去。** **没验证过的备份等于没有备份。**

下一篇往下一层:库里的表怎么建、怎么改、怎么删。 重点是一条 ALTER 能干什么、TRUNCATE 和 DROP 差在哪。

相关笔记