MySQL不看视频 · SSH 实操

第四单元 · 概念、类型、改表、分页 · 第 10 / 21 节

改表结构:加列、改类型、换名、删列

在 lesson10_alter 上把 ALTER TABLE 的四个动作各走一遍:ADD COLUMN ... AFTER 加列、MODIFY 改类型并补齐 NOT NULL/DEFAULT、CHANGE 换列名、DROP COLUMN 删列;再用 RENAME TABLE 改名、用一张临时表演示 DROP TABLE,每一步都用 DESC 和一条 SELECT 核对数据没丢。

1 个步骤 · 进度只存本机

约 22 分钟。表建好以后发现少一列、类型选小了、列名起错了,都要在不丢数据的前提下改。这一节把 ALTER TABLE 的四个动作和两种危险动作(改名、删表)各走一遍。

SSH / Linux 终端MySQL 终端输出示例(只读)复制按钮只复制命令,不含 $ / mysql> 提示符和输出。

已完成 0 / 1 步

你现在在哪个提示符

应该在 mysql>。断了就先回到学习终端(PowerShell):

SSH / Linux 终端bash
ssh root@8.148.204.152
SSH / Linux 终端bash
mysql-learning

进去以后确认当前库:

MySQL 终端sql
SELECT DATABASE();
输出示例(只读,不要粘贴执行)text不可复制
+------------+
| DATABASE() |
+------------+
| itcast     |
+------------+
1 row in set (0.00 sec)

显示 itcast 就对了。如果是 NULL,执行 USE itcast;。

第一步:一张表,四次改结构,数据一行不丢

第 2 节你学过建表,可现实里表建完总要改:多加一个手机号、年龄原来用 TINYINT 现在不够、phone 想改名成 mobile、试用的列最后不要了。这些全用 ALTER TABLE,它只改结构,不动行数据。

先建一张最小的表:

MySQL 终端sql
SHOW TABLES LIKE 'lesson10_alter';
输出示例(只读,不要粘贴执行)text不可复制
Empty set (0.00 sec)
MySQL 终端sql
CREATE TABLE lesson10_alter (
  id INT PRIMARY KEY COMMENT '工号',
  name VARCHAR(20) NOT NULL COMMENT '姓名',
  age TINYINT UNSIGNED COMMENT '年龄'
) COMMENT='本教程第四单元练习表:改表结构';
输出示例(只读,不要粘贴执行)text不可复制
Query OK, 0 rows affected (0.03 sec)
MySQL 终端sql
INSERT INTO lesson10_alter (id, name, age) VALUES
  (1, '张伟', 23),
  (2, '李娜', 21),
  (3, '王强', 25);
输出示例(只读,不要粘贴执行)text不可复制
Query OK, 3 rows affected (0.00 sec)
Records: 3  Duplicates: 0  Warnings: 0
MySQL 终端sql
DESC lesson10_alter;
输出示例(只读,不要粘贴执行)text不可复制
+-------+------------------+------+-----+---------+-------+
| Field | Type             | Null | Key | Default | Extra |
+-------+------------------+------+-----+---------+-------+
| id    | int              | NO   | PRI | NULL    |       |
| name  | varchar(20)      | NO   |     | NULL    |       |
| age   | tinyint unsigned | YES  |     | NULL    |       |
+-------+------------------+------+-----+---------+-------+
3 rows in set (0.00 sec)

三列:id、name、age。下面四种改法依次来,每改一次就看一眼 DESC。

加一列。想让手机号紧跟在姓名后面,用 ADD COLUMN 加 AFTER:

MySQL 终端sql
ALTER TABLE lesson10_alter ADD COLUMN phone VARCHAR(20) AFTER name;
输出示例(只读,不要粘贴执行)text不可复制
Query OK, 0 rows affected (0.02 sec)
Records: 0  Duplicates: 0  Warnings: 0
MySQL 终端sql
DESC lesson10_alter;
输出示例(只读,不要粘贴执行)text不可复制
+-------+------------------+------+-----+---------+-------+
| Field | Type             | Null | Key | Default | Extra |
+-------+------------------+------+-----+---------+-------+
| id    | int              | NO   | PRI | NULL    |       |
| name  | varchar(20)      | NO   |     | NULL    |       |
| phone | varchar(20)      | YES  |     | NULL    |       |
| age   | tinyint unsigned | YES  |     | NULL    |       |
+-------+------------------+------+-----+---------+-------+
4 rows in set (0.00 sec)

phone 出现在 name 和 age 之间——AFTER name 决定位置,不写就默认加在最后一列。新加的列对已有的 3 行来说是 NULL(没给默认值),这不影响原来的数据。

改类型。年龄原来是 TINYINT UNSIGNED,现在要求非空、默认 18,用 MODIFY COLUMN:

MySQL 终端sql
ALTER TABLE lesson10_alter MODIFY COLUMN age SMALLINT NOT NULL DEFAULT 18;
输出示例(只读,不要粘贴执行)text不可复制
Query OK, 3 rows affected (0.09 sec)
Records: 3  Duplicates: 0  Warnings: 0

注意 MODIFY 后面要把这一列的完整定义重写一遍:类型、NOT NULL、DEFAULT 都要写全,没写的部分就真的没了。很多人写 MODIFY age SMALLINT 之后发现“非空约束不见了”,就是这个原因。已有的 23、21、25 都在 SMALLINT 范围内,所以数据原样保留。

换列名。phone 想改成 mobile,用 CHANGE COLUMN 旧名 新名 类型:

MySQL 终端sql
ALTER TABLE lesson10_alter CHANGE COLUMN phone mobile VARCHAR(20) COMMENT '手机号';
输出示例(只读,不要粘贴执行)text不可复制
Query OK, 0 rows affected (0.02 sec)
Records: 0  Duplicates: 0  Warnings: 0

CHANGE 和 MODIFY 的区别只有一点:CHANGE 多一个“旧名”,可以顺手改名;MODIFY 只能改定义、不能改名。同样,类型定义要写全。

删一列。试用的 mobile 不要了,用 DROP COLUMN:

MySQL 终端sql
ALTER TABLE lesson10_alter DROP COLUMN mobile;
输出示例(只读,不要粘贴执行)text不可复制
Query OK, 0 rows affected (0.02 sec)
Records: 0  Duplicates: 0  Warnings: 0
MySQL 终端sql
DESC lesson10_alter;
输出示例(只读,不要粘贴执行)text不可复制
+-------+-------------+------+-----+---------+-------+
| Field | Type        | Null | Key | Default | Extra |
+-------+-------------+------+-----+---------+-------+
| id    | int         | NO   | PRI | NULL    |       |
| name  | varchar(20) | NO   |     | NULL    |       |
| age   | smallint    | NO   |     | 18      |       |
+-------+-------------+------+-----+---------+-------+
3 rows in set (0.00 sec)

回到 3 列,但和最开始不一样了:age 已经是 SMALLINT NOT NULL DEFAULT 18。删列会连这一列的数据一起删掉,删之前先想清楚——这也是为什么下面「删表」要单独讲。

四种改法走完,先确认数据一行没丢:

MySQL 终端sql
SELECT id, name, age FROM lesson10_alter ORDER BY id;
输出示例(只读,不要粘贴执行)text不可复制
+----+--------+-----+
| id | name   | age |
+----+--------+-----+
|  1 | 张伟   |  23 |
|  2 | 李娜   |  21 |
|  3 | 王强   |  25 |
+----+--------+-----+
3 rows in set (0.00 sec)

3 行、3 列,age 还是 23、21、25。ALTER TABLE 改的是“表长什么样”,INSERT/UPDATE 改的才是“表里有什么”,这两件事这一节要一直分清。

表改名用 RENAME TABLE 旧名 TO 新名,它不是 ALTER,但同样只动结构:

MySQL 终端sql
RENAME TABLE lesson10_alter TO lesson10_alter_bak;
输出示例(只读,不要粘贴执行)text不可复制
Query OK, 0 rows affected (0.03 sec)
MySQL 终端sql
SHOW TABLES LIKE 'lesson10_alter%';
输出示例(只读,不要粘贴执行)text不可复制
+------------------------------------+
| Tables_in_itcast (lesson10_alter%) |
+------------------------------------+
| lesson10_alter_bak                 |
+------------------------------------+
1 row in set (0.00 sec)

名字变了,数据跟着表一起走。马上改回来,后面的核对还要用原名:

MySQL 终端sql
RENAME TABLE lesson10_alter_bak TO lesson10_alter;
输出示例(只读,不要粘贴执行)text不可复制
Query OK, 0 rows affected (0.02 sec)
MySQL 终端sql
SHOW TABLES LIKE 'lesson10_alter%';
输出示例(只读,不要粘贴执行)text不可复制
+------------------------------------+
| Tables_in_itcast (lesson10_alter%) |
+------------------------------------+
| lesson10_alter                     |
+------------------------------------+
1 row in set (0.00 sec)

删表用 DROP TABLE,整张表连同数据一起消失,不能撤销。这里用一张用完就扔的临时表演示,不动 lesson10_alter:

MySQL 终端sql
CREATE TABLE lesson10_alter_tmp (
  id INT PRIMARY KEY,
  note VARCHAR(10)
);
输出示例(只读,不要粘贴执行)text不可复制
Query OK, 0 rows affected (0.04 sec)
MySQL 终端sql
INSERT INTO lesson10_alter_tmp (id, note) VALUES (1, '临时');
输出示例(只读,不要粘贴执行)text不可复制
Query OK, 1 row affected (0.00 sec)
MySQL 终端sql
SELECT COUNT(*) AS 行数 FROM lesson10_alter_tmp;
输出示例(只读,不要粘贴执行)text不可复制
+--------+
| 行数   |
+--------+
|      1 |
+--------+
1 row in set (0.00 sec)
MySQL 终端sql
DROP TABLE lesson10_alter_tmp;
输出示例(只读,不要粘贴执行)text不可复制
Query OK, 0 rows affected (0.02 sec)
MySQL 终端sql
SHOW TABLES LIKE 'lesson10_alter%';
输出示例(只读,不要粘贴执行)text不可复制
+------------------------------------+
| Tables_in_itcast (lesson10_alter%) |
+------------------------------------+
| lesson10_alter                     |
+------------------------------------+
1 row in set (0.00 sec)

lesson10_alter_tmp 已经不在了,只剩正式的那张表。记住三种“删”的区别:DROP TABLE 删的是整张表;DELETE FROM 表 删的是行、表还在;清空整表(所有行都没了、表还留着)也属于这一类不可逆操作。三者都撤不回来(除非有备份),在学习库上只对自己前缀的表动手。

小结:P10 DDL 小结——建库、建表、改表、删表的顺序

那一集是 DDL 的收口,把库和表两层动作串成一条线:先有库,再在库里建表(列名 + 类型 + 约束),表用得不对就改结构(加列、改类型、换名、删列、改名),最后不要了才删表;库这一层同理,建库、切库、删库。真正要带走的是顺序感和代价感:改结构之前先用 DESC 看清现状,一次只改一件事,改完立刻用 DESC 和一条 SELECT 核对;删列、删表、删库都是不可逆的,代价依次变大,动手前先问“这张表里有没有别人的数据”。本节只在自己前缀的表上演示,学习库里的 itcast 和课程表一律不碰。

小结:P11 图形化工具——只用来看,不用来管

那一集介绍的图形化客户端(DataGrip 这类)能连数据库、点开表看结构和数据、在一个窗口里写 SQL 再执行。本课程不要求你装它,因为在服务器上通过 SSH 用 mysql> 已经能完成全部练习;如果你想装,把它当只读的观察窗口用:看表结构、看几行数据、把 SELECT 写在一个顺手的地方。有三件事不要在这个共享练习库上用它做——建库删库、清空整表、整库导入或“重置”;这些动作在 GUI 里通常只是一个右键菜单,点错不可撤销,而且删的是大家共用的 itcast。还要记住:GUI 并没有绕过权限,它只是把同样的 SQL 发给同一个服务端,course 账号做不到的事,换成图形界面照样做不到。

常见报错怎么办

看到意思怎么办
1050 Table ... already exists表已经存在(多半是重复跑建表)先 SHOW TABLES LIKE 'lesson10_%';,已有就跳过建表,直接核对
1060 Duplicate column name要加的列已经有了DESC 看一眼;已加过就往下走,不要重复加
1054 Unknown column列名不存在(改名/删列后再按旧名操作)用当前 DESC 的结果写列名,别凭记忆
1265 Data truncated要改成的类型装不下已有数据(改表时是 1265,插入超长是 1406)先查最长的那行,再决定新长度
说明 刻意写错的例子:重复加一列
MySQL 终端sql
ALTER TABLE lesson10_alter ADD COLUMN name VARCHAR(30);
输出示例(只读,不要粘贴执行)text不可复制
ERROR 1060 (42S21): Duplicate column name 'name'

name 已经存在,ADD COLUMN 不会“覆盖”,只会报 1060。要改 name 的定义请用 MODIFY 或 CHANGE。

说明 刻意写错的例子:把 name 改成只剩 1 个字符
MySQL 终端sql
ALTER TABLE lesson10_alter MODIFY COLUMN name VARCHAR(1);
输出示例(只读,不要粘贴执行)text不可复制
ERROR 1265 (01000): Data truncated for column 'name' at row 1

表里已经有 张伟 这样的两字姓名,缩到 1 个字符装不下,1265(Data truncated)直接拒绝——注意它拒绝了整条语句,表结构没变、数据也没被截断。这一步和插入超长报 1406 不同:改表时 MySQL 用 1265 表示“已有数据放不进新定义”。

说明 刻意写错的例子:对一个已经删掉的列改名
MySQL 终端sql
ALTER TABLE lesson10_alter CHANGE COLUMN mobile m2 VARCHAR(20);
输出示例(只读,不要粘贴执行)text不可复制
ERROR 1054 (42S22): Unknown column 'mobile' in 'lesson10_alter'

mobile 在上面的流程里已经被 DROP COLUMN 删掉了,CHANGE 找不到它,1054。

说明 刻意写错的例子:重复建表
MySQL 终端sql
CREATE TABLE lesson10_alter (id INT);
输出示例(只读,不要粘贴执行)text不可复制
ERROR 1050 (42S01): Table 'lesson10_alter' already exists

1050。重复学习时你会在建表这一步看到它,这不是坏了,是表已经在——直接跳过建表,从 DESC 往下核对。

练习

**1. 给 lesson10_alter 加一列 hired DATE,位置放在 age 后面;用 DESC 确认后再把它删掉,让表回到 3 列。**

提示 提示

加列用 ADD COLUMN ... AFTER age,删列用 DROP COLUMN;两次都用 DESC 核对。

参考答案 答案
MySQL 终端sql
ALTER TABLE lesson10_alter ADD COLUMN hired DATE AFTER age;
输出示例(只读,不要粘贴执行)text不可复制
Query OK, 0 rows affected (0.02 sec)
Records: 0  Duplicates: 0  Warnings: 0
MySQL 终端sql
DESC lesson10_alter;
输出示例(只读,不要粘贴执行)text不可复制
+-------+-------------+------+-----+---------+-------+
| Field | Type        | Null | Key | Default | Extra |
+-------+-------------+------+-----+---------+-------+
| id    | int         | NO   | PRI | NULL    |       |
| name  | varchar(20) | NO   |     | NULL    |       |
| age   | smallint    | NO   |     | 18      |       |
| hired | date        | YES  |     | NULL    |       |
+-------+-------------+------+-----+---------+-------+
4 rows in set (0.00 sec)
MySQL 终端sql
ALTER TABLE lesson10_alter DROP COLUMN hired;
输出示例(只读,不要粘贴执行)text不可复制
Query OK, 0 rows affected (0.02 sec)
Records: 0  Duplicates: 0  Warnings: 0
MySQL 终端sql
DESC lesson10_alter;
输出示例(只读,不要粘贴执行)text不可复制
+-------+-------------+------+-----+---------+-------+
| Field | Type        | Null | Key | Default | Extra |
+-------+-------------+------+-----+---------+-------+
| id    | int         | NO   | PRI | NULL    |       |
| name  | varchar(20) | NO   |     | NULL    |       |
| age   | smallint    | NO   |     | 18      |       |
+-------+-------------+------+-----+---------+-------+
3 rows in set (0.00 sec)

hired 加在 age 后面,删掉以后表回到 id、name、age 三列。注意 age 仍然是 smallint NOT NULL DEFAULT 18——那是最上面 MODIFY 的结果,不会因为这次练习回退。

说明 常见错

MySQL 的位置关键字只有 FIRST(加到第一列)和 AFTER 列名(加到某列后面),**没有 BEFORE**:写成 BEFORE age 会直接得到 1064 语法错。写 FIRST 能成功,但列会被插到 id 前面,也不是题目要的位置。加完一定用 DESC 看顺序。

**2. 用一条查询确认改表没有丢数据,并说出 age 的合计。**

提示 提示

COUNT(*) 数行,SUM(age) 求和;这一节没有删过任何行。

参考答案 答案
MySQL 终端sql
SELECT COUNT(*) AS 行数, SUM(age) AS 年龄合计 FROM lesson10_alter;
输出示例(只读,不要粘贴执行)text不可复制
+--------+--------------+
| 行数   | 年龄合计     |
+--------+--------------+
|      3 |           69 |
+--------+--------------+
1 row in set (0.00 sec)

3 行、69 —— 23 + 21 + 25。整个改表过程只动了结构,行数没变。

说明 常见错

用 SELECT COUNT(age) 代替 COUNT(*):这一列没有 NULL 时两者相同,但一旦有人插了空年龄就会少算一行,数行统一用 COUNT(*)。

**3. 用一条查询列出 lesson10_alter 三列的列名、类型、是否允许空、默认值,按列的原始顺序排。**

提示 提示

这些信息都在 information_schema.COLUMNS 里,ORDINAL_POSITION 是列的序号。

参考答案 答案
MySQL 终端sql
SELECT ORDINAL_POSITION AS 序号, COLUMN_NAME AS 列, COLUMN_TYPE AS 类型,
       IS_NULLABLE AS 允许空, COLUMN_DEFAULT AS 默认值
FROM information_schema.COLUMNS
WHERE TABLE_SCHEMA = 'itcast' AND TABLE_NAME = 'lesson10_alter'
ORDER BY ORDINAL_POSITION;
输出示例(只读,不要粘贴执行)text不可复制
+--------+------+-------------+-----------+-----------+
| 序号   | 列   | 类型        | 允许空    | 默认值    |
+--------+------+-------------+-----------+-----------+
|      1 | id   | int         | NO        | NULL      |
|      2 | name | varchar(20) | NO        | NULL      |
|      3 | age  | smallint    | NO        | 18        |
+--------+------+-------------+-----------+-----------+
3 rows in set (0.00 sec)

age 这一行是 smallint、NO、18——和 MODIFY 之后 DESC 看到的一致。

说明 常见错

漏写 TABLE_SCHEMA = 'itcast':同名的 COLUMNS 记录在别的库里也可能有,结果会混进别的库的同名表,行数一下就多了。

**4. MODIFY 和 CHANGE 有什么区别?各举一个本节用过的例子。**

参考答案 答案

两者都改列定义,CHANGE 多一个旧列名、可以顺便改名,MODIFY 不能改名。本节里 MODIFY COLUMN age SMALLINT NOT NULL DEFAULT 18 只改了类型和默认值;CHANGE COLUMN phone mobile VARCHAR(20) 把列名从 phone 换成了 mobile,同时重写了类型。

本单元验收

  1. 能用 ADD COLUMN ... AFTER 把新列插到指定位置,并用 DESC 验证顺序。
  2. 能说出 MODIFY 必须重写完整定义,漏写 NOT NULL/DEFAULT 会怎样。
  3. 能说出 CHANGE 与 MODIFY 的唯一区别,并各写一条语句。
  4. 能说出“删整张表”“删行”“清空整表”三种操作的区别,以及为什么本节只在 lesson10_ 前缀的表上演示。
  5. SELECT COUNT(*), SUM(age) FROM lesson10_alter; 得到 3 和 69,DESC 是 id/name/age 三列。

做到这里

做到这里今天可以停。你现在能安全地改一张表的结构:加列、改类型、换名、删列、改名、删表,并且每次都用 DESC 和一条 SELECT 核对。

下一节回到查询:先把“第 2 页从哪一行开始”算清楚,再用五道题把条件、LIKE、分组、排序、分页串起来。

来源与边界

本节对应 BV1Kr4y1i7ru 平台 part 9(原标题「09. 基础-SQL-DDL-表操作-修改&删除」)、part 10(「10. 基础-SQL-DDL小结」)、part 11(「11. 基础-SQL-图形化界面工具DataGrip」),在 P01–P50 范围内;主题与同版讲义《MySQL-基础篇》2.3.2.4 修改(p24)、2.3.2.5 删除(p25)、2.4 图形化界面工具(p26–34)对应。part 10 为小结集,part 11 的工具介绍按“只读认识”处理。讲义为公开镜像,仅作事实核对。

无逐字稿,本节未声称听过讲解。表名、3 行数据、全部改表语句与练习均为本教程原创;1050、1060、1054、1265 四个报错都是在作者自己的隔离 MySQL 8.0.46 实例上真实触发后抄下来的。本节不安装、不演示任何图形化工具,也不提供建库/删库语句。