第一单元 · 连接、建表、查询 · 第 2 / 21 节
建第一张表,放进 5 行
检查 lesson01_students 不存在,建表并插入 5 行,用 DESC 和 SELECT 核对;处理重复建表、重复插入和重新开始练习。
约 20 分钟。做完这一节,你会有自己的第一张表,里面有 5 条记录,而且知道重复练习时怎么不把数据弄乱。
$ / mysql> 提示符和输出。已完成 0 / 6 步
你现在在哪个提示符
理想情况是 mysql>,可以直接往下做。如果现在是 PS C:\Users\...>,先 ssh root@8.148.204.152;如果现在是 root@iZ7xvfx4kssqec2zzn99q9Z:~#,先 mysql-learning。
进来以后先确认库:
SELECT DATABASE();
+------------+
| DATABASE() |
+------------+
| itcast |
+------------+
1 row in set (0.00 sec)
如果显示的不是 itcast,输入下面这句切回来:
USE itcast;
Database changed
USE 只改变当前连接默认用哪个库,不动数据,也不复制表。
先确认这张表还不存在
SHOW TABLES LIKE 'lesson01_students';
Empty set (0.00 sec)
结果是 Empty set,说明 itcast 里还没有 lesson01_students。这一步不是形式:表已经存在时再建,MySQL 会直接拒绝,后面会看到这个错误。
如果这里已经有一行,不要往下建表。直接翻到本节的「重复学习怎么办」,先看行数和数据,再决定是接着用还是清空重来。
为什么不叫 students:课程里的 emp、tb_user 会随着章节换成不同结构。加 lesson01_ 前缀,你的表和课程表互不干扰,也能一眼看出是本教程追加的练习表。
建表
在 mysql> 里输入下面整段。可以一次复制粘贴,MySQL 会等你敲分号才执行:
CREATE TABLE lesson01_students (
id INT PRIMARY KEY COMMENT '学号',
name VARCHAR(20) NOT NULL COMMENT '姓名',
age TINYINT UNSIGNED COMMENT '年龄',
city VARCHAR(20) COMMENT '所在城市',
joined DATE COMMENT '加入日期'
) COMMENT='本教程追加的练习表,与视频示例无关';
Query OK, 0 rows affected (0.03 sec)
Query OK, 0 rows affected 表示表结构建好了,但里面还没有数据。0 rows 不是失败,建表本来就不产生行。
逐行读一遍这张表的定义:
CREATE TABLE lesson01_students (:建一张叫lesson01_students的表,括号里是字段清单。id INT PRIMARY KEY:id是整数;PRIMARY KEY是主键,意思是这一列的值在表里唯一,用来认每一行,而且不能为空。name VARCHAR(20) NOT NULL:name是最多 20 个字符的文本;NOT NULL表示插入时必须给值,不能留空。age TINYINT UNSIGNED:age是 0 到 255 的整数;UNSIGNED表示不要负数。这里没写NOT NULL,所以可以留空。city VARCHAR(20):城市,最多 20 个字符,可以留空。joined DATE:加入日期,只存年月日,格式是YYYY-MM-DD,可以留空。COMMENT '...':给字段和表加说明文字,方便以后看,但它不是约束,不会拦住不合规的数据。约束是数据库强制执行的规则,比如主键唯一、NOT NULL必须有值。
字段之间用英文逗号分隔,最后一个字段后面不加逗号。这一条最容易错,多一个逗号就会报语法错误。
几个词的一句话解释:
- 字段(列):表头那一格,定义这一类数据叫什么、存什么类型。
- 行(记录):一条完整的数据,比如某个学生的全部信息。
- 主键:能唯一认出每一行的那一列,这里用学号
id。 - NULL:没有填值,不等于 0,也不等于空字符串
''。
表注释里写了“本教程追加的练习表,与视频示例无关”。这句话是给你自己看的,免得以后把它当成课程里的 emp 或 tb_user。
看表结构
建完先别急着插数据,用 DESC 看一眼实际结构。DESC 是 describe 的缩写,作用是显示表里有哪些字段、什么类型、能不能为空。
DESC lesson01_students;
+--------+------------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+--------+------------------+------+-----+---------+-------+
| id | int | NO | PRI | NULL | |
| name | varchar(20) | NO | | NULL | |
| age | tinyint unsigned | YES | | NULL | |
| city | varchar(20) | YES | | NULL | |
| joined | date | YES | | NULL | |
+--------+------------------+------+-----+---------+-------+
5 rows in set (0.00 sec)
这张表有 6 列、5 行。每一行是一个字段的定义,不是一条学生记录:
Field是字段名,Type是类型,Null表示能不能为空,Key标出主键,Default是默认值,Extra是附加信息。id的Null是NO,Key是PRI,说明主键不能为空。name的Null是NO,因为写了NOT NULL。age、city、joined的Null都是YES,可以留空。Default是“没给值时自动填什么”,这里显示NULL,表示没有额外指定默认值。
DESC 是你以后的检查工具。表建得对不对,不看感觉,看这张结构表。
放入 5 行
INSERT INTO lesson01_students (id, name, age, city, joined) VALUES
(1, '张伟', 23, '北京', '2024-03-02'),
(2, '李娜', 21, '上海', '2024-03-05'),
(3, '王强', 25, '广州', '2024-02-20'),
(4, '赵敏', 22, NULL, '2024-03-09'),
(5, '陈晨', 24, '杭州', '2024-03-12');
Query OK, 5 rows affected (0.00 sec)
Records: 5 Duplicates: 0 Warnings: 0
Query OK, 5 rows affected 表示 5 行都写进去了。下面那行 Records: 5 Duplicates: 0 Warnings: 0 是明细:处理 5 条,重复 0 条,警告 0 条。
这条 INSERT 的写法值得看懂:
- 表名后面用括号列出 5 个字段,值的顺序和字段顺序一一对应。
- 每个括号是一行,5 个括号就是 5 行。
- 文本和日期用英文单引号包起来,数字不用。
NULL不加引号。加了引号的'NULL'会变成四个字符的文本,不是空值。
看数据
SELECT * FROM lesson01_students ORDER BY id;
+----+--------+------+--------+------------+
| id | name | age | city | joined |
+----+--------+------+--------+------------+
| 1 | 张伟 | 23 | 北京 | 2024-03-02 |
| 2 | 李娜 | 21 | 上海 | 2024-03-05 |
| 3 | 王强 | 25 | 广州 | 2024-02-20 |
| 4 | 赵敏 | 22 | NULL | 2024-03-09 |
| 5 | 陈晨 | 24 | 杭州 | 2024-03-12 |
+----+--------+------+--------+------------+
5 rows in set (0.00 sec)
现在这张表有 5 行、5 列。横着看是一行记录,竖着看是一列值:
- 第 1 行是 id 1 的张伟,23 岁,北京,2024-03-02 加入。
- 第 4 行赵敏的
city显示NULL,因为插入时写的就是NULL。 *表示所有列;ORDER BY id让结果按学号从小到大排,方便对照。
ORDER BY 在这里只是为了显示稳定。不写它,MySQL 没有义务按你插入的顺序返回,行序可能变。
断开再进来,数据还在
可以现在输入 exit 回到 Linux,再 mysql-learning 进来,然后执行:
SELECT COUNT(*) AS 行数 FROM lesson01_students;
+--------+
| 行数 |
+--------+
| 5 |
+--------+
1 row in set (0.00 sec)
还是 5 行。MySQL 是跑在服务器上的服务,关掉客户端、断开 SSH,都不会清空表。下次登录接着用就行。
重复学习怎么办
这一节最容易被搞乱的地方,是过几天重新打开页面,又把建表和插入跑一遍。先认清三种情况,不用删库。
情况一:只是重新进来,想接着学。
先查表在不在,再看行数:
SHOW TABLES LIKE 'lesson01_students';
+--------------------------------------+
| Tables_in_itcast (lesson01_students) |
+--------------------------------------+
| lesson01_students |
+--------------------------------------+
1 row in set (0.00 sec)
SELECT COUNT(*) AS 行数 FROM lesson01_students;
+--------+
| 行数 |
+--------+
| 5 |
+--------+
1 row in set (0.00 sec)
如果表名查到了、行数是 5,说明上一节已经做完了。再用 DESC lesson01_students; 对一下 5 个字段,结构一致就直接去第 3 节,不要重复建表,也不要重复插入。
如果行数是 0,说明表建好了但数据还没进去,直接执行前面「放入 5 行」那段 INSERT,不要重建表。
情况二:又把 CREATE TABLE 跑了一遍。
会看到:
CREATE TABLE lesson01_students (
id INT PRIMARY KEY COMMENT '学号',
name VARCHAR(20) NOT NULL COMMENT '姓名',
age TINYINT UNSIGNED COMMENT '年龄',
city VARCHAR(20) COMMENT '所在城市',
joined DATE COMMENT '加入日期'
) COMMENT='本教程追加的练习表,与视频示例无关';
ERROR 1050 (42S01): Table 'lesson01_students' already exists
1050 的意思是表已经存在。表还在,里面的 5 行也还在,不需要修复,接着用就行。
有些人会改成 CREATE TABLE IF NOT EXISTS 再跑:
CREATE TABLE IF NOT EXISTS lesson01_students (
id INT PRIMARY KEY COMMENT '学号',
name VARCHAR(20) NOT NULL COMMENT '姓名',
age TINYINT UNSIGNED COMMENT '年龄',
city VARCHAR(20) COMMENT '所在城市',
joined DATE COMMENT '加入日期'
) COMMENT='本教程追加的练习表,与视频示例无关';
Query OK, 0 rows affected, 1 warning (0.00 sec)
它确实不报错了,但只是把错误降级成一条警告:
SHOW WARNINGS;
+-------+------+------------------------------------------+
| Level | Code | Message |
+-------+------+------------------------------------------+
| Note | 1050 | Table 'lesson01_students' already exists |
+-------+------+------------------------------------------+
1 row in set (0.00 sec)
注意:IF NOT EXISTS 只避免“重复建表”的报错,它不会帮你补字段、改类型,也不会让旧表变成新结构。如果 DESC 的结果和你预期不一样,先停下来对照字段,不要继续插数据。本单元不要求你删表重建。
情况三:又把 INSERT 跑了一遍。
会看到:
INSERT INTO lesson01_students (id, name, age, city, joined) VALUES
(1, '张伟', 23, '北京', '2024-03-02'),
(2, '李娜', 21, '上海', '2024-03-05'),
(3, '王强', 25, '广州', '2024-02-20'),
(4, '赵敏', 22, NULL, '2024-03-09'),
(5, '陈晨', 24, '杭州', '2024-03-12');
ERROR 1062 (23000): Duplicate entry '1' for key 'lesson01_students.PRIMARY'
1062 是主键冲突:id = 1 已经有一行了,不能再来一行。失败的这条 INSERT 不会写入任何数据,所以表里仍然是 5 行。用上面的 COUNT(*) 再确认一次即可。
真的想从零再练一遍(可选)。
只清空这张表自己的行,不碰库,也不碰课程表:
DELETE FROM lesson01_students;
Query OK, 5 rows affected (0.00 sec)
再执行一次原来的 INSERT:
INSERT INTO lesson01_students (id, name, age, city, joined) VALUES
(1, '张伟', 23, '北京', '2024-03-02'),
(2, '李娜', 21, '上海', '2024-03-05'),
(3, '王强', 25, '广州', '2024-02-20'),
(4, '赵敏', 22, NULL, '2024-03-09'),
(5, '陈晨', 24, '杭州', '2024-03-12');
Query OK, 5 rows affected (0.01 sec)
Records: 5 Duplicates: 0 Warnings: 0
SELECT COUNT(*) AS 行数 FROM lesson01_students;
+--------+
| 行数 |
+--------+
| 5 |
+--------+
1 row in set (0.00 sec)
DELETE FROM lesson01_students; 会删掉这张表的全部行。它只影响你自己的练习表。不要删库,不要用任何“切回空库”或“重置阶段”的脚本,也不要重装 MySQL 来“恢复”。那些会清掉整个练习库或别人的表,不是本教程的修复手段。
常见错误
1050 Table already exists
上面已经遇到。表在,数据在,跳过建表,继续。
1062 Duplicate entry
上面也遇到了。主键重复,先 SELECT 看看现有数据,确认是自己想加的那行,再决定跳过还是删掉重来。
1146 Table doesn't exist
SELECT * FROM lesson01_student;
ERROR 1146 (42S02): Table 'itcast.lesson01_student' doesn't exist
报错里的表名是 lesson01_student,少了一个 s。MySQL 把库名和表名一起写出来了:itcast.lesson01_student。先 SHOW TABLES LIKE 'lesson01%'; 看真实表名,再改 SQL。
1048 Column cannot be null
INSERT INTO lesson01_students (id, name) VALUES (6, NULL);
ERROR 1048 (23000): Column 'name' cannot be null
name 字段写了 NOT NULL,插入时给 NULL 就会被拒绝。这一列必须给一个名字;把 name 从 INSERT 里去掉也会因为缺少必填值而失败。这里 id = 6 的那行没有写进去,表还是 5 行。
中文和单引号
中文可以直接用,连接和表都是 utf8mb4。名字里带英文单引号时,用两个单引号表示一个:
SELECT '王''强' AS 姓名;
+---------+
| 姓名 |
+---------+
| 王'强 |
+---------+
1 row in set (0.00 sec)
结果里的 王'强 是一条值,不是两个字符串。中文引号 “” 不能当字符串边界,粘贴 SQL 时注意别把英文引号自动替换成中文引号。
练习
1. 给表加第 6 行,自己编一个学号、姓名、年龄、城市和日期,查出来,再删掉。
先自己写,写完再看下面的示例。
参考答案 看示例答案
INSERT INTO lesson01_students (id, name, age, city, joined) VALUES (6, '林小雨', 20, '成都', '2024-03-15');
Query OK, 1 row affected (0.00 sec)
SELECT id, name, age, city FROM lesson01_students WHERE id = 6;
+----+-----------+------+--------+
| id | name | age | city |
+----+-----------+------+--------+
| 6 | 林小雨 | 20 | 成都 |
+----+-----------+------+--------+
1 row in set (0.00 sec)
确认查到的就是刚加的那行,再删掉:
DELETE FROM lesson01_students WHERE id = 6;
Query OK, 1 row affected (0.01 sec)
SELECT COUNT(*) AS 行数 FROM lesson01_students;
+--------+
| 行数 |
+--------+
| 5 |
+--------+
1 row in set (0.00 sec)
回到 5 行。DELETE 后面的 WHERE id = 6 很重要,少了它就会删掉整张表的行。
**2. 看 DESC lesson01_students; 的结果回答:哪几个字段必须给值?哪几个可以留空?**
参考答案 对答案
id 和 name 必须给值。id 是主键,主键本身不能为空;name 写了 NOT NULL。age、city、joined 的 Null 列都是 YES,可以留空。
3. 再执行一次上面的 5 行 INSERT,读报错,指出重复的是哪个 id,并说明表有没有被改。
参考答案 对答案
报错是 ERROR 1062 ... Duplicate entry '1' for key 'lesson01_students.PRIMARY',重复的是 id = 1。这条 INSERT 整体失败,一行都没写进去,表仍然是 5 行;用 SELECT COUNT(*) AS 行数 FROM lesson01_students; 可以确认。
做到这里
做到这里今天可以停。表建好了,5 行数据在了,你也知道重复执行时该看什么、该跳过什么。
下一节只查这张表:选列、用 WHERE 筛行、用 ORDER BY 排序、用 LIMIT 取前几行。不会新增、修改或删除数据。
说明 这一节和视频课、讲义是什么关系(可选读)
建表、数据类型、插入这些主题对应课程的 DDL 与 DML 部分,但本页不按视频时间组织,也不要求看视频。
示例表 lesson01_students 是本教程为动手路径新写的,不是视频里的 emp、tb_user,字段和行值都不同;表注释里也标了这一点。所有 SQL 都在 MySQL 8.0.46 隔离实例上实跑过,包括重复执行和报错场景。