第三章 MySQL 库表操作

3.1 SQL 语句基础

3.1.1 SQL 简介

SQL(Structured Query Language,结构化查询语言):在关系型数据库上执行数据操作、数据检索以及数据维护的标准语言。

用 SQL 可以完成:

  • 改变数据库的结构

  • 更改系统的安全设置

  • 增加用户对数据库或表的许可权限

  • 在数据库中检索需要的信息

  • 对数据库的信息进行更新

3.1.2 SQL 语句分类

分类 全称 作用对象 常见语句
DDL Data Definition Language,数据定义语言 库、表、列、索引等数据库对象 CREATE、DROP、ALTER、RENAME、TRUNCATE
DML Data Manipulation Language,数据操作语言 库里的记录(行) INSERT、DELETE、UPDATE、SELECT
DCL Data Control Language,数据控制语言 库、表、字段、用户的访问权限和安全级别 GRANT、REVOKE
TCL Transaction Control,事务控制 事务 COMMIT、ROLLBACK、SAVEPOINT

记忆点:DDL 动"结构",DML 动"数据",DCL 动"权限",TCL 动"事务"。

3.1.3 SQL 书写规范

SQL 语句不区分大小写 (建议关键字用大写),但字符串常量区分大小写

可单行或多行书写,以 ; 结尾

关键词不能跨多行书写,也不能简写

用空格和缩进提高可读性

注释写法:

bash 复制代码
/* 多行注释 */
// 多行注释
​
-- 单行注释(注意 -- 后面要跟一个空格)
# 单行注释

3.2 数据库操作

3.2.1 数据库的登录及退出

前提:必须先启动 MySQL 服务,否则报错:

sql 复制代码
[root@server ~]# mysql -u root -p
ERROR 2002 (HY000): Can't connect to local MySQL server through socket '/var/lib/mysql/mysql.sock' (2)
​
# 先启动服务
[root@server ~]# systemctl start mysqld

登录格式:

复制代码
mysql -u 用户名 -h 服务器的主机地址 -p密码
参数 含义
-u 登录数据库的用户名,如 root
-h 服务器主机地址;客户端与服务器同机时可写 localhost 或直接写 IP
-p 用户登录密码,-p 与密码之间不能有空格;无密码时不写 -p

注意:-p 123456(有空格)会被当成"密码为空 + 把 123456 当库名处理",登录失败。

正常登录:

sql 复制代码
[root@server ~]# mysql -uroot -p
Enter password:
Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 16
Server version: 8.0.37 MySQL Community Server - GPL
​
Copyright (c) 2000, 2024, Oracle and/or its affiliates.
Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective owners.
​
Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

登录信息逐条解释:

提示信息 含义
Commands end with ; or \g mysql 命令以 ; 或 \g 结束,遇到结束符才开始执行
Your MySQL connection id is 16 数据库的连接次数
Server version: 8.0.37 ... 服务器版本
Type 'help;' or '\h' for help 输入 help; 或 \h 查看帮助
Type '\c' to clear the current input statement 输入 \c 清除前面已输入的命令(不是清屏)

带密码登录会有警告(密码出现在命令历史里,不安全):

sql 复制代码
[root@server ~]# mysql -uroot -p123456
mysql: [Warning] Using a password on the command line interface can be insecure.

退出:exit / quit / \q

3.2.2 查看数据库

sql 复制代码
show databases [like 条件];      -- 注意是 databases,有 s
sql 复制代码
mysql> show databases;
+--------------------+
| Database           |
+--------------------+
| information_schema |
| mysql              |
| performance_schema |
| sys                |
+--------------------+
4 rows in set (0.00 sec)

MySQL 自带的 4 个库:

库名 作用
information_schema 存储系统中数据库对象的信息:用户表信息、列信息、权限信息、字符集信息、分区信息等(数据字典表)
performance_schema 存储数据库服务器的性能参数
mysql 存储系统的用户权限信息及帮助信息
sys 5.7 新增(之前需手工导入),用视图把 information_schema 和 performance_schema 结合起来,查询结果更易理解

带通配符的条件:

通配符 含义
% 匹配任意零个或多个字符
_ 匹配任意单个字符
sql 复制代码
mysql> show databases like 'sys';
+----------------+
| Database (sys) |
+----------------+
| sys            |
+----------------+
1 row in set (0.00 sec)
​
mysql> show databases like 's%';        -- 以 s 开头
mysql> show databases like '__sql';     -- 两个任意字符 + sql → mysql

常用查询函数:

sql 复制代码
mysql> select now();        -- 当前时间
+---------------------+
| now()               |
+---------------------+
| 2023-02-10 09:39:14 |
+---------------------+

mysql> select version();    -- 数据库版本 → 8.0.37
mysql> select user();       -- 当前登录用户及主机名 → root@localhost

3.2.3 创建数据库

sql 复制代码
create database 数据库名;
mysql> create database testdb;
Query OK, 1 row affected (0.00 sec)

mysql> create database testdb;       -- 已存在则报错
ERROR 1007 (HY000): Can't create database 'testdb'; database exists

查看创建数据库的语句 (注意:database 没有 s):

sql 复制代码
show create database 数据库名;
mysql> show create database testdb \G
*************************** 1. row ***************************
       Database: testdb
Create Database: CREATE DATABASE `testdb` /*!40100 DEFAULT CHARACTER SET utf8mb4
COLLATE utf8mb4_0900_ai_ci */ /*!80016 DEFAULT ENCRYPTION='N' */

建库语句三段解析:

片段 含义
CREATE DATABASE \testdb`` 创建数据库 testdb
/*!40100 DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci */ 字符集 utf8mb4(一个字符占 1~4 字节,可存 emoji);utf8mb4_0900_ai_ci 是排序规则,ai = 不区分重音,ci = 不区分大小写
/*!80016 DEFAULT ENCRYPTION='N' */ 数据库未加密

\G 的作用:把宽表格竖排显示,避免内容太长折行看不清。

关于反引号 `````:

作用:避免与 SQL 语法(保留字)冲突,同时让代码更规范易读

只有保留关键字和特殊字符必须加,普通表名列名可不加;但建议统一加,保持规范

不推荐用关键字作为库名/表名

sql 复制代码
mysql> create database database;     -- 报错,database 是关键字
ERROR 1064 (42000): You have an error in your SQL syntax; ...

mysql> create database `database`;   -- 加反引号即可
Query OK, 1 row affected (0.01 sec)

3.2.4 切换数据库

sql 复制代码
select database();     -- 查看当前使用的数据库,未选择时为 NULL(显示为空)
use 数据库名;           -- 切换/选择数据库(可不加 ;)
sql 复制代码
mysql> select database();
+------------+
| database() |
+------------+
| NULL       |
+------------+

mysql> use testdb;
Database changed

mysql> select database();
+------------+
| database() |
+------------+
| testdb     |
+------------+

3.2.5 删除数据库

sql 复制代码
drop database 数据库名;
sql 复制代码
mysql> drop database testdb;
Query OK, 0 rows affected (0.01 sec)

3.2.6 小结

sql 复制代码
# 新建数据库
create database 数据库名;

# 设为当前数据库
use 数据库名;

# 查看当前使用的数据库
select database();

# 显示所有数据库
show databases;

# 删除数据库
drop database 数据库名;

习题:创建 temp1、temp2 数据库,查看所有数据库,使用 temp1 数据库,删除 temp1、temp2 数据库。

sql 复制代码
mysql> create database temp1;
mysql> create database temp2;
mysql> show databases;
mysql> use temp1;
mysql> drop database temp1;
mysql> drop database temp2;

3.3 MySQL 字符集

MySQL 字符集包括两个概念:字符集(CHARACTER) 和 排序规则/字符序(COLLATION)。

3.3.1 概念

3.3.1.1 字符集

1、Character Set(字符集):字符的编码规则,规定了字符在数据库中的存储格式(占多少空间、支持哪些字符)

2、选错字符集,轻则影响性能,重则数据存储乱码

3、MySQL 5.7 默认字符集是 latin1,8.0 默认是 utf8mb4

4、MySQL 8 的库、表以及程序中应尽可能使用 utf8mb4(支持 emoji)

5、MySQL 中的 utf8 是 utf8mb3 的别名,避免使用

  • 字符集选用规则:

    • 面向国外业务、要处理多国多语言 → utf8 / utf8mb4

    • 只支持中文、无国外业务 → 为性能考虑可用 GBK

3.3.1.2 字符序(Collation)

1、字符序就是字符排序的规则集合,例如 A>B>a>b 或 a>b>A>B

2、字符序主要影响字符的排序结果

3.3.2 查看 MySQL 字符集

sql 复制代码
show character set;                 -- 查看所有支持的字符集
show character set like 'gbk';      -- 查看指定字符集
show character set like 'utf%';
sql 复制代码
mysql> show character set like 'gbk';
+---------+------------------------+-------------------+--------+
| Charset | Description            | Default collation | Maxlen |
+---------+------------------------+-------------------+--------+
| gbk     | GBK Simplified Chinese | gbk_chinese_ci    |      2 |
+---------+------------------------+-------------------+--------+
字段 含义
Charset 字符集名称
Description 字符集的简单描述
Default collation 该字符集的默认字符序
Maxlen 该字符集中字符的最大存储长度(字节)

常见字符集:

字符集 支持范围 Maxlen
latin1 西欧字符、希腊字符等 1
gbk 中文简体(非国际通用字符集) 2
big5 中文繁体 2
utf8 / utf8mb3 几乎支持世界所有国家字符 3
utf8mb4 完全兼容 UTF-8,用 4 字节存更多字符 4

3.3.3 查看 MySQL 字符序

复制代码
show collation;
字段 含义
Collation 字符序名称
Charset 该字符序关联的字符集
Id 字符序 ID
Default 是否是所关联字符集的默认字符序(armscii8_general_ci 是默认,armscii8_bin 不是)
Compiled 字符集是否已编译到服务器中
Sortlen 与对字符串排序所需的内存量有关
Pad_attribute 比较字符串时对末尾 padding 的处理:NO PAD 表示把末尾 padding 也算进去,否则不算

字符序命名规则:

  1. 以对应的字符集名称开头 (如 utf8mb4_...)

  2. 中间是国家名或 general

  3. 后缀表示特性:

后缀 含义
bin 二进制
ci 大小写不敏感(case insensitive)
cs 大小写敏感(case sensitive)
ai 口音(Accent)不敏感
as 口音敏感
ks 假名(Kanatype)敏感

3.3.4 查看当前字符集设置

sql 复制代码
show variables like 'character_set%';


mysql> show variables like 'character_set%';
+--------------------------+----------------------------+
| Variable_name            | Value                      |
+--------------------------+----------------------------+
| character_set_client     | utf8mb4                    |
| character_set_connection | utf8mb4                    |
| character_set_database   | utf8mb4        # 重要      |
| character_set_filesystem | binary                     |
| character_set_results    | utf8mb4                    |
| character_set_server     | utf8mb4        # 重要      |
| character_set_system     | utf8mb3                    |
| character_sets_dir       | /usr/share/mysql/charsets/ |
+--------------------------+----------------------------+
变量 含义
character_set_client MySQL 客户端的字符集
character_set_connection 数据通信链路字符集,客户端向服务器发请求时按此编码
character_set_database 数据库默认使用的字符集
character_set_filesystem 服务器文件系统字符集,固定为 binary
character_set_results 服务器返回给客户端的结果使用的字符集
character_set_server 服务器默认字符集
character_set_system 服务器存储元数据使用的字符集

**注意:**client / connection / results 三者与库表字符集不一致时最容易出乱码

3.3.5 utf8 与 utf8mb4 的区别

  • MySQL 5.5.3 之后新增 utf8mb4,mb4 = most bytes 4,专门兼容 4 字节的 Unicode

  • utf8mb4 是 utf8 的超集,除改编码外无需其他转换;为省空间,一般场景用 utf8 也够

  • 那为什么还要 utf8mb4?

    • MySQL 的 utf8 最大只用 3 字节,遇到 4 字节宽字符就插入异常

    • 3 字节 UTF-8 最大只能编码到 0xFFFF,即 Unicode 的基本多文种平面(BMP)

    • 不在 BMP 内的字符(Emoji、很多生僻汉字、新增 Unicode 字符)都无法用 utf8 存

  • 结论:8.0 之后建议统一使用 utf8mb4


3.4 数据库对象

3.4.1 组成

数据库对象包括:库(database)、表(table)、列(column)、索引(index)、视图(view)、存储过程/函数、触发器、约束等。它们都存放在 information_schema 这类系统库中,有各自的属性和名字。

3.4.2 数据库对象的命名规则

1、使用有意义的英文词汇,词汇之间用下划线分隔

2、由字母、数字、#、下划线、$ 组成,必须以英文字母开头

3、不要使用 MySQL 保留字

4、所有对象使用小写(MySQL 可配置大小写敏感,为统一性一律小写)

5、同一个数据库对象集合内对象不能同名

3.4.3 生产中对象命名规范

数据库:

1、命名尽量不超过 30 个字符

2、一般为 项目名称 + 代表库含义的简写,如 IM 项目的工作流库 → im_flow

3、使用小写

表:

类型 规则 示例
常规表 t_ + 模块简写 + 表含义简写 t_user_eduinfo(用户模块的教育信息表)
临时表 temp_ + 模块 + 表 + 日期后缀 temp_user_eduinfo_20240520
备份表 bak_ + 模块 + 表 + 日期后缀 bak_user_eduinfo_20231219

同一模块的表尽量用相同前缀,表名尽量表达含义

多个单词以下划线 _ 分隔

常规表表名尽量不超过 30 个字符,temp/bak 表视情况而定,也尽量简短

命名使用小写


3.5 表的基本操作

3.5.1 数据类型

类型分三类:数字、文本、日期/时间。

1、整型
数据类型 字节数 带符号值范围(默认) 不带符号值范围(unsigned)
tinyint 1 -128, 127 0, 255
smallint 2 -32768, 32767 0, 65535
mediumint 3 -8388608, 8388607 0, 16777215
int 4 -2147483648, 2147483647 0, 4294967295
bigint 8 -9223372036854775808, 9223372036854775807 0, 18446744073709551615

**注:**unsigned bigint 上限 = 2^64 − 1 = 18446744073709551615(讲义写作 ...616,属笔误)。

选型提醒:一定要为合适的列选合适类型。例如枚举字段只有 0/1,用 TINYINT 就够;若用 BIGINT,按 100 万行算就浪费约 700 万字节(≈6.7 MB),表多了浪费更多。

整型 (N) 的写法 (如 int(10)):

无论 N 是多少,int 永远占 4 字节

N 只是显示宽度,不足补 0,超过长度则无视长度直接显示整个数字

MySQL 8.0.17 之后不推荐这种写法

2、浮点型 / 定点型
数据类型 字节数 备注
float 4 单精度浮点型
double 8 双精度浮点型
decimal --- 定点型,以字符串形式保存,不丢精度

可用 float(M,D)、double(M,D) 限制总宽度 M 和精度 D(如 float(3,2));不指定 M、D 时按实际精度处理

精度丢失实验:

sql 复制代码
mysql> create table t1(float_num float(10,2), double_num double(20,2), decimal_num decimal(20,2));
Query OK, 0 rows affected, 2 warnings (0.03 sec)
​
mysql> insert into t1 value (1234567.66, 1234567899000000.66, 123456789000000.66);
Query OK, 1 row affected (0.00 sec)
​
mysql> select * from t1;
+------------+---------------------+--------------------+
| float_num  | double_num          | decimal_num        |
+------------+---------------------+--------------------+
| 1234567.62 | 1234567899000000.80 | 123456789000000.66 |
+------------+---------------------+--------------------+

写入的 1234567.66 变成了 1234567.62 ------ 这就是 float/double 的近似值存储 问题。 结论:float/double 在 db 中存的是近似值 ,decimal 以字符串形式保存,不丢精度。银行、互联网金融等对小数敏感的系统必须用 decimal。

3、文本 / 字符串型
数据类型 描述
char(size) 固定长度字符串(字母、数字、特殊字符),括号内指定长度,最多 255 个字符
varchar(size) 可变长度字符串,括号内指定最大长度
tinytext 最大 255 个字符
text 最大 65,535 个字符
mediumtext 最大 16,777,215 个字符
longtext 最大 4,294,967,295 个字符
blob 二进制大对象,最多 65,535 字节
mediumblob 二进制大对象,最多 16,777,215 字节
longblob 二进制大对象,最多 4,294,967,295 字节
enum(x,y,z,...) 枚举,列表最多 65,535 个值;插入不在列表中的值会写入空值;值按输入顺序 存储(书写为 ENUM('X','Y','Z'))
set 与 enum 类似,最多 64 个列表项,但可存一个以上的值
bash 复制代码
char vs varchar

char 固定长度 0~255(与编码无关),不足用空格补足,取数据时会去掉结尾空格 → 适合固定长度内容(手机号、身份证号)

varchar 适合长度不固定的内容

补充纠正:varchar 上限不是 255 字符。MySQL 中 VARCHAR 最大 65,535 字节(还受行长度限制),长度超过 255 也不会被自动转成 TEXT。

blob vs text

blob 用二进制形式存储,无排序规则、无字符集

text 用字符形式存储,有排序规则和字符集,适合存文章内容、评论等
4、 日期 / 时间型
数据类型 字节数 格式 备注
date 3 yyyy-MM-dd 存储日期值
time 3 HH:mm:ss 存储时分秒
year 1 yyyy 存储年
datetime 8 yyyy-MM-dd HH:mm:ss 存储日期 + 时间
timestamp 4 yyyy-MM-dd HH:mm:ss 存储日期 + 时间,可作时间戳
常用类型总结
场景 推荐类型
主键/编号 int unsigned(配 auto_increment)
金额 decimal(M,2)
定长编码(手机号、身份证、性别) char
变长文本(姓名、地址) varchar
长文本(文章、评论) text
日期 / 时间 date / datetime

3.5.2 创建表

格式:

bash 复制代码
create table [if not exists] 表名 (表定义选项);

表定义选项:

复制代码
列名1 列类型1 约束,
列名2 列类型2 约束,
……

默认表被创建到当前数据库中;若表已存在、没有当前数据库或数据库不存在,都会报错

  • 创建表必须指定的信息:

    • 表名(不区分大小写,不能用 DROP、ALTER、INSERT 等关键字)

    • 每个列(字段)的名称和数据类型,多列用逗号隔开

**注意:**必须先 use 数据库名,否则报 No database selected

示例 1:创建数据库并建表

sql 复制代码
mysql> create database mydb2_stuinfo;
Query OK, 1 row affected (0.02 sec)
​
mysql> use mydb2_stuinfo;
Database changed
​
mysql> select database();
+---------------+
| database()    |
+---------------+
| mydb2_stuinfo |
+---------------+
​
mysql> create table student1(id int, name varchar(30), sex char(2), age int
    -> unsigned, score float, addr varchar(50));
Query OK, 0 rows affected (0.02 sec)
​
mysql> show tables;
+-------------------------+
| Tables_in_mydb2_stuinfo |
+-------------------------+
| student1                |
+-------------------------+
​
mysql> desc student1;
+-------+--------------+------+-----+---------+-------+
| Field | Type         | Null | Key | Default | Extra |
+-------+--------------+------+-----+---------+-------+
| id    | int          | YES  |     | NULL    |       |
| name  | varchar(30)  | YES  |     | NULL    |       |
| sex   | char(2)      | YES  |     | NULL    |       |
| age   | int unsigned | YES  |     | NULL    |       |
| score | float        | YES  |     | NULL    |       |
| addr  | varchar(50)  | YES  |     | NULL    |       |
+-------+--------------+------+-----+---------+-------+
6 rows in set (0.00 sec)

示例 2:新建员工表

sql 复制代码
mysql> create database mydb3_employee;
mysql> use mydb3_employee;
mysql> create table tb1(id int(11), name varchar(25), salary float);
Query OK, 0 rows affected, 1 warning (0.03 sec)     -- int(11) 已不推荐,产生 warning
练习:在 mydb2_stuinfo 库中新建班级表 class1,字段:cla_num int、cla_name varchar(10)、number int、monitor varchar(20)、creation_date date。
sql 复制代码
mysql> create table class1(
    -> cla_num int,
    -> cla_name varchar(10),
    -> number int,
    -> monitor varchar(20),
    -> creation_date date);
Query OK, 0 rows affected (0.02 sec)

3.5.3 查看表

1、 显示当前库中已有的表

sql 复制代码
show tables [from 数据库名] [like wild];



mysql> show tables;
+-------------------------+
| Tables_in_mydb2_stuinfo |
+-------------------------+
| class1                  |
| student1                |
+-------------------------+
2 rows in set (0.00 sec)

mysql> show tables from mydb3_employee;      -- 查看另一个库的表
+--------------------------+
| Tables_in_mydb3_employee |
+--------------------------+
| tb1                      |
+--------------------------+

2、查看表结构(各列信息)

sql 复制代码
{describe | desc} 表名 [列名];
show columns from 表名;
字段 含义
Field 字段名称
Type 字段类型
Null 是否允许为空(YES / NO)
Key 索引类型(PRI 主键 / UNI 唯一 / MUL 普通索引)
Default 默认值
Extra 附加信息(如 auto_increment)
sql 复制代码
mysql> desc student1;
+-------+--------------+------+-----+---------+-------+
| Field | Type         | Null | Key | Default | Extra |
+-------+--------------+------+-----+---------+-------+
| id    | int          | YES  |     | NULL    |       |
...
6 rows in set (0.00 sec)

mysql> show columns from student1;       -- 结果与 desc 相同

3、 查看建表语句(含存储引擎、字符集)

sql 复制代码
show create table 表名 \G


mysql> show create table student1\G
*************************** 1. row ***************************
       Table: student1
Create Table: CREATE TABLE `student1` (
  `id` int DEFAULT NULL,
  `name` varchar(30) DEFAULT NULL,
  `sex` char(2) DEFAULT NULL,
  `age` int unsigned DEFAULT NULL,
  `score` float DEFAULT NULL,
  `addr` varchar(50) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci
1 row in set (0.00 sec)

只有 show create table 才能看到 comment 列描述、存储引擎、字符集/字符序 ,desc 看不到。

3.5.4 删除表

sql 复制代码
drop table [if exists] 表名;


mysql> drop table tb1;
Query OK, 0 rows affected (0.01 sec)

mysql> drop table if exists tb1;     -- 推荐写法:先判断是否存在
Query OK, 0 rows affected (0.01 sec)

注意:

1、用户必须拥有 drop table 权限,否则表不会被删除

2、推荐使用 if exists,避免表不存在时报错

3.5.5 修改表

3.5.5.1 修改表名(重命名)
sql 复制代码
# 法 1
alter table 表名 rename 新表名;
​
# 法 2
rename table 表名 to 新表名;
mysql> create table student2(id int, name varchar(30), salary float);
mysql> alter table student2 rename student3;
mysql> rename table student3 to teacher1;
3.5.5.2 新增列
sql 复制代码
alter table 表名 add 新列名 列类型 [after 列名 | first];

after:在指定列之后插入新列

first:在第一列插入新列

没有 before 关键字;不写 after/first 则默认加到最后一列

sql 复制代码
# 加一列 password,默认追加到末尾
mysql> alter table student1 add password char(8);

# 在 age 之后加 notes
mysql> alter table student1 add notes varchar(30) after age;

# 在第一列加 aa
mysql> alter table student1 add aa int first;
sql 复制代码
mysql> desc student1;
+----------+--------------+------+-----+---------+-------+
| Field    | Type         | Null | Key | Default | Extra |
+----------+--------------+------+-----+---------+-------+
| aa       | int          | YES  |     | NULL    |       |
| id       | int          | YES  |     | NULL    |       |
| name     | varchar(30)  | YES  |     | NULL    |       |
| sex      | char(2)      | YES  |     | NULL    |       |
| age      | int unsigned | YES  |     | NULL    |       |
| notes    | varchar(30)  | YES  |     | NULL    |       |
| score    | float        | YES  |     | NULL    |       |
| addr     | varchar(50)  | YES  |     | NULL    |       |
| password | char(8)      | YES  |     | NULL    |       |
+----------+--------------+------+-----+---------+-------+
9 rows in set (0.00 sec)
3.5.5.3 删除列
sql 复制代码
alter table 表名 drop 列名;
sql 复制代码
mysql> alter table student1 drop notes;
mysql> alter table student1 drop aa;
3.5.5.4 修改列名
sql 复制代码
alter table 表名 change 旧列名 新列名 列类型;

change 可以列名和列类型一起改(新类型必须写上)

sql 复制代码
mysql> alter table student1 change password passwd char(8);
3.5.5.5 修改列类型
sql 复制代码
alter table 表名 modify 列名 列类型;
sql 复制代码
mysql> alter table student1 add birthday char(10);     -- 先加一列 char(10)
mysql> alter table student1 modify birthday date;      -- 再改成 date
3.5.5.6 修改列位置
sql 复制代码
alter table 表名 modify 列名 列类型 after 某列;
alter table 表名 modify 列名 列类型 first;

注意:add ... after 是"新增一列放某位置",modify ... after 是"把已有列挪到某位置",两者别混。

练习:新建商品数据库 mydb4_product 并使用;创建商品表 pro_tb1(num int、trade_name varchar(30)、number int、price float(5,2));显示表结构;新增 notes varchar(30)(最后一列);把 num 改为 char(4);删除该表(先判断)。
sql 复制代码
mysql> create database mydb4_product;
mysql> use mydb4_product;
mysql> create table pro_tb1(num int, trade_name varchar(30), number int, price float(5,2));
mysql> desc pro_tb1;
mysql> alter table pro_tb1 add notes varchar(30);
mysql> alter table pro_tb1 modify num char(4);
mysql> desc pro_tb1;
mysql> drop table if exists pro_tb1;

3.5.6 复制表

1、只复制表结构

sql 复制代码
create table 新表名 like 源表;
sql 复制代码
mysql> create table student2 like student1;
Query OK, 0 rows affected (0.01 sec)

mysql> select * from student2;      -- 只是结构框架,没有数据
Empty set (0.00 sec)

2、复制表结构 + 数据

sql 复制代码
create table 新表名 select * from 源表;
sql 复制代码
mysql> create table student3 select * from student1;
Query OK, 0 rows affected (0.01 sec)
​
mysql> select * from student3;
+------+----------+------+------+-------+-------+----------+------------+
| id   | name     | sex  | age  | score | addr  | passwd   | birthday   |
+------+----------+------+------+-------+-------+----------+------------+
| 1000 | zhangsan | M    |   18 |  98.5 | xi'an | 12345678 | 2005-10-01 |
| 1001 | lisi     | W    |   20 | 66.79 | baoji | 87654321 | 2003-05-01 |
+------+----------+------+------+-------+-------+----------+------------+

3、只复制数据

sql 复制代码
insert into 新表名 select * from 源表;
sql 复制代码
mysql> insert into student2 select * from student1;
Query OK, 2 rows affected (0.00 sec)
写法 结构 数据 索引/约束
create table 新 like 源 有 无 有(外键除外)
create table 新 select * from 源 有 有 不复制索引和约束
insert into 新 select * from 源 需先建表 有 ---

3.5.7 数据字典

1、数据字典 :类似系统编目或花名册,保存数据库服务器上的元数据信息(数据库的整体属性信息)

2、元数据(meta data):"data about data",描述数据的数据。内容包括:数据库属性、数据表属性、字段属性、视图属性、用户信息、统计类信息等

3、information_schema 是 MySQL 系统自带的库,提供访问数据库元数据的方式,堪称 MySQL 实例的百科全书(字符集、权限、实体对象、外键约束、分区、压缩表、表信息、索引信息、参数、优化、锁和事务等)

sql 复制代码
mysql> use information_schema;
mysql> show tables;

常用表:

表名 存放内容
tables 数据库里所有的数据表,以及每个表所在的数据库
schemata 所有的数据库信息
views 所有的视图信息
columns 所有的列信息
triggers 所有的触发器
routines 所有存储过程和函数
key_column_usage 所有的主外键
table_constraints 全部约束
statistics 数据表的索引

3.5.8 小结

sql 复制代码
# 新建表
create table 表名(列名1 类型, 列名2 类型, ......);
​
# 查看表结构
desc 表名;
​
# 修改表名
alter table 旧表名 rename 新表名;
​
# 表中添加新列
alter table 表名 add 新列名 列类型 [after|first] 列名;
​
# 删除列
alter table 表名 drop 列名;
​
# 修改列名
alter table 表名 change 旧列名 新列名 列类型;
​
# 修改列类型 / 移动列位置
alter table 表名 modify 列名 列类型 [after 某列 | first];
​
# 删除表
drop table if exists 表名;

删除表前的注意事项:

  1. 删除前一定备份数据,以免数据丢失

  2. 确认该表没有被其他表引用(外键约束)

  3. 确认数据已备份且不再需要

  4. 确认表数据已清空,否则破坏外键约束

  5. 确认相关索引已处理,残留索引可能导致删除失败

  6. 确认相关程序已停止运行,避免影响其他程序

3.6 表的约束

3.6.1 概念

为什么需要约束:真正约束字段的是数据类型,但数据类型约束很单一,需要额外约束保证数据合法性。例如"身份证字段要求唯一",光靠类型定义做不到;还有"字段能否为 NULL、有无默认值、能否加注释、能否作为唯一标识该行的数据"等,类型都管不了。

本质 :约束是 MySQL 通过限制用户操作 的方式,来维护数据安全与完整性的一套方案;是在表上强制执行的数据校验规则。

数据完整性从四方面考虑:

完整性 含义 例子
实体完整性 不能存在两条完全相同、无法区分的记录 主键
域完整性 列取值在合理范围内 年龄 0~120,性别"男/女"
引用完整性 引用的数据必须真实存在 员工所在部门在部门表中能找到
用户自定义完整性 业务规则 用户名唯一、密码不能为空、部门经理工资不高于本部门平均工资 5 倍

约束作为数据库对象,存放在系统表中,也有自己的名字。

常见约束:非空约束、默认值约束、主键约束、外键约束、唯一约束、检查约束(另有列描述 comment)。

创建约束的时机:

  • 建表的同时创建

  • 建表后创建(修改表)

约束可定义在列级或表级。

3.6.2 语法

3.6.2.1 列级约束(定义列的同时定义约束)

操作 语法
定义 create table 表名 (列名 类型 约束)
修改 alter table 表名 modify 列名 列类型 列约束
删除 alter table 表名 modify 列名 列类型(不加约束即可删除)

3.6.2.2 表级约束(在定义了所有列之后定义)

操作 语法
定义 create table 表名 (列信息, constraint 约束名 约束类型(要约束的列名))
添加 alter table 表名 add [constraint 约束名] 约束类型(要约束的列名)
删除 alter table 表名 drop 约束类型 约束名
  • 约束名推荐:表名_列名_约束类型简介

  • 表级约束不能直接修改,必须先删除原有表级约束,再新增

3.6.3 非空约束(NOT NULL)

1、作用:限定某个字段/某列的值不允许为空

2、两个取值:null(默认)和 not null

3、列字段默认一般都允许为空,但实际开发应尽可能保证不为空,因为数据为空无法参与运算

4、所有数据类型的值都可以是 NULL;空字符串 ≠ NULL,0 ≠ NULL

sql 复制代码
mysql> create table myclass1(
    -> class_name varchar(20) not null,
    -> class_room varchar(20) not null,
    -> class_id int);
Query OK, 0 rows affected (0.02 sec)

mysql> desc myclass1;
+------------+-------------+------+-----+---------+-------+
| Field      | Type        | Null | Key | Default | Extra |
+------------+-------------+------+-----+---------+-------+
| class_name | varchar(20) | NO   |     | NULL    |       |
| class_room | varchar(20) | NO   |     | NULL    |       |
| class_id   | int         | YES  |     | NULL    |       |
+------------+-------------+------+-----+---------+-------+

# 少给 class_name 赋值 → 失败
mysql> insert into myclass1 (class_room) values('计科1');
ERROR 1364 (HY000): Field 'class_name' does not have a default value

# 两个非空列都赋值 → 成功;class_id 无约束可为空
mysql> insert into myclass1 (class_name,class_room) values('A104','计科1');
Query OK, 1 row affected (0.00 sec)

建表后添加 / 删除非空约束:

sql 复制代码
# 添加
alter table student1 modify id int not null;
alter table student1 modify name varchar(30) not null;

# 删除(不写 not null 即可,null 可省略)
alter table student1 modify id int null;
alter table student1 modify name varchar(30);

3.6.4 默认值约束(DEFAULT)

作用:给某个字段/某列指定默认值;插入数据时若此字段没有显式赋值,则自动取默认值

默认值的生效时机:插入时不给该字段赋值

sql 复制代码
mysql> create table teacher1(id int not null, name varchar(30) not null, sex char(2) default '女');
Query OK, 0 rows affected (0.03 sec)

mysql> desc teacher1;
+-------+-------------+------+-----+---------+-------+
| Field | Type        | Null | Key | Default | Extra |
+-------+-------------+------+-----+---------+-------+
| id    | int         | NO   |     | NULL    |       |
| name  | varchar(30) | NO   |     | NULL    |       |
| sex   | char(2)     | YES  |     | 女      |       |
+-------+-------------+------+-----+---------+-------+

# 不写 sex → 取默认值"女"
mysql> insert into teacher1(id,name) values('1001','林雪');
mysql> select * from teacher1;
+------+--------+------+
| id   | name   | sex  |
+------+--------+------+
| 1001 | 林雪   | 女   |
+------+--------+------+

# 显式写值 → 用插入值
mysql> insert into teacher1 values('1002','石磊','男');

建表后增加默认值:

sql 复制代码
alter table student1 modify sex char(2) default '女';

注意:若某字段同时设置 not null + default,不插入时取默认值、插入时取插入值,此时 not null 就"毫无意义"了(因为总有值)。但要注意:若既无 not null 又无 default,插入时不给值就是 NULL。

3.6.5 列描述 ------ COMMENT

列描述没有实际含义 ,专门用来描述字段,会随建表语句一起保存,供操作者查看该列含义,相当于 C/C++ 的注释

desc 看不到 comment,要用 show create table 查看

sql 复制代码
mysql> create table myclass2(
    -> class_name varchar(20) not null comment '教室',
    -> class_room varchar(20) default '计科2');
Query OK, 0 rows affected (0.02 sec)

mysql> show create table myclass2\G
*************************** 1. row ***************************
       Table: myclass2
Create Table: CREATE TABLE `myclass2` (
  `class_name` varchar(20) NOT NULL COMMENT '教室',
  `class_room` varchar(20) DEFAULT '计科2' COMMENT '班级'
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci
1 row in set (0.00 sec)

3.6.6 主键约束(PRIMARY KEY)

作用:数据库存数据不是唯一目的,还要考虑提取数据 ;表里的记录需要一个特定的唯一标识来表示唯一性,这个标识就是主键(类似学号、序号),可以据此唯一定位一条记录

主键:primary key,用来唯一地约束该字段里的数据

  • 特点:

    • 主键字段不能为空、不能重复

    • 一张表最多只能有一个主键

    • 主键所在的列通常是整数类型

    • 主键约束是最频繁的约束

注意:

复制代码
#### 创建主键约束时,系统默认在该列(或列组合)上建立对应的**主键索引** (能走主键查询就走主键,效率更高)
  • 删除主键约束后,对应的索引会自动删除
  • 不要修改主键字段的值------主键是记录的唯一标识,改了可能破坏数据完整性

示例:创建时增加主键

sql 复制代码
mysql> create table t1 ( id int unsigned primary key comment '学号不能为空',
    -> name varchar(20) not null, sex char(2) default '男');
Query OK, 0 rows affected (0.02 sec)

mysql> desc t1;
+-------+--------------+------+-----+---------+-------+
| Field | Type         | Null | Key | Default | Extra |
+-------+--------------+------+-----+---------+-------+
| id    | int unsigned | NO   | PRI | NULL    |       |
| name  | varchar(20)  | NO   |     | NULL    |       |
| sex   | char(2)      | YES  |     | 男      |       |
+-------+--------------+------+-----+---------+-------+
3 rows in set (0.01 sec)

mysql> insert into t1 values(1,'孙文','女');
mysql> insert into t1(id,name) values(2,'李文华');

mysql> insert into t1(id,name) values(1,'黎明');    -- 主键重复 → 报错
ERROR 1062 (23000): Duplicate entry '1' for key 't1.PRIMARY'

示例:修改表,追加主键

sql 复制代码
mysql> alter table teacher1 add primary key(id);
Query OK, 0 rows affected (0.07 sec)

# 也可以写成:
alter table 表名 modify 列名 列类型 primary key;

注意:若该字段内已有重复值,不能直接设为主键,必须先删掉重复数据再添加。

删除主键:

sql 复制代码
alter table 表名 drop primary key;

复合主键 :在所有字段之后用 primary key(主键字段列表) 定义。多个字段合在一起作为一个主键------单个字段重复没问题,只要组合起来不冲突即可。

sql 复制代码
mysql> create table t2 (id int, hostname varchar(10), ip varchar(20), port int
    -> unsigned, primary key (ip,port));
Query OK, 0 rows affected (0.02 sec)

mysql> desc t2;
+----------+--------------+------+-----+---------+-------+
| Field    | Type         | Null | Key | Default | Extra |
+----------+--------------+------+-----+---------+-------+
| id       | int          | YES  |     | NULL    |       |
| hostname | varchar(10)  | YES  |     | NULL    |       |
| ip       | varchar(20)  | NO   | PRI | NULL    |       |
| port     | int unsigned | NO   | PRI | NULL    |       |
+----------+--------------+------+-----+---------+-------+

mysql> insert into t2 values(1,'node1','192.168.48.2',120);
mysql> insert into t2 values(2,'node2','192.168.48.3',120);   -- 单个字段重复,可以

mysql> insert into t2 values(3,'node2','192.168.48.2',120);   -- 组合冲突,报错
ERROR 1062 (23000): Duplicate entry '192.168.48.2-120' for key 't2.PRIMARY'

3.6.7 主键自增长(AUTO_INCREMENT)

1、作用:给主键添加自增长的数值

2、auto_increment:对应字段不给值时,由系统自动触发,从当前字段已有最大值 +1 得到新值;通常和主键搭配,作为逻辑主键

  • 注意:

    1. 自增长字段必须是整数

    2. 可以不设初始值,默认从 1 开始递增

    3. 被自增长的字段必须是主键或其他具有唯一性的键

    4. 自增长字段也可以手动插入数据,只要不与已有数据重复即可,之后新增数据从最大值继续递增

    5. 任何字段要做自增长,前提是本身是一个索引 (desc 里 Key 一栏有值)

    6. 一张表最多只能有一个自增长列

    7. 约束的字段必须具备 NOT NULL 属性

sql 复制代码
# 1. 先加主键
mysql> alter table student1 add primary key(id);
Query OK, 0 rows affected (0.06 sec)

# 2. 设置自增长
mysql> alter table student1 modify id int auto_increment;
Query OK, 3 rows affected (0.05 sec)

# 3. 查看:Extra 出现 auto_increment
mysql> desc student1;
+----------+--------------+------+-----+---------+----------------+
| Field    | Type         | Null | Key | Default | Extra          |
+----------+--------------+------+-----+---------+----------------+
| id       | int          | NO   | PRI | NULL    | auto_increment |
| name     | varchar(30)  | YES  |     | NULL    |                |
| sex      | char(2)      | YES  |     | 女      |                |
| age      | int unsigned | YES  |     | NULL    |                |
| score    | float        | YES  |     | NULL    |                |
| addr     | varchar(50)  | YES  |     | NULL    |                |
| passwd   | char(8)      | YES  |     | NULL    |                |
| birthday | date         | YES  |     | NULL    |                |
+----------+--------------+------+-----+---------+----------------+

# 4. 不写 id,系统自动补
mysql> insert into student1(name) values('关六');
Query OK, 1 row affected (0.00 sec)

指定自增长的起始值 (建表时加 auto_increment=N):

sql 复制代码
mysql> create table t3 ( id int primary key auto_increment, systemname
    -> varchar(10) not null ) auto_increment=100;
Query OK, 0 rows affected (0.02 sec)

mysql> insert into t3(systemname) values('windows'),('linux');

mysql> select * from t3;
+-----+------------+
| id  | systemname |
+-----+------------+
| 100 | windows    |
| 101 | linux      |
+-----+------------+

另一种方式:第一条记录手动插入 id=5,之后就从 5 往后递增(自增以表中最大值为基准)。


3.6.8 唯一性约束(UNIQUE)

1、产生原因:表里往往有多个字段需要唯一(数据不能重复),但一张表只能有一个主键,唯一键就用来解决"多字段唯一"的问题

2、作用:限制某个字段/某列的值不能重复

3、唯一键与主键的关系:

对比项 主键 PRIMARY KEY 唯一键 UNIQUE
数量 只能一个 可以多个
空值 不允许 NULL 允许 NULL(可多个 NULL)
定位 更多是标识唯一性、用来查找 更多是保证业务上不重复、保证数据完整性
关系 二者不是对立而是补充,共同彻底杜绝误插入 同左

例:学生信息表有身份证号和学号,可把身份证号设为主键 ,学号设为唯一键

sql 复制代码
mysql> create table t4( id int primary key,
    -> name varchar(20) unique comment '名字不能重名,可以为空' );
Query OK, 0 rows affected (0.02 sec)

mysql> desc t4;
+-------+-------------+------+-----+---------+-------+
| Field | Type        | Null | Key | Default | Extra |
+-------+-------------+------+-----+---------+-------+
| id    | int         | NO   | PRI | NULL    |       |
| name  | varchar(20) | YES  | UNI | NULL    |       |
+-------+-------------+------+-----+---------+-------+

mysql> insert into t4 values(1,'刘文');
mysql> insert into t4 values(2,'刘文');     -- 唯一键重复,失败
ERROR 1062 (23000): Duplicate entry '刘文' for key 't4.name'
mysql> insert into t4 values(2,'张磊');

唯一键冲突会让自增字段出现"断号":

sql 复制代码
mysql> alter table t4 modify id int auto_increment;
mysql> insert into t4(name) values('张磊磊');    -- id=3
mysql> insert into t4(name) values('张磊磊');    -- 冲突报错,但 id=4 已被消耗
ERROR 1062 (23000): Duplicate entry '张磊磊' for key 't4.name'

mysql> insert into t4(name) values('钱明');      -- 结果 id=5,跳过了 4
mysql> select * from t4;
+----+-----------+
| id | name      |
+----+-----------+
|  1 | 刘文      |
|  2 | 张磊      |
|  3 | 张磊磊    |
|  5 | 钱明      |
+----+-----------+

原因:自增值在插入尝试时 就已分配,即使语句失败也不回退。所以自增列不保证连续,不要拿它当业务编号用。


3.6.9 外键约束(FOREIGN KEY)

作用:限定某个表的某个字段的引用完整性

概念:

术语 含义
主表(父表) 被引用的表、被参考的表
从表(子表) 引用别人的表、参考别人的表
外键 从表中的某个字段,引用主表的某个(或多个)字段
引用键 主表被引用的字段

外键约束主要定义在从表 上;主表则必须有主键约束或唯一键约束

定义外键后,要求外键列的数据必须在主表的主键列中存在,或为 NULL

格式:

sql 复制代码
foreign key (从表的字段名称) references 主表名字(主表的字段名称)

示例:

sql 复制代码
# 1. 定义主表
mysql> create table myclass3(id int primary key, name varchar(20) not null);
Query OK, 0 rows affected (0.02 sec)
​
# 2. 定义从表(带外键)
mysql> create table myclass3_stu( id int primary key, name varchar(30) not null,
    -> class_id int, foreign key (class_id) references myclass3(id) );
Query OK, 0 rows affected (0.02 sec)
​
mysql> desc myclass3_stu;
+----------+-------------+------+-----+---------+-------+
| Field    | Type        | Null | Key | Default | Extra |
+----------+-------------+------+-----+---------+-------+
| id       | int         | NO   | PRI | NULL    |       |
| name     | varchar(30) | NO   |     | NULL    |       |
| class_id | int         | YES  | MUL | NULL    |       |   ← MUL 说明建了外键索引
+----------+-------------+------+-----+---------+-------+
​
# 3. 主表先插数据
mysql> insert into myclass3 values(10,'C++'),(20,'Java');
​
# 4. 从表正常插入
mysql> insert into myclass3_stu values(1,'Li',10),(2,'Sun',20);
Query OK, 2 rows affected (0.00 sec)
​
# 5. 插入不存在的班级 → 被外键拦住
mysql> insert into myclass3_stu values(3,'wang',30);
ERROR 1452 (23000): Cannot add or update a child row: a foreign key constraint fails
(`stu_info`.`myclass3_stu`, CONSTRAINT `myclass3_stu_ibfk_1` FOREIGN KEY (`class_id`)
REFERENCES `myclass3` (`id`))
​
# 6. 用 NULL 代替 → 表示该学生还没分配班级,可以插入
mysql> insert into myclass3_stu values(3,'wang',null);
Query OK, 1 row affected (0.00 sec)

语义理解 :学生表通过 class_id 和班级表关联,此时学生表是从表、班级表是主表。

要加 class_id=30 的学生,班级表里必须先有 id=30 的班级

要删班级表里 id=20 的班级,必须先保证学生表里没有 class_id=20 的学生

外键可以为 NULL(表示尚未分配)

外键本质上是给表与表之间增加了一层约束关系,插入时自动校验

注意:

  1. 主表必须已存在于数据库中,或就是当前正在创建的表(后者称自参照表 ,这种结构叫自参照完整性)

  2. 必须为主表定义主键

  3. 主键不能包含空值 ,但允许外键出现空值;只要外键的每个非空值都能在主键中找到,外键内容就是正确的

  4. 在主表表名后指定的列(或列组合)必须是主表的主键或候选键

  5. 外键中列的数目必须和主表主键中列的数目相同(联合主键场景)

  6. 外键中列的数据类型必须和主表主键中对应列的数据类型相同


3.6.10 检查约束(CHECK)

作用:检查数据表中字段值有效性 的手段,一般用于设置值的范围

  • 注意:

    • 要根据实际情况设置,减少无效数据输入

    • 默认值约束和非空约束可以看作特殊的检查约束

    • MySQL 8.0.16 之前 check 只是"写着不报错但不生效"(需靠触发器实现),8.0.16 起才真正支持

示例 1:创建表时设置

sql 复制代码
mysql> create table t5(id int primary key, name varchar(20), salary float,
    -> check(salary>0 and salary<10000) );
Query OK, 0 rows affected (0.02 sec)

mysql> insert into t5 values(1,'Li',5984);
Query OK, 1 row affected (0.00 sec)

mysql> insert into t5 values(2,'wang',12000);
ERROR 3819 (HY000): Check constraint 't5_chk_1' is violated.

示例 2:修改表时添加

sql 复制代码
alter table 表名 add [constraint 约束名] check(条件);

mysql> alter table t5 add constraint check_id check(id>0);
Query OK, 1 row affected (0.02 sec)

3.6.11 删除表的约束

对象 列级 / 表级 删除方式
NOT NULL 仅列级 MODIFY
DEFAULT 仅列级 MODIFY
AUTO_INCREMENT(严格说不是约束) 仅列属性 MODIFY
PRIMARY KEY 列级 / 表级 DROP PRIMARY KEY
UNIQUE 列级 / 表级 DROP INDEX 约束名
FOREIGN KEY 列级 / 表级 DROP FOREIGN KEY 约束名
CHECK 列级 / 表级 DROP CHECK 约束名
sql 复制代码
# 删除 not null 约束、default 约束(不写即删)
alter table 表名 modify 列名 类型;

# 删除 unique 约束
alter table 表名 drop index 唯一约束名;

# 删除 primary key 约束
alter table 表名 drop primary key;

# 删除 foreign key 约束
alter table 表名 drop foreign key 外键名;

# 删除 check 约束
alter table 表名 drop check 约束名;

3.6.12 总结:完整建表语句

sql 复制代码
create table 表名(
  列名 列类型 [auto_increment] [default 默认值] [列约束] comment '说明',
  ...
  [表约束]
) [engine=表类型] [default] charset=字符集;
组成部分 说明
列类型 该列数据的存储类型
auto_increment 自动增长,只能是数值类型的列
default 该列的默认值
约束 对列的一些限制
engine 表类型,即存储引擎
charset 表的字符集

3.6.13 存储引擎

数据库存储引擎是数据库底层软件组织,DBMS 用它来创建、查询、更新和删除数据;不同引擎提供不同的存储机制、索引技巧、锁定水平

插件式存储引擎是 MySQL 最重要的特性之一:可按应用需要选择如何存储和索引数据、是否使用事务

MySQL 的核心就是存储引擎

常见引擎:

引擎 特点 / 适用场景
MyISAM MySQL 5.5 之前 的默认引擎,最为常用;插入、查询速度快;不支持事务;适合以读写为主、很少更新删除、对事务完整性和并发性要求不高的场景
InnoDB 事务型首选/默认 引擎;支持 ACID 事务 、行级锁定、外键;适合对事务完整性要求高、并发下要求数据一致性的场景
MEMORY 所有数据置于内存,插入/更新/查询效率极高;占用与数据量成正比的内存;重启后数据丢失
Archive 适合存储大量独立的、作为历史记录的数据;插入速度快,但查询支持较差
Federated 把不同的 MySQL 服务器联合起来,逻辑上组成一个完整数据库,适合分布式应用
CSV 逻辑上以逗号分隔数据;为每个表创建一个 .csv 文件(普通文本,每行一条记录);不支持索引
BlackHole "黑洞"引擎,写入的数据都会消失,一般用于记录 binlog 做复制的中继
PERFORMANCE_SCHEMA 主要用于收集数据库服务器的性能参数

MyISAM / MEMORY / InnoDB 对比:

功能 MyISAM MEMORY InnoDB
存储限制 256TB RAM 64TB
支持事务 No No Yes
支持全文索引 Yes No No
支持 B 树索引 Yes Yes Yes
支持哈希索引 No Yes No
支持集群索引 No No Yes
支持数据索引 No Yes Yes
支持数据压缩 Yes No No
空间使用率 低 N/A 高
支持外键 No No Yes

查看与指定:

sql 复制代码
# 查看当前默认存储引擎
mysql> show variables like '%storage_engine%';
+---------------------------------+-----------+
| Variable_name                   | Value     |
+---------------------------------+-----------+
| default_storage_engine          | InnoDB    |
| default_tmp_storage_engine      | InnoDB    |
| disabled_storage_engines        |           |
| internal_tmp_mem_storage_engine | TempTable |
+---------------------------------+-----------+

# 查看所有支持的引擎
mysql> show engines\G

# 建表时指定
create table (...) engine=InnoDB;

建表不指定引擎时,使用 my.cnf 中 default-storage-engine=InnoDB 指定的引擎。


3.6.14 练习

创建销售数据库 mydb5_sales 并使用;创建产品表 pro_tb(pro_num int 唯一键、pro_name varchar(30)、pro_price float(5,2)、pro_spe varchar(8) 默认值"件");创建订单表 orders(id int 主键自增长、customer_id int、pro_num int 外键、signing_date date、amount float(5,2),金额 check 大于 0)。

sql 复制代码
mysql> create database mydb5_sales;
Query OK, 1 row affected (0.01 sec)
​
mysql> use mydb5_sales;
Database changed
​
mysql> create table pro_tb(pro_num int unique, pro_name varchar(30) not null,
    -> pro_price float(5,2) not null, pro_spe varchar(8) default '件' );
Query OK, 0 rows affected, 2 warnings (0.01 sec)
​
mysql> desc pro_tb;
+-----------+-------------+------+-----+---------+-------+
| Field     | Type        | Null | Key | Default | Extra |
+-----------+-------------+------+-----+---------+-------+
| pro_num   | int         | YES  | UNI | NULL    |       |
| pro_name  | varchar(30) | NO   |     | NULL    |       |
| pro_price | float(5,2)  | NO   |     | NULL    |       |
| pro_spe   | varchar(8)  | YES  |     | 件      |       |
+-----------+-------------+------+-----+---------+-------+
4 rows in set (0.01 sec)
​
mysql> create table orders(id int primary key auto_increment, customer_id int not null,
    -> pro_num int not null, signing_date date, amount float(5,2),
    -> check(amount>0), foreign key(pro_num) references pro_tb(pro_num));
Query OK, 0 rows affected, 4 warnings (0.02 sec)
​
mysql> desc orders;
+--------------+------------+------+-----+---------+----------------+
| Field        | Type       | Null | Key | Default | Extra          |
+--------------+------------+------+-----+---------+----------------+
| id           | int        | NO   | PRI | NULL    | auto_increment |
| customer_id  | int        | NO   |     | NULL    |                |
| pro_num      | int        | NO   | MUL | NULL    |                |
| signing_date | date       | YES  |     | NULL    |                |
| amount       | float(5,2) | YES  |     | NULL    |                |
+--------------+------------+------+-----+---------+----------------+
5 rows in set (0.00 sec)

注意: 外键引用的列必须本身有索引 ------这里 pro_tb.pro_num 先设了 unique,所以 foreign key(pro_num) references pro_tb(pro_num) 才能成功。若引用的是无索引的普通列,会报 errno 150。


3.7 MySQL 数据库账户及授权

3.7.1 密码策略

MySQL 8.0 起 ,默认身份验证插件从 mysql_native_password 改为 caching_sha2_password

MySQL 5.7 的默认插件一直是 mysql_native_password:密码验证快、无需在网络中发送真实密码、不需要加密连接。但它依赖 SHA1 算法,而 NIST(美国国家标准与技术研究院)已建议停止使用 SHA1,因为 SHA1 及 MD5 等哈希算法已被证明非常容易破解

查看数据库当前密码策略:

sql 复制代码
mysql> show variables like "%password%";
+----------------------------------------------+-----------------+
| Variable_name                                | Value           |
+----------------------------------------------+-----------------+
| # 是否自动生成RSA密钥对文件
  caching_sha2_password_auto_generate_rsa_keys | ON              |
| # 哈希回合数
  caching_sha2_password_digest_rounds          | 5000            |
| # RSA 私钥文件
  caching_sha2_password_private_key_path       | private_key.pem |
| # RSA 公钥文件
  caching_sha2_password_public_key_path        | public_key.pem  |
| # MySQL密码过期时间,单位为天
  default_password_lifetime                    | 0               |
| # 超时断开
  disconnect_on_expired_password               | ON              |
| # 随机密码长度
  generated_random_password_length             | 20              |
| # 是否支持代理用户控制认证插件
  mysql_native_password_proxy_users            | OFF             |
| # 不允许用户使用最近n次用过的密码
  password_history                             | 0               |
| # 修改密码时是否需要提供当前密码,OFF 不需要
  password_require_current                     | OFF             |
| # 不允许用户使用最近n天内用过的密码
  password_reuse_interval                      | 0               |
| report_password                              |                 |
| sha256_password_auto_generate_rsa_keys       | ON              |
| sha256_password_private_key_path             | private_key.pem |
| sha256_password_proxy_users                  | OFF             |
| sha256_password_public_key_path              | public_key.pem  |
+----------------------------------------------+-----------------+

查看/安装密码强度校验插件:

sql 复制代码
mysql> show variables like 'validate_password%';
Empty set (0.01 sec)
# 注意:显示为空说明插件未安装

mysql> INSTALL COMPONENT 'file://component_validate_password';
Query OK, 0 rows affected (0.01 sec)

mysql> show variables like 'validate_password%';
+--------------------------------------+--------+
| Variable_name                        | Value  |
+--------------------------------------+--------+
| # 是否能将密码设置成当前用户名
  validate_password.check_user_name    | ON     |
| # 密码强度校验字典文件路径,默认为空
  validate_password.dictionary_file    |        |
| # 密码最小长度,默认8,最小值为4
  validate_password.length             | 8      |
| # 至少包含的大写+小写字母个数
  validate_password.mixed_case_count   | 1      |
| # 至少包含的数字个数
  validate_password.number_count       | 1      |
| # 密码强度检查等级
  validate_password.policy             | MEDIUM |
| # 必须包含的特殊字符个数
  validate_password.special_char_count | 1      |
+--------------------------------------+--------+
7 rows in set (0.00 sec)

密码强度检查等级(validate_password.policy):

等级 检查对象
0 or LOW 检查长度
1 or MEDIUM 检查长度、数字、大小写、特殊字符
2 or STRONG 检查长度、数字、大小写、特殊字符、字典文件

设置密码强度参数:

sql 复制代码
mysql> show variables like 'validate_password.%';        # 查看策略

mysql> set global validate_password.policy=low;          # 强度等级(或 0)
mysql> set global validate_password.mixed_case_count=0;  # 大小写字母个数
mysql> set global validate_password.number_count=6;      # 数字个数
mysql> set global validate_password.special_char_count=0;# 特殊字符个数
mysql> set global validate_password.length=6;            # 密码长度

mysql> show variables like 'validate_password.%';        # 再次查看
+-------------------------------------------------+-------+
| Variable_name                                   | Value |
+-------------------------------------------------+-------+
| validate_password.changed_characters_percentage | 0     |
| validate_password.check_user_name               | ON    |
| validate_password.dictionary_file               |       |
| validate_password.length                        | 6     |
| validate_password.mixed_case_count              | 0     |
| validate_password.number_count                  | 6     |
| validate_password.policy                        | LOW   |
| validate_password.special_char_count            | 0     |
+-------------------------------------------------+-------+

mysql> flush privileges;        # 刷新权限使其生效

set global 是临时修改,重启 MySQL 后失效。要永久生效需改配置文件:

sql 复制代码
[root@server ~]# vim /etc/my.cnf
# 文末添加:
validate_password.policy=low
validate_password.length=6
validate_password.mixed_case_count=0
validate_password.number_count=6
validate_password.special_char_count=0

[root@server ~]# systemctl restart mysqld

修改密码:

sql 复制代码
mysql> select user, host, plugin from mysql.user;                    # 查看 root 用户信息
mysql> alter user 'root'@'localhost' identified by '123456';         # 改本地 root 密码
mysql> flush privileges;

注意区分主机名:本地是 localhost ,远程是 %。改错 host 会导致改的那个账号密码没变。


3.7.2 登录账户管理

MySQL 8 的账户权限管理原则:先创建账户,再赋予权限

用户信息存放在 mysql 库下的 user 表

sql 复制代码
mysql> select user();                                     # 查看当前用户信息
mysql> use mysql;
mysql> show tables;
mysql> desc user;
mysql> select user,host,authentication_string from user;  # 认证信息(密文)在 authentication_string

3.7.3 新建登录账户

格式:

sql 复制代码
create user 'username'@'localhost' identified by 'password';

含义:

参数 说明
username 创建的用户名
localhost 指定该用户可从哪个主机登录。本地用户用 localhost ;想让其从任意远程主机 登录,用通配符 %
password 该用户的登录密码(须符合密码策略)
sql 复制代码
mysql> create user 'test1'@'localhost' identified by '123456';
Query OK, 0 rows affected (0.01 sec)

mysql> select user, host, plugin from mysql.user;
+------------------+-----------+-----------------------+
| user             | host      | plugin                |
+------------------+-----------+-----------------------+
| test1            | localhost | caching_sha2_password |
| ...              |           |                       |
+------------------+-----------+-----------------------+

mysql> exit
Bye
[root@server ~]# mysql -u test1 -p
Enter password:

新账户默认只有 USAGE 权限(仅能连接登录):

sql 复制代码
mysql> show databases;        # 只能看到有限的库
+--------------------+
| Database           |
+--------------------+
| information_schema |
| performance_schema |
+--------------------+
2 rows in set (0.01 sec)

3.7.4 账户授权

作用:通过账户权限限制普通账户的工作范围及内容

  • 赋权原则:

    1. root 账号绝对禁止 允许任何 IP 访问,即登录方式不能用 %

    2. 应用账号和管理账号要分离

    3. 赋权最小化 :一般只给 select 权限,最好不要给 update、insert 等编辑权限

    4. 密码设置合理

    5. 定期清理不使用的账户,收回权限或删除

常见用户权限:

权限 权限说明 权限级别
CREATE 创建数据库、表、索引 数据库、表、索引
DROP 删除数据库、表 数据库、表
GRANT OPTION 赋予权限 数据库、表
REFERENCES 引用权限 数据库、表
ALTER 更改表 表
DELETE 删除表数据 表
INDEX 操作索引 表
INSERT 添加表数据 表
SELECT 查询表数据 表
UPDATE 更新表数据 表
CREATE VIEW 创建视图 视图
SHOW VIEW 查看视图 视图
ALTER ROUTINE 更改存储过程 存储过程
CREATE ROUTINE 创建存储过程 存储过程
EXECUTE 执行存储过程 存储过程
FILE 服务器主机文件的访问 文件管理
CREATE TEMPORARY TABLES 创建临时表 服务器管理
LOCK TABLES 锁表 服务器管理
CREATE USER 创建用户 服务器管理
RELOAD 执行 flush privileges、refresh、reload 等刷新命令 服务器管理
PROCESS 查看进程 服务器管理
REPLICATION CLIENT 查看主从服务器状态 服务器管理
REPLICATION SLAVE 主从复制 服务器管理
SHOW DATABASES 查看数据库 服务器管理
SHUTDOWN 关闭数据库 服务器管理
SUPER 超级权限 服务器管理
ALL PRIVILEGES 所有权限(不含 grant 权限) ---
USAGE 没有任何权限(仅能连接) ---

查看权限:

sql 复制代码
mysql> show grants\G                            # 查看当前账户权限
mysql> show grants for 'test1'@'localhost';     # 查看指定账户权限
+-------------------------------------------+
| Grants for test1@localhost                |
+-------------------------------------------+
| GRANT USAGE ON *.* TO `test1`@`localhost` |
+-------------------------------------------+
1 row in set (0.00 sec)
# 说明 test1 只有 usage 默认权限,即连接登录的权限

赋权语法:

sql 复制代码
grant 权限列表 on 数据库名.表名 to '用户名'@'来源地址' [identified by '密码'];
参数 说明
权限列表 如 SELECT、INSERT、UPDATE、CREATE;授予全部用 ALL
数据库名 具体库名,* 代表所有数据库
表名 具体表名,* 代表所有表
来源地址 允许登录的主机;本地用 localhost,任意远程用 %

示例 1:创建 test1 账户并赋权

sql 复制代码
mysql> grant select on *.* to 'test1'@'localhost';
Query OK, 0 rows affected (0.01 sec)

mysql> show grants for 'test1'@'localhost';
+--------------------------------------------+
| Grants for test1@localhost                 |
+--------------------------------------------+
| GRANT SELECT ON *.* TO `test1`@`localhost` |
+--------------------------------------------+

mysql> select * from mysql.user where user='test1'\G      # 查看 user 表里的权限位

赋权后 test1 重新登录,show databases 就能看到全部库了(因为给了 *.* 的 SELECT)。

示例 2:test2 账户,赋 select + create,仅允许 10.100.170.0 网段登录

sql 复制代码
mysql> create user 'test2'@'10.100.170.%' identified by '123456';
mysql> show grants for 'test2'@'10.100.170.%';
mysql> grant select,create on *.* to 'test2'@'10.100.170.%';

# 从该网段的机器登录
[root@server ~]# mysql -u test2 -p -h 10.100.170.198
mysql> create database test2db_test;      # 有 create 权限,成功
mysql> show databases;
+--------------------+
| Database           |
+--------------------+
| information_schema |
| mydb1_test         |
| ...                |
| test2db_test       |
+--------------------+

示例 3:test3 赋 ALL,登录后再给 test4 赋权(踩坑演示)

sql 复制代码
mysql> create user 'test3'@'%' identified by '123456';
mysql> grant all on *.* to 'test3'@'%';
mysql> show grants for 'test3'@'%'\G

[root@server ~]# mysql -u test3 -p
mysql> create database mydbx_temp1;                  # 有 all 权限,成功
mysql> create user 'test4'@'%' identified by '123456';
mysql> show grants for 'test4'@'%';

mysql> grant select on *.* to 'test4'@'%';           # 被拒绝!
ERROR 1045 (28000): Access denied for user 'test3'@'%' (using password: YES)

注意:``ALL 里不包含"给别人赋权"的权限 GRANT OPTION。 解决办法:由 root 授予 with grant option

sql 复制代码
[root@server ~]# mysql -u root -p
# 给 root 添加系统管理员特权
mysql> grant system_user on *.* to 'root'@'%';
# 给 test3 添加赋权能力
mysql> grant all on *.* to 'test3'@'%' with grant option;
[root@server ~]# mysql -u test3 -p
mysql> grant select on *.* to 'test4'@'%';           # 这次成功
Query OK, 0 rows affected (0.01 sec)

3.7.5 回收权限

格式:

sql 复制代码
revoke 权限列表/all on 库名.表名 from '用户名'@'来源地址';

revoke 语法与 grant 几乎一样,只需把关键字 to 换成 from。

sql 复制代码
mysql> select user, host, plugin, select_priv from mysql.user;   # select_priv 查权限位
mysql> show grants for 'test4'@'%';

mysql> revoke select on *.* from 'test4'@'%';          # 回收单个权限
mysql> show grants for 'test4'@'%';

mysql> revoke all on *.* from 'test3'@'%';             # 回收所有权限
mysql> revoke all on *.* from 'test2'@'10.100.170.%';
mysql> revoke all on *.* from 'test1'@'localhost';

只 revoke all 后账户仍然存在(还剩 USAGE,即能连不能干),要彻底清掉得用 drop user。


3.7.6 删除账户

格式:

sql 复制代码
drop user '用户名'@'访问主机名';
sql 复制代码
mysql> select user, host from mysql.user;
mysql> drop user 'test1'@'localhost';
mysql> drop user 'test2'@'10.100.170.%';
mysql> drop user test3;          # 注意:主机为 % 时可以不写
mysql> drop user test4;

mysql> select user, host from mysql.user;
+------------------+-----------+
| user             | host      |
+------------------+-----------+
| mysql.infoschema | localhost |
| mysql.session    | localhost |
| mysql.sys        | localhost |
| root             | localhost |
+------------------+-----------+
4 rows in set (0.00 sec)

剩下这 4 个是系统内置账户,不能删。


命令速查

库操作

sql 复制代码
create database 库名;                 -- 建库
show databases [like '通配'];         -- 看库
use 库名;                             -- 切库
select database();                    -- 当前库
show create database 库名;            -- 建库语句
drop database 库名;                   -- 删库

字符集

sql 复制代码
show character set [like 'x%'];
show collation;
show variables like 'character_set%';

表结构(DDL)

sql 复制代码
create table 表(列 类型 [约束] comment '说明', ...) [engine=..] [charset=..];
desc 表名;  /  show columns from 表名;
show tables [from 库名] [like 'x%'];
show create table 表名\G;
alter table 表 rename 新表名;  /  rename table 表 to 新表名;
alter table 表 add 列 类型 [after 列|first];
alter table 表 drop 列;
alter table 表 change 旧列 新列 类型;
alter table 表 modify 列 类型 [after 列|first];
create table 新 like 源;              -- 只复制结构
create table 新 select * from 源;     -- 结构 + 数据
insert into 新 select * from 源;      -- 只复制数据
drop table [if exists] 表名;

约束

sql 复制代码
-- 建表时
列 类型 not null / default 值 / unique / primary key / auto_increment / check(条件)
constraint 约束名 约束类型(列)
foreign key(从表列) references 主表(主表列)

-- 事后增删
alter table 表 add primary key(列);
alter table 表 add constraint 名 unique(列);
alter table 表 add foreign key(列) references 主表(列);
alter table 表 add constraint 名 check(条件);
alter table 表 drop primary key;
alter table 表 drop index 唯一约束名;
alter table 表 drop foreign key 外键名;
alter table 表 drop check 约束名;
alter table 表 modify 列 类型;         -- 删 not null / default / auto_increment

账户与权限(DCL)

sql 复制代码
create user 'u'@'host' identified by 'pwd';
alter user 'u'@'host' identified by '新密码';
show grants for 'u'@'host';
grant 权限 on 库.表 to 'u'@'host' [with grant option];
revoke 权限 on 库.表 from 'u'@'host';
drop user 'u'@'host';
flush privileges;
install component 'file://component_validate_password';

注意易错点:

  1. show databases 有 s;create/show create/drop database 没有 s

  2. -p 与密码之间不能有空格 ;\c 是清除当前输入,不是清屏

  3. 建库/建表前未 use → No database selected

  4. 反引号只对保留字/特殊字符必需,但建议统一加

  5. utf8 = utf8mb3(3 字节),存 emoji 必须 utf8mb4

  6. int(N) 的 N 只是显示宽度,不改变 4 字节占用;8.0.17 后不推荐

  7. 金额字段用 decimal,不要 用 float/double(近似值)

  8. add 只有 after / first,没有 before

  9. change 改列名必须带上新类型 ;modify 只改类型/位置

  10. create table ... like 只复制结构;create table ... select 带数据但不复制索引/约束

  11. 表级约束不能直接改,只能删了重建

  12. NULL、空字符串 ''、0 是三个不同的东西

  13. 一张表只能一个主键、一个自增列 ;但唯一键可以有多个

  14. 唯一键允许 NULL,主键不允许;唯一键报错也会消耗自增值(出现断号)

  15. 自增列不保证连续,不能当业务编号用

  16. 外键定义在从表 ,主表被引用的列必须有主键/唯一键索引(否则 errno 150)

  17. 外键可以为 NULL,主键不可以

  18. check 在 8.0.16 之前不生效(只写不校验)

  19. ALL 权限里不含 GRANT OPTION ,要赋权必须显式 with grant option

  20. set global 改密码策略重启即失效 ,永久生效要写 /etc/my.cnf

  21. root 账号禁止 用 % 登录;应用账号与管理账号要分离

  22. revoke all 后账户仍然存在 (仅剩 USAGE),要删得用 drop user

相关推荐
时间的拾荒人1 小时前
Redis 进阶实战:渐进式遍历、持久化机制与事务详解
数据库·redis·缓存
普通人62 小时前
MySQL_FIND_IN_SET函数详解
android·数据库·mysql
真上帝的左手2 小时前
4. 关系型数据库-MySQL-Canal 进阶
数据库·mysql·canal
xiaoye-duck2 小时前
《C++ 项目:手写大模型接入 SDK》DataManager数据管理模块:SQLite持久化存储
开发语言·数据库·c++·sqlite
codeejun2 小时前
每日一Go·MySQL-14、慢 SQL 产生原因全解
sql·mysql·golang
唐老板3 小时前
uuidv7 来了:为什么别再拿 uuidv4 当主键了
数据库
SelectDB16 小时前
让变更流出 Doris:Apache Doris 5.0 基于增量计算的 CDC 技术解析
数据库
这个DBA有点耶16 小时前
MCP隔离层+行级语义审计+输出校验:AI访问数据库的安全边界拆解
数据库·人工智能·ai编程