MySQL不看视频 · SSH 实操

第一单元 · 连接、建表、查询 · 第 2 / 21 节

建第一张表,放进 5 行

检查 lesson01_students 不存在,建表并插入 5 行,用 DESC 和 SELECT 核对;处理重复建表、重复插入和重新开始练习。

6 个步骤 · 进度只存本机

约 20 分钟。做完这一节,你会有自己的第一张表,里面有 5 条记录,而且知道重复练习时怎么不把数据弄乱。

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

已完成 0 / 6 步

你现在在哪个提示符

理想情况是 mysql>,可以直接往下做。如果现在是 PS C:\Users\...>,先 ssh root@8.148.204.152;如果现在是 root@iZ7xvfx4kssqec2zzn99q9Z:~#,先 mysql-learning。

进来以后先确认库:

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

如果显示的不是 itcast,输入下面这句切回来:

MySQL 终端sql
USE itcast;
输出示例(只读,不要粘贴执行)text不可复制
Database changed

USE 只改变当前连接默认用哪个库,不动数据,也不复制表。

先确认这张表还不存在

MySQL 终端sql
SHOW TABLES LIKE 'lesson01_students';
输出示例(只读,不要粘贴执行)text不可复制
Empty set (0.00 sec)

结果是 Empty set,说明 itcast 里还没有 lesson01_students。这一步不是形式:表已经存在时再建,MySQL 会直接拒绝,后面会看到这个错误。

如果这里已经有一行,不要往下建表。直接翻到本节的「重复学习怎么办」,先看行数和数据,再决定是接着用还是清空重来。

为什么不叫 students:课程里的 emp、tb_user 会随着章节换成不同结构。加 lesson01_ 前缀,你的表和课程表互不干扰,也能一眼看出是本教程追加的练习表。

建表

在 mysql> 里输入下面整段。可以一次复制粘贴,MySQL 会等你敲分号才执行:

MySQL 终端sql
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='本教程追加的练习表,与视频示例无关';
输出示例(只读,不要粘贴执行)text不可复制
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 的缩写,作用是显示表里有哪些字段、什么类型、能不能为空。

MySQL 终端sql
DESC lesson01_students;
输出示例(只读,不要粘贴执行)text不可复制
+--------+------------------+------+-----+---------+-------+
| 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 行

MySQL 终端sql
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');
输出示例(只读,不要粘贴执行)text不可复制
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' 会变成四个字符的文本,不是空值。

看数据

MySQL 终端sql
SELECT * FROM lesson01_students ORDER BY id;
输出示例(只读,不要粘贴执行)text不可复制
+----+--------+------+--------+------------+
| 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 进来,然后执行:

MySQL 终端sql
SELECT COUNT(*) AS 行数 FROM lesson01_students;
输出示例(只读,不要粘贴执行)text不可复制
+--------+
| 行数   |
+--------+
|      5 |
+--------+
1 row in set (0.00 sec)

还是 5 行。MySQL 是跑在服务器上的服务,关掉客户端、断开 SSH,都不会清空表。下次登录接着用就行。

重复学习怎么办

这一节最容易被搞乱的地方,是过几天重新打开页面,又把建表和插入跑一遍。先认清三种情况,不用删库。

情况一:只是重新进来,想接着学。

先查表在不在,再看行数:

MySQL 终端sql
SHOW TABLES LIKE 'lesson01_students';
输出示例(只读,不要粘贴执行)text不可复制
+--------------------------------------+
| Tables_in_itcast (lesson01_students) |
+--------------------------------------+
| lesson01_students                    |
+--------------------------------------+
1 row in set (0.00 sec)
MySQL 终端sql
SELECT COUNT(*) AS 行数 FROM lesson01_students;
输出示例(只读,不要粘贴执行)text不可复制
+--------+
| 行数   |
+--------+
|      5 |
+--------+
1 row in set (0.00 sec)

如果表名查到了、行数是 5,说明上一节已经做完了。再用 DESC lesson01_students; 对一下 5 个字段,结构一致就直接去第 3 节,不要重复建表,也不要重复插入。

如果行数是 0,说明表建好了但数据还没进去,直接执行前面「放入 5 行」那段 INSERT,不要重建表。

情况二:又把 CREATE TABLE 跑了一遍。

会看到:

MySQL 终端sql
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='本教程追加的练习表,与视频示例无关';
输出示例(只读,不要粘贴执行)text不可复制
ERROR 1050 (42S01): Table 'lesson01_students' already exists

1050 的意思是表已经存在。表还在,里面的 5 行也还在,不需要修复,接着用就行。

有些人会改成 CREATE TABLE IF NOT EXISTS 再跑:

MySQL 终端sql
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='本教程追加的练习表,与视频示例无关';
输出示例(只读,不要粘贴执行)text不可复制
Query OK, 0 rows affected, 1 warning (0.00 sec)

它确实不报错了,但只是把错误降级成一条警告:

MySQL 终端sql
SHOW WARNINGS;
输出示例(只读,不要粘贴执行)text不可复制
+-------+------+------------------------------------------+
| Level | Code | Message                                  |
+-------+------+------------------------------------------+
| Note  | 1050 | Table 'lesson01_students' already exists |
+-------+------+------------------------------------------+
1 row in set (0.00 sec)

注意:IF NOT EXISTS 只避免“重复建表”的报错,它不会帮你补字段、改类型,也不会让旧表变成新结构。如果 DESC 的结果和你预期不一样,先停下来对照字段,不要继续插数据。本单元不要求你删表重建。

情况三:又把 INSERT 跑了一遍。

会看到:

MySQL 终端sql
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');
输出示例(只读,不要粘贴执行)text不可复制
ERROR 1062 (23000): Duplicate entry '1' for key 'lesson01_students.PRIMARY'

1062 是主键冲突:id = 1 已经有一行了,不能再来一行。失败的这条 INSERT 不会写入任何数据,所以表里仍然是 5 行。用上面的 COUNT(*) 再确认一次即可。

真的想从零再练一遍(可选)。

只清空这张表自己的行,不碰库,也不碰课程表:

MySQL 终端sql
DELETE FROM lesson01_students;
输出示例(只读,不要粘贴执行)text不可复制
Query OK, 5 rows affected (0.00 sec)

再执行一次原来的 INSERT:

MySQL 终端sql
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');
输出示例(只读,不要粘贴执行)text不可复制
Query OK, 5 rows affected (0.01 sec)
Records: 5  Duplicates: 0  Warnings: 0
MySQL 终端sql
SELECT COUNT(*) AS 行数 FROM lesson01_students;
输出示例(只读,不要粘贴执行)text不可复制
+--------+
| 行数   |
+--------+
|      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

MySQL 终端sql
SELECT * FROM lesson01_student;
输出示例(只读,不要粘贴执行)text不可复制
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

MySQL 终端sql
INSERT INTO lesson01_students (id, name) VALUES (6, NULL);
输出示例(只读,不要粘贴执行)text不可复制
ERROR 1048 (23000): Column 'name' cannot be null

name 字段写了 NOT NULL,插入时给 NULL 就会被拒绝。这一列必须给一个名字;把 name 从 INSERT 里去掉也会因为缺少必填值而失败。这里 id = 6 的那行没有写进去,表还是 5 行。

中文和单引号

中文可以直接用,连接和表都是 utf8mb4。名字里带英文单引号时,用两个单引号表示一个:

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

结果里的 王'强 是一条值,不是两个字符串。中文引号 “” 不能当字符串边界,粘贴 SQL 时注意别把英文引号自动替换成中文引号。

练习

1. 给表加第 6 行,自己编一个学号、姓名、年龄、城市和日期,查出来,再删掉。

先自己写,写完再看下面的示例。

参考答案 看示例答案
MySQL 终端sql
INSERT INTO lesson01_students (id, name, age, city, joined) VALUES (6, '林小雨', 20, '成都', '2024-03-15');
输出示例(只读,不要粘贴执行)text不可复制
Query OK, 1 row affected (0.00 sec)
MySQL 终端sql
SELECT id, name, age, city FROM lesson01_students WHERE id = 6;
输出示例(只读,不要粘贴执行)text不可复制
+----+-----------+------+--------+
| id | name      | age  | city   |
+----+-----------+------+--------+
|  6 | 林小雨    |   20 | 成都   |
+----+-----------+------+--------+
1 row in set (0.00 sec)

确认查到的就是刚加的那行,再删掉:

MySQL 终端sql
DELETE FROM lesson01_students WHERE id = 6;
输出示例(只读,不要粘贴执行)text不可复制
Query OK, 1 row affected (0.01 sec)
MySQL 终端sql
SELECT COUNT(*) AS 行数 FROM lesson01_students;
输出示例(只读,不要粘贴执行)text不可复制
+--------+
| 行数   |
+--------+
|      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 隔离实例上实跑过,包括重复执行和报错场景。