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 也算进去,否则不算 |
字符序命名规则:
-
以对应的字符集名称开头 (如
utf8mb4_...) -
中间是国家名或
general -
后缀表示特性:
| 后缀 | 含义 |
|---|---|
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 表名;
删除表前的注意事项:
-
删除前一定备份数据,以免数据丢失
-
确认该表没有被其他表引用(外键约束)
-
确认数据已备份且不再需要
-
确认表数据已清空,否则破坏外键约束
-
确认相关索引已处理,残留索引可能导致删除失败
-
确认相关程序已停止运行,避免影响其他程序
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 开始递增
-
被自增长的字段必须是主键或其他具有唯一性的键
-
自增长字段也可以手动插入数据,只要不与已有数据重复即可,之后新增数据从最大值继续递增
-
任何字段要做自增长,前提是本身是一个索引 (
desc里 Key 一栏有值) -
一张表最多只能有一个自增长列
-
约束的字段必须具备 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(表示尚未分配)
外键本质上是给表与表之间增加了一层约束关系,插入时自动校验
注意:
-
主表必须已存在于数据库中,或就是当前正在创建的表(后者称自参照表 ,这种结构叫自参照完整性)
-
必须为主表定义主键
-
主键不能包含空值 ,但允许外键出现空值;只要外键的每个非空值都能在主键中找到,外键内容就是正确的
-
在主表表名后指定的列(或列组合)必须是主表的主键或候选键
-
外键中列的数目必须和主表主键中列的数目相同(联合主键场景)
-
外键中列的数据类型必须和主表主键中对应列的数据类型相同
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 账户授权
作用:通过账户权限限制普通账户的工作范围及内容
-
赋权原则:
-
root 账号绝对禁止 允许任何 IP 访问,即登录方式不能用
% -
应用账号和管理账号要分离
-
赋权最小化 :一般只给
select权限,最好不要给update、insert等编辑权限 -
密码设置合理
-
定期清理不使用的账户,收回权限或删除
-
常见用户权限:
| 权限 | 权限说明 | 权限级别 |
|---|---|---|
| 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';
注意易错点:
-
show databases有 s;create/show create/drop database没有 s -
-p与密码之间不能有空格 ;\c是清除当前输入,不是清屏 -
建库/建表前未
use→No database selected -
反引号只对保留字/特殊字符必需,但建议统一加
-
utf8=utf8mb3(3 字节),存 emoji 必须utf8mb4 -
int(N)的 N 只是显示宽度,不改变 4 字节占用;8.0.17 后不推荐 -
金额字段用
decimal,不要 用float/double(近似值) -
add只有after/first,没有 before -
change改列名必须带上新类型 ;modify只改类型/位置 -
create table ... like只复制结构;create table ... select带数据但不复制索引/约束 -
表级约束不能直接改,只能删了重建
-
NULL、空字符串''、0是三个不同的东西 -
一张表只能一个主键、一个自增列 ;但唯一键可以有多个
-
唯一键允许 NULL,主键不允许;唯一键报错也会消耗自增值(出现断号)
-
自增列不保证连续,不能当业务编号用
-
外键定义在从表 ,主表被引用的列必须有主键/唯一键索引(否则 errno 150)
-
外键可以为 NULL,主键不可以
-
check在 8.0.16 之前不生效(只写不校验) -
ALL权限里不含GRANT OPTION,要赋权必须显式with grant option -
set global改密码策略重启即失效 ,永久生效要写/etc/my.cnf -
root 账号禁止 用
%登录;应用账号与管理账号要分离 -
revoke all后账户仍然存在 (仅剩 USAGE),要删得用drop user