MySQL基础12—DDL

概述

本章,您将学习 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 新字段名 数据类型;
    • 也可以使用 firstafter 关键字来调整新增字段的位置,语法为 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:sshhh:mm:ss N 用来指定可选的秒精度,支持微妙级别的精度,即最大为数字 6 3 字节

TIME 类型不仅可以用于表示一天中的某个时间(24小时制),还可以表示两个事件之间的时间间隔(换言之,hh 可以远大于 24 且还允许负值)。TIME 以 hh:mm:sshhh:mm:ss 格式检索和显示值,时间值的范围为 ‘-838:59:59’ 到 ‘838:59:59’。

TIME 类型支持多种时间格式,限于篇幅,建议 TIME 类型的值应该使用推荐的 hh:mm:sshhh:mm:ss 格式。

另外请注意,TIME 类型支持存储微秒精度的时间(也就是整数秒后面加小数点和 6 位数字),例如 19:43:50.236545

YEAR 类型

用来表示年份的类型,格式为 YYYY,范围为 19012155。占用 1 字节。

字符串数据类型

字符型

包含 charvarchar 关键字,如下表的说明:

关键字 说明 大小
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 表示字节数。

二进制型

包含 binaryvarbinary 关键字,它们与 charvarchar 类似,只不过它们存储的是二进制字符串。

关键字 说明 大小
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 在使用时应该注意哪些?

  1. 空字符与 null 并不是包含关系,这就导致了在查询时用到的关键字不一样

    # 查询空字符
    select * from 表名 where 字段='';
    
    # 查询 null
    select * from 表名 where 字段 is null;
  2. 聚合函数(sum() 函数、avg() 函数、max() 函数、min() 函数、count() 函数)会忽略 null 值但 并不忽略 空字符

  3. 在 DQL 中,若条件表达式为不等于(<>),则空字符和 null 都会被忽略

  4. 若字段的约束为 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;
Avatar photo

关于 陸風睿

GNU/Linux 从业者、开源爱好者、技术钻研者,撰写文档既是兴趣也是工作内容之一。Q - "281957576";WeChat - "jiulongxiaotianci",Github - https://github.com/jimcat8
用一杯咖啡支持我们,我们的每一篇[文档]都经过实际操作和精心打磨,而不是简单地从网上复制粘贴。期间投入了大量心血,只为能够真正帮助到您。
暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇