概述
本章,您将学习 MySQL 中有关 DDL 的知识。
DDL(Data Definition Language,数据定义语言),主要指的是定义库和表。
DDL 的内容包括:
- 管理库
-
管理表
- 数据类型
- 约束
- 字段的自增长
管理库
创建库
语法为:
create database 库名;
# 有时库比较多,也会添加条件判断,即如果库不存在,则创建对应的库
create database if not exists 库名;
示例:
create database home;
show databases;
每当我们创建一个库,在对应的数据目录中就会生成同名的目录。在我的环境中,MySQL 的数据目录为 /usr/local/mysql8/data/
:
Shell > ls -ld /usr/local/mysql8/data/home/
drwxr-x--- 2 mysql mysql 4096 Aug 22 10:58 /usr/local/mysql8/data/home/
关于数据目录里文件的含义与说明,我们后面会有专门的篇幅来介绍,现有的基础知识还不能够驾驭它。
修改库
通常,我们都不建议在生产环境下直接修改库,这会导致各种各样的问题。一个库创建之后,我们能修改的只有它的字符集以及排序规则,默认情况下,库的字符集为 utf8mb4,库的排序规则为 utf8mb4_0900_ai_ci
show variables like 'character_set_da%';
+------------------------+---------+
| Variable_name | Value |
+------------------------+---------+
| character_set_database | utf8mb4 |
+------------------------+---------+
1 row in set (0.00 sec)
show variables like 'collation_data%';
+--------------------+--------------------+
| Variable_name | Value |
+--------------------+--------------------+
| collation_database | utf8mb4_0900_ai_ci |
+--------------------+--------------------+
1 row in set (0.00 sec)
需要注意的是,这两个系统变量会在未来的版本中被删除。
若您需要显式定义字符集与排序规则,可将下面的内容写入到配置文件中(/etc/my.cnf
):
...
[mysqld]
...
character-set-server = utf8mb4
collation-server = utf8mb4_0900_ai_ci
...
修改已存在库的字符集与排序规则:
alter database 库名 character set 字符集 collate 排序规则;
Q:有哪些字符集与排序规则可选?
执行以下语句进行查询:
show collation;
show collation like 'utf8mb4%';
show collation where Charset='utf8mb4';
删除库
语法为:
drop database 库名;
# 同样也可以添加条件判断。当库存在时,则删除
drop database if exists 库名;
查看库
# 查看所有的库
show databases;
# 查看单个库的创建语法
show create database 库名;
表管理
创建表
语法为:
create table 表名(
字段名1 数据类型(属性) 约束类型,
字段名2 数据类型(属性) 约束类型,
字段名3 数据类型(属性) 约束类型,
...
字段名n 数据类型(属性) 约束类型
);
- 对于单张表而言,字段名称与数据类型必须定义,而约束类型则是可选的
- 在创建表时也可以添加条件判断,例如当同名的表不存在时则创建
create table if not exists 表名(...);
上篇 DML 我们定义了 home 这个库,来试试创建一个简单的成员表:
use home;
create table if not exists member(
ID int,
name varchar(10),
gender char(5),
age int
);
插入一行数据:
insert into member values(1,'frank','male',20);
查看表
# 查看表的结构
desc 表名;
# 查阅表的创建语法
show create table 表名;
# 查看当前库下的所有表
show tables from 库名;
删除表
drop table 表名;
drop table if exists 表名;
修改表
可执行的操作有:
-
对已有字段的重命名
alter table 表名 change 已有字段名 重命名的字段名 数据类型;
-
修改已有字段的数据类型或约束
alter table 表名 modify 已有字段名 数据类型;
-
添加新字段
alter table 表名 add 新字段名 数据类型;
- 也可以使用
first
或after
关键字来调整新增字段的位置,语法为alter table 表名 add 新字段名 数据类型 after 已有字段名;
-
删除已有字段
alter table 表名 drop 字段名;
-
修改表名
alter table 表名 rename to 新表名;
如您所见,修改表的所有操作都属于 alter table
语法大类,更多请参阅 这里。
创建新表时从已有表复制
# 创建新表时,复制已有表的所有字段结构
create table 新表名 like 已有表名;
# 创建新表时,只复制已有表的部分字段结构
create table 新表名 select 字段1,字段2... from 已有表名 where 1=0;
# 创建新表时,复制已有表的所有字段结构以及数据
create table 新表名 select * from 已有表名;
# 创建新表时,复制已有表的部分字段结构以及部分数据
create table 新表名 select 字段1,字段2... from 已有表名 where 筛选条件;
数据类型
数据类型可划分为几个大类:
- 数值数据类型
- 日期和时间数据类型
- 字符串数据类型
- 空间数据类型
- json 数据类型
数值数据类型
具体细分为:
- 整数型
- 定点型(精准值)
- 浮点型(近似值)
- 位值型(bit-value)
整数型
整数型参阅下表:
关键字 | 大小 | 有符号范围 | 无符号范围 | 用途 |
---|---|---|---|---|
tinyint |
1 字节 | [-128,127] | [0,255] | 小整数型 |
samllint |
2 字节 | [-32768,32767] | [0,65535] | 大整数型 |
mediumint |
3 字节 | [-8388608,8388607] | [0,16777215] | 大整数型 |
int |
4 字节 | [-2147483648,2147483647] | [0,4294967295] | 大整数型 |
bigint |
8 字节 | [-9223372036854775808,9223372036854775807] | [0,18446744073709551615] | 极大整数型 |
对于整数型,显示宽度 属性已经被弃用,而且在未来的版本中会被删除。另外需要注意的是,显示宽度与所存储的值范围 无关,例如 int(5)
中的 5 表示显示宽度,但并不能改变 int 所存储的值范围。默认情况下,整数型使用有符号,除非您另外指定了 unsigned
关键字,这表示使用无符号。
Q:什么是有符号与无符号?
- 有符号:包含正号(
+
)、负号(-
)和 0,换言之,可以表示正数、负数和零 - 无符号:仅包含零和正数
Q:无符号的字段插入负数数据,会出现什么情况?
输出错误提示文本且无法插入数据。来看具体的例子:
use home;
create table if not exists jobs(
ID int unsigned,
NAME char(10)
);
insert into jobs values(-100,'IT');
Error: Out of range value for column 'ID' at row 1
# 无数据输出
select * from jobs;
Q:如果插入的数据超过了数据类型的范围,会出现什么?
输出错误提示文本且无法插入数据。来看具体的例子:
use home;
insert into jobs values(5000000000,'IT');
Error: Out of range value for column 'ID' at row 1
定点型
定点型(精准值)参阅下表:
关键字 | 大小 | 有符号范围 | 无符号范围 | 用途 |
---|---|---|---|---|
decimal(M,D) |
取决于整数部分与小数部分的个数 | 取决于 M 和 D | 取决于 M 和 D | 货币等高精准度 |
其中 M 表示总的有效位数,D 表示小数点后面有多少位,例如 decimal(5,2)
表示能够存储任何有效五位数以及两位小数的值,其范围为 [-999.99,999.99]。若 D 为 0,则表示所存储的值不包含小数点以及小数点部分。未指定 (M,D)
,则 M 为 10,D 为 0 。M 的范围为 [1,65],D 的范围为 [0,30] 且不得大于 M 的值。
MySQL 中,若使用 decimal(M,D)
,则 MySQL 会使用二进制格式进行存储。整数部分,每 4 字节存储 9 个十进制数字,十进制个数不足 9 倍数的,按照余数进行处理;小数部分,每 4 字节存储 9 个十进制数字,十进制个数不足 9 倍数的,按照余数进行处理。decimal 占用的总大小需要整数部分的大小加上小数部分的大小。
余数 | 大小 |
---|---|
0 | 0 字节 |
1-2 | 1 字节 |
3-4 | 2 字节 |
5-6 | 3 字节 |
7-9 | 4 字节 |
例如 decimal(18,9)
中,整数部分与小数部分都各占用 4 字节,共占用 8 字节。DECIMAL(20,6)
的整数部分有 14 个十进制数字,则占用 4 + 3 = 7 字节;小数部分有 6 个十进制数字,占用 3 字节,所以一共占用 10 字节。例如 decimal(5,3)
,整数部分是 2 个十进制数字,占用 1 字节;小数部分是 3 个十进制数字,占用 2 字节,共占用 3 字节。
浮点型
浮点型(近似值)参阅下表:
关键字 | 大小 | 用途 |
---|---|---|
float(p) |
4 字节(p 取值在 [0,23] 范围时) 或 8 字节(p 取值在 [24,53] 时) | 温度、重量、价格等 |
p 值在 [0,23] 时,有符号的取值范围:
- 负数区间 – [-3.402823466E+38,-1.175494351E-38]
- 0
- 正数区间 – [1.175494351E-38,3.402823466E+38]
p 值在 [0,23] 时,无符号的取值范围:
- 0
- 正数区间 – [1.175494351E-38,3.402823466E+38]
p 值在 [24,53] 时,有符号的取值范围:
- 负数区间 – [-1.7976931348623157E+308,-2.2250738585072014E-308]
- 0
- 正数区间 – [2.2250738585072014E-308,1.7976931348623157E+308]
p 值在 [24,53] 时,无符号的取值范围:
- 0
- 正数区间 – [2.2250738585072014E-308,1.7976931348623157E+308]
E 表示指数符号,即以 10 为底数的幂运算,例如 -3.402823466E+38 表示 -3.402823466 x 1038。p 值指定以 bit 为单位的可选精度,仅影响存储大小。当 p 值大于 23 时,即旧版本的 double
数据类型。从 8.0.17 版本开始,非标准的 float(M,D)
和 double(M,D)
表示形式已经被弃用且在未来的版本中不再受支持。
看下面的例子:
# 在 jobs 表中新增字段,字段位置在 name 字段的后面,字段的数据类型为浮点型
alter table jobs add price float(25) after name;
# 查看表结构 —— price 字段为旧的 double 数据类型
desc jobs;
+-------+--------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+-------+--------------+------+-----+---------+-------+
| ID | int unsigned | YES | | NULL | |
| NAME | char(10) | YES | | NULL | |
| price | double | YES | | NULL | |
+-------+--------------+------+-----+---------+-------+
3 rows in set (0.00 sec)
位值型
位值型主要用来存储位(Bit)的值,每个位仅能用 0 或 1 来表示。
关键字 | 大小 | 用途 |
---|---|---|
bit(M) |
众所周知,8 Bit 等于 1 字节。在 MySQL 中,每 8 Bit 占用 1 字节,不足 8 Bit 则仍然占用 1 字节 | 权限开关、功能的启用与关闭、考勤是否打卡等 |
M 用来表示位数,其范围为 [1,64]。当 M 为 64 时,表示最大占用存储大小为 4 字节。
例如 bit(8)
表示有 8 个位,能表示的取值范围为 [0,2^8 -1],即 [0,255]。例如 bit[9]
,其占用 2 字节大小但浪费了 7 Bit
# 在 price 价格字段后面再加一个权限字段 permission
alter table jobs add `permission` bit(4) after price;
# 插入新的行数据
insert into jobs values(2,'Tom',20.35,b'1100');
# 查询
select * from jobs;
ID NAME price permission
2 Tom 20.35 1100
# 将该行数据的权限字段更新为 1110
update jobs set permission=b'1110' where id=2;
日期和时间数据类型
DATE、DATETIME 和 TIMESTAMP 类型
关键字 | 值格式 | 说明 | 大小 |
---|---|---|---|
date |
YYYY-MM-DD |
无 | 3 字节 |
datetime |
YYYY-MM-DD hh:mm:ss |
最大支持微妙级别的精度,需要在关键字后面的括号中指定,最大为数字 6 。例如指定小数点 2 位精度 datetime(2) |
8 字节 |
timestamp |
YYYY-MM-DD hh:mm:ss |
最大支持微妙级别的精度,需要在关键字后面的括号中指定,最大为数字 6 。例如指定小数点 6 位精度 timestamp(6) |
4 字节 |
DATE 类型:该类型用于包含日期部分但不包含时间部分的值,值的标准格式为 'YYYY-MM-DD'
,范围为 ‘1000-01-01’ 到 ‘9999-12-31’
DATETIME 类型:该类型用于包含日期部分和时间部分的值,值的标准格式为 YYYY-MM-DD hh:mm:ss
,范围为 ‘1000-01-01 00:00:00’ 到 ‘9999-12-31 23:59:59’
TIMESTAMP 类型:该类型用于包含日期部分和时间部分的值,值的标准格式为 YYYY-MM-DD hh:mm:ss
,但范围为 ‘1970-01-01 00:00:01’ UTC 到 ‘2038-01-19 03:14:07’ UTC。存储时,会将当前时区转换为 UTC,查询时再转换为当前时区。
alter table jobs add create_time datetime after `permission`;
update jobs set create_time='2025-08-25 19:22:30' where id=2;
select * from jobs;
ID NAME PRICE PERMISSION CREATE_TIME
2 Tom 20.35 1110 2025-08-25 19:22:30
TIME 类型
关键字 | 值格式 | 说明 | 大小 |
---|---|---|---|
time(N) |
hh:mm:ss 或 hhh:mm:ss |
N 用来指定可选的秒精度,支持微妙级别的精度,即最大为数字 6 | 3 字节 |
TIME 类型不仅可以用于表示一天中的某个时间(24小时制),还可以表示两个事件之间的时间间隔(换言之,hh
可以远大于 24 且还允许负值)。TIME 以 hh:mm:ss
或 hhh:mm:ss
格式检索和显示值,时间值的范围为 ‘-838:59:59’ 到 ‘838:59:59’。
TIME 类型支持多种时间格式,限于篇幅,建议 TIME 类型的值应该使用推荐的 hh:mm:ss
或 hhh:mm:ss
格式。
另外请注意,TIME 类型支持存储微秒精度的时间(也就是整数秒后面加小数点和 6 位数字),例如 19:43:50.236545
YEAR 类型
用来表示年份的类型,格式为 YYYY
,范围为 1901
到 2155
。占用 1 字节。
字符串数据类型
字符型
包含 char
、varchar
关键字,如下表的说明:
关键字 | 说明 | 大小 |
---|---|---|
char(N) |
固定长度固定大小的非二进制字符串。N 表示最大可以容纳多少长度的字符,最大为 255。例如 CHAR(20) 表示最多可以容纳 20 个字符 | utf8mb4 字符集中,每个字符(包括中文汉字)占用 4 字节。例如 char(10) ,无论值的有效长度是多少,都固定分配 40 字节。 |
varchar(N) |
可变长度动态分配大小的非二进制字符串,N 表示最大可以容纳多少长度的字符,最大为 65535 | 实际占用空间=实际字符数×每个字符的字节数+长度标识字节。在 utf8mb4 字符集中,每个字符占用 1-4 字节(英文 1 字节,常见汉字 3 字节,罕见汉字以及生僻字占用 4 字节)。例如 varchar(100) 可以最大存储 100 个英文字符或 33 个常见汉字。长度标识字节依据实际的值长度,若长度小于等于 255 字符,则分配 1 字节存储长度信息;若长度大于 255 字符,则分配 2 字节存储长度信息 |
注意!在早期的 MySQL 版本中(5.0 之前的版本),varchar(N)
中的 N 表示字节数。
二进制型
包含 binary
和 varbinary
关键字,它们与 char
和 varchar
类似,只不过它们存储的是二进制字符串。
关键字 | 说明 | 大小 |
---|---|---|
binary(N) |
固定长度固定大小的二进制字符串。N 指的是字节长度,其范围为 0-255 | 0 – 255 字节,存储时会用 0x00 填充至指定长度 |
varbinary(N) |
可变长度动态分配大小的二进制字符串。N 指的是字节长度,其范围为 0-65535 | 实际占用空间=数据实际字节长度 + 长度标识字节(1 或 2 字节) |
blob 是一种用来存储二进制大对象(音频、视频、图片等)的数据类型,划分为几个子类:
关键字 | 说明 | 大小 |
---|---|---|
tinyblob |
不超过 255 个字符的二进制 | 占用大小=实际的长度 + 1 |
blob |
不超过 65535 个字符的二进制 | 占用大小=实际的长度 + 2 |
mediumblob |
不超过 16777215 个字符的二进制 | 占用大小=实际的长度 + 3 |
longblob |
不超过 4294967295 个字符的二进制 | 占用大小=实际的长度 + 4 |
文本型
划分为几个子类:
关键字 | 说明 | 大小 |
---|---|---|
tinytext |
不超过 255 个字符的文本 | 占用大小=实际的长度 + 1 |
text |
不超过 65535 个字符的文本 | 占用大小=实际的长度 + 2 |
mediumtext |
不超过 16777215 个字符的文本 | 占用大小=实际的长度 + 3 |
longtext |
不超过 4294967295 个字符的文本 | 占用大小=实际的长度 + 4 |
枚举型
所谓枚举型,即插入的数据必须属于列表的其中一个成员。
关键字 | 说明 | 大小 |
---|---|---|
enum('value1','value2','value3'...) |
最多可以有 65535 个不同的成员 | 1 字节(成员数量小于等于 255 个时) 或 2 字节(成员数量大于 256 且小于等于 65535) |
来看这样的一个例子:
use home;
CREATE TABLE shirts (
name VARCHAR(40),
size ENUM('x-small', 'small', 'medium', 'large', 'x-large')
);
insert into shirts values('dress shirt','large'), ('t-shirt','medium'),('polo shirt','small');
select * from shirts;
+-------------+--------+
| name | size |
+-------------+--------+
| dress shirt | large |
| t-shirt | medium |
| polo shirt | small |
+-------------+--------+
3 rows in set (0.00 sec)
注意点:
- 必须使用引号(单引号或双引号)包含枚举值,每个枚举值使用逗号进行分隔
- 每个枚举值都会分配一个索引号,从 1 开始记
- 虽然枚举值允许空字符(
''
) 和 NULL 值,但空字符的索引号为 0 ,NULL 值的索引号为 NULL - 枚举值不能重复
- 强烈建议不要使用数字作为枚举值
比如:
# 这里的 2 指的是成员在列表中的索引位置
select * from shirts where size=2;
+------------+-------+
| name | size |
+------------+-------+
| polo shirt | small |
+------------+-------+
1 row in set (0.00 sec)
比如可以查询分配了无效枚举值的行数据:
select * from shirts where size=0;
集合型
使用 set
关键字包含一个集合中的一个或多个成员,每个成员值必须使用引号包含且使用逗号进行分隔。插入数据时,必须从定义的成员中选择 0 个或多个,比如在创建表时使用了 set('one','two') not null
,则插入值时允许有:
- ‘ ‘
- ‘one’
- ‘two’
- ‘one,two’
关键字 | 说明 | 大小 |
---|---|---|
set('value1','value2','value3'...) |
最多可以有 64 个不同的成员。 插入值时成员的排列顺序和出现次数都不重要,稍后检索时,每个成员都只会出现一次,看下面的例子。 | 1 字节 (成员数 1-8 时)或 2 字节(成员数 9-16 时) 或 3 字节(成员数 17-24 时) 或 4 字节(成员数 25-32 时) 或 8 字节(成员数 33-64 时) |
use home;
create table if not exists myset(
test set ('a','b','c','d')
);
insert into myset values('a,d'),('d,a'),('a,d,a'),('a,d,d'),('d,a,d');
select * from myset;
+------+
| test |
+------+
| a,d |
| a,d |
| a,d |
| a,d |
| a,d |
+------+
5 rows in set (0.00 sec)
空间数据类型
这类数据类型允许存储地理空间数据,如点、线、多边形等,主要用于 GIS(地理信息系统) 应用。由于内容较多,这一类特殊的数据类型后续再单独介绍。
json 数据类型
主要用来储存 JSON 文档。JSON 是一种半结构化数据,既可以存储结构化数据,也可以存储非结构化数据。
一提到文档型数据库,你肯定听说过 ELK (Elasticsearch、Logstash 和 Kibana),由于后续组件越来越多,便改名为 Elastic Stack。这一套集成搜索引擎、分析引擎、数据可视化、数据收集的解决方案全部建立在 JSON 文档上。
这一部分的内容较多,后面再单独介绍。
约束
MySQL 中的约束:一种对表中数据进行限制的规则,用来保证数据的规范、可靠、合法、有效和正确。
主要有 6 大类约束类型,如下表所示:
约束类型 | 关键字 | 说明 |
---|---|---|
非空值约束 | not null |
插入的数据不能留空 |
默认值约束 | default |
插入的数据有默认值 |
主键约束 | primary key |
插入的数据允许一个空字符、非空值且唯一 |
唯一约束 | unique |
插入的数据具有唯一性。只能有一个空字符,可允许多个 null |
检查约束 | check |
检查插入的数据是否有效 |
外键约束 | foreign key |
与主键约束一起搭配使用,用来限制两张表的数据关系 |
有些资料会提到 MySQL 不支持检查约束,但其实从 8.0.16 版本开始,MySQL 就已经开始支持检查约束。
请注意!空字符(''
,引号中间没有空格) 和空值(null
) 是两个不同的概念。另外,主键、外键、唯一约束都会自动生成索引(一种提高数据查询效率的数据结构,其将数据重新排序,使磁盘上的扇区更加地集中,减少 IO 次数的同时可最大程度提高查询速度)。
约束的级别范围有两个:
- 列级约束 – 直接在字段定义后声明,与字段类型同级。只能作用于单个列且不能自定义约束名。
- 表级约束 – 在所有字段定义完成后单独声明。不支持非空值约束和默认值约束。可作用于单列或多列组合,支持自定义约束名。
create table if not exists t1(
id int not null,
localtion varchar(20),
...
表级约束
);
主键约束和外键约束示例
先创建主表,然后创建从表:
use home;
# 创建主表,专业表,包含专业 ID 号以及专业名称
create table if not exists major (
id int primary key,
majorname varchar(30)
);
# 创建从表,学生信息表,包含学生的学号、姓名、性别、年龄和专业id
create table if not exists stuinfo(
id int unsigned primary key ,
stuname char(20) not null,
gender char(4) check(gender='男' or gender='女'),
age tinyint unsigned default 18,
majorid int,
foreign key (majorid) references `major`(id)
);
插入数据,先在主表插入数据,然后在从表插入数据:
insert into major values(1,'建筑工程'),(2,'计算机软件工程');
insert into stuinfo values(2025031000,'吴','男',20,2);
查询:
select
b.stuname,
a.id,
a.majorname
from
major as a
inner join stuinfo as b on a.id = b.majorid
where
b.id = 2025031000;
# 输出为:
+---------+----+-----------------------+
| stuname | id | majorname |
+---------+----+-----------------------+
| 吴 | 2 | 计算机软件工程 |
+---------+----+-----------------------+
1 row in set (0.00 sec)
删除表时,先删除从表,再删除主表:
drop table if exists stuinfo;
drop table if exists major;
表级约束
该级别的约束对非空约束和默认值约束 不支持 。
来看这样的例子:
use home;
create table if not exists major1(
id int unsigned primary key,
majorname varchar(25)
);
create table if not exists stuinfo1(
id int unsigned,
stuname char(20),
gender char(2),
age tinyint unsigned,
majorid int unsigned,
constraint `pk_id` primary key(id),
constraint `ck_gender` check(gender='男' or gender='女'),
constraint `fk_stuinfo1_major1` foreign key(majorid) references `major1`(id)
);
stuinfo1 表的说明:
- 为了避免和关键字冲突,自定义的约束名建议用反引号包含
- 需要在所有字段定义完成后再单独声明表级约束且从
constraint
关键字开始定义,如上面所示,这里有三个表级约束 - 自定义约束名需要直观,如上所示
- 表级约束中,主键约束的名称不能被自定义,强制命名为
PRIMARY
,见下面的示例
show index from stuinfo1\G;
*************************** 1. row ***************************
Table: stuinfo1
Non_unique: 0
Key_name: PRIMARY ←← 这里
Seq_in_index: 1
Column_name: id
Collation: A
Cardinality: 0
Sub_part: NULL
Packed: NULL
Null:
Index_type: BTREE
Comment:
Index_comment:
Visible: YES
Expression: NULL
*************************** 2. row ***************************
Table: stuinfo1
Non_unique: 1
Key_name: fk_stuinfo1_major1
Seq_in_index: 1
Column_name: majorid
Collation: A
Cardinality: 0
Sub_part: NULL
Packed: NULL
Null: YES
Index_type: BTREE
Comment:
Index_comment:
Visible: YES
Expression: NULL
2 rows in set (0.00 sec)
Q:列级约束与表级约束的区别有哪些?
- 声明的位置不同
- 支持的约束类型不同
- 只有表级约束才能自定义约束名(表级约束中,主键约束的名称被强制定义为
PRIMARY
) -
列级约束只能作用于单个字段,但表级约束可以作用于单个或多个字段,例如:
create table if not exists stuinfo2( ... constraint
pk_id_stuname
primary key(id,stuname) );我们把这种称为 复合主键 或 组合主键。
约束 Q & A
Q:若使用唯一约束,插入的数据是否只能有一个空字符且可允许多个 null?
是的(在默认的 InnoDB 存储引擎是这样的,其他存储引擎未测试)。
use home;
create table if not exists t2(
id int unsigned primary key,
name char(10) unique
);
insert into t2 values(1,null),(2,null),(3,null);
# 符合表述
select * from t2;
+----+------+
| id | name |
+----+------+
| 1 | NULL |
| 2 | NULL |
| 3 | NULL |
+----+------+
3 rows in set (0.00 sec)
# 未报错
insert into t2 values(4,'');
# 报错
insert into t2 values(5,'');
# 符合表述
select * from t2;
+----+------+
| id | name |
+----+------+
| 1 | NULL |
| 2 | NULL |
| 3 | NULL |
| 4 | |
+----+------+
4 rows in set (0.00 sec)
在 MySQL 中,null 表示未知值,既然是未知值(这些 null 数据可能相等,也可能不相等),那么存在多个 null 不违反唯一性。
Q:主键约束是否要求插入的数据只能有一个空字符、非空值且唯一?
是的。见下面的例子:
# 不允许 null,插入数据失败
insert into t2 values(null,'frank');
# 要求值的唯一性,插入数据失败
insert into t2 values(4,'jack');
# 若主键是字符串类型,则允许一个空字符
create table if not exists t3(
xname char(20) primary key
);
## 首次插入空字符成功。若再次插入空字符,则违反唯一性
insert into t3 values('');
Q:空字符和 null 在使用时应该注意哪些?
-
空字符与 null 并不是包含关系,这就导致了在查询时用到的关键字不一样
# 查询空字符 select * from 表名 where 字段=''; # 查询 null select * from 表名 where 字段 is null;
-
聚合函数(sum() 函数、avg() 函数、max() 函数、min() 函数、count() 函数)会忽略 null 值但 并不忽略 空字符
-
在 DQL 中,若条件表达式为不等于(
<>
),则空字符和 null 都会被忽略 -
若字段的约束为
not null
,则表示该列可以插入空字符数据,如下:create table if not exists t4( test char(20) not null ); # 插入数据成功 insert into t4 values('');
管理约束
-
not null
约束为现有字段添加
not null
约束:alter table 表名 modify 现有字段 旧数据类型或新数据类型 not null;
删除已有的
not null
约束:alter table 表名 modify 现有字段 旧数据类型或新数据类型;
-
primary key
约束在现有字段添加列级主键:
alter table 表名 modify 现有字段 旧数据类型或新数据类型 primary key;
删除已有的
primary key
约束:alter table 表名 drop primary key;
-
unique
约束在现有字段添加
unique
约束:alter table 表名 add unique(现有字段名);
删除已有的
unique
约束:# 查询 key_name 列的输出 show index from 表名; # 删除对应的 unique 约束 alter table 表名 drop index 对应的key_name列输出;
-
check
约束在现有字段添加
check
约束:alter table 表名 add check(检查条件);
删除已有的
check
约束:# 查询表的创建语句。检查约束会自动生成约束名 show create table 表名; # 将 check 约束删除 alter table 表名 drop constraint 自动生成的约束名;
字段的自增长
功能类似 Excel 中的填充序列。需要使用到关键字 auto_increment
,比如:
use home;
create table if not exists autoincre(
id int unsigned primary key auto_increment
);
注意事项:
- 自增长除了可以和主键约束搭配,也可以与唯一约束搭配
- 单张表只能有一个字段的自增长,即
auto_increment
关键字在创建表的语句中只能出现一次 - 使用字段的自增长,则对应字段的数据类型 必须 是 数值型
- 字段的自增长搭配主键约束,则允许插入的数据是 null,这时该列会自动填充值
-
字段的自增长允许在创建表之后再添加,语法为:
alter table 表名 modify 现有字段 旧数据类型或新数据类型 primary key auto_increment;
-
自增长的初始值为 1 ,步长为 1,可通过变量查询到:
show variables like '%auto_increment%'; +--------------------------+-------+ | Variable_name | Value | +--------------------------+-------+ | auto_increment_increment | 1 | | auto_increment_offset | 1 | +--------------------------+-------+ 2 rows in set (0.01 sec)
auto_increment_offset 控制初始值,auto_increment_increment 控制步长(每次增长时的大小被称为步长)
若您需要删除自增长(在生产环境中谨慎操作!),则相应的语法为:
alter table 表名 modify 列名 数据类型;
比如上面的 autoincre 表,我想删除自增长但保留数据类型与主键约束:
alter table autoincre modify id int unsigned;
