05 数据类型

数据类型

1. 常用数据类型一览

分类数据类型说明
数值类型BIT(M)位类型。M 指定位数,默认值 1,范围 1–64。
TINYINT [UNSIGNED]很小的整数。带符号范围:-128 ~ 127;无符号范围:0 ~ 255。默认为有符号。
BOOL / BOOLEAN布尔类型,是 TINYINT(1) 的同义词,0 表示 FALSE,非 0 表示 TRUE
SMALLINT [UNSIGNED]小整数。带符号范围:-32,768 ~ 32,767;无符号范围:0 ~ 65,535。
INT / INTEGER [UNSIGNED]最常用的整数类型。带符号范围:-2,147,483,648 ~ 2,147,483,647;无符号范围:0 ~ 4,294,967,295。
BIGINT [UNSIGNED]大整数。带符号范围:-9,223,372,036,854,775,808 ~ 9,223,372,036,854,775,807;无符号范围:0 ~ 18,446,744,073,709,551,615。
FLOAT(M,D) [UNSIGNED]单精度浮点数。M 为总位数,D 为小数位数。占用 4 字节。存在精度误差。
DOUBLE(M, D) [UNSIGNED]双精度浮点数。比 FLOAT 精度更高,占用 8 字节。
DECIMAL(M,D) [UNSIGNED]定点数(高精度)。M 为总位数,D 为小数位数。用于 货币、金额计算,不会产生精度误差。
字符串与二进制类型CHAR(size)定长字符串,最大长度 255 字符。不足部分用空格填充。性能稳定但浪费空间。
VARCHAR(size)最常用字符串类型,可变长度字符串,最大 65,535 字符。节省空间,性能较好。
TEXT长文本对象,最大 65,535 字符。不支持默认值
TINYTEXT / MEDIUMTEXT / LONGTEXT不同大小的文本类型,最大长度分别为 255、16MB、4GB。
BLOB二进制大对象,用于存储图片、文件、音频等。
TINYBLOB / MEDIUMBLOB / LONGBLOB不同大小的二进制对象类型。
时间日期类型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。受时区影响,插入/更新自动设置当前时间。
TIME时间类型,格式 HH:MM:SSHHH:MM:SS(可表示超过 24 小时的时间)。
YEAR年份类型,可为 2 位或 4 位数字格式(推荐 4 位)。
字符串枚举类型ENUM(‘v1’,’v2’,…)枚举类型,列值必须是预定义列表中的一个值。内部按数字存储。节省空间但灵活性差。
SET(‘v1’,’v2’,…)集合类型,可同时存储多个预定义值(以逗号分隔)。每个成员值本身不能含逗号。

1. 补充说明

说明
[UNSIGNED]表示无符号数,只能存储正数和零。
M, D 参数FLOATDOUBLE 中:M 为显示宽度,D 为小数位数;在 DECIMAL 中:M 为总位数,D 为小数位数。
size 参数对于 CHARVARCHAR,表示 字符数 而非字节数。UTF8 编码下一个字符占 1–3 字节,UTF8MB4 下占 1–4 字节。
TIMESTAMP 特性默认在插入或更新时自动设为当前时间,受服务器时区影响。
ENUM / SET 内部存储虽然是字符串类型,但内部以整数映射存储,在性能上优于 VARCHAR,但修改枚举值需要重建表。

2. 实际开发常用推荐表

场景推荐数据类型理由
主键、自增 IDBIGINT UNSIGNED足够大、可自增,不会溢出。
数量、状态码INT / TINYINT占用少,查询快。
金额、精确计算DECIMAL(10,2)高精度,避免浮点误差。
姓名、标题VARCHAR(100)适合变长字符串。
内容、备注TEXT存放长文本。
日期时间DATETIME直观、不受时区影响。
更新时间TIMESTAMP自动更新特性方便日志记录。
性别、状态、类型ENUM固定选项值,节省空间。

2. 数值类型

1. tinyint 类型

1. 作用

TINYINT 是 MySQL 中 最小的整数类型,常用于存储范围较小的整数值(例如状态标识、布尔值、性别、等级、标志位等)。它只占用 1 个字节(8 位) 存储空间,因此在性能和空间利用上非常高效。

2. 数据范围

类型取值范围
tinyint(有符号)-128 ~ 127
tinyint unsigned(无符号)0 ~ 255

UNSIGNED 关键字用于声明“无符号整数”,即不存储负数,范围翻倍。

3. 基本使用

inyint 类型占用 1 字节,有符号 tinyint 的取值范围为 -128~127,插入该范围内的数据时都能成功插入,如果插入的数据不在 -128~127 范围内,那么插入数据时就会产生报错:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# 创建表 t1,字段 num 类型为 tinyint(默认有符号)
create table t1(
num tinyint
);

# 插入范围内的数据(100-128~127 之间,成功)
insert into t1 values (100);

# 插入有符号 tinyint 的最小值(-128,成功)
insert into t1 values (-128);

# 插入超出范围的数据(-129 小于最小值 -128,报错)
insert into t1 values (-129);
ERROR 1264 (22003): Out of range value for column 'num' at row 1

# 查询表 t1 中的数据,验证插入结果
select * from t1;
+------+
| num |
+------+
| 100 |
| -128 |
+------+

由于 tinyint 类型占用 1 字节,因此无符号 tinyint 的取值范围为 0255,插入该范围的数据时都能成功插入。如果插入的数据不在 0255 范围内,那么插入数据时就会产生报错:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
# 创建表 t2,字段 num 类型为 tinyint unsigned(无符号,范围 0~255
create table t2(
num tinyint unsigned
);

# 插入范围内的数据(1000~255 之间,成功)
insert into t2 values (100);

# 插入负数(-1 小于 0,无符号不允许负数,报错)
insert into t2 values (-1);
ERROR 1264 (22003): Out of range value for column 'num' at row 1

# 插入超出最大值的数据(256 大于 255,报错)
insert into t2 values (256);
ERROR 1264 (22003): Out of range value for column 'num' at row 1

# 插入无符号 tinyint 的最大值(255,成功)
insert into t2 values (255);

# 查询表 t2 中的数据,验证插入结果
select * from t2;
+------+
| num |
+------+
| 100 |
| 255 |
+------+

4. 表示布尔值(最常见)

1
2
3
4
5
6
7
# tinyint 的常见用途:表示布尔值(0 代表 false1 代表 true
# 例如创建一个表示"是否启用"的字段,用 tinyint 存储更节省空间
create table status_table(
is_enabled tinyint # 0=禁用,1=启用
);

is_active TINYINT(1) DEFAULT 1 COMMENT '是否启用:0=禁用,1=启用';

说明一下:MySQL 本身是不支持 bool 类型的,当把一个数据设置成 bool 类型时,数据库会自动将其转换成 tinyint(1) 的数据类型,其实这个就是变相的 bool 类型,因为 tinyint(1) 只有 1 和 0 两种取值,可以分别对应 bool 类型的 true 和 false。

  • TINYINT(1) 实际上与 BOOL / BOOLEAN 等价;
  • 存储时底层仍是整数,0 表示 FALSE,非 0 表示 TRUE
  • 建议在逻辑字段中统一约定:0 = 否,1 = 是。

5. 表示小范围状态码

适用于枚举值较少的场景(如状态、类型、标志),取值有限时优先用 TINYINT 而不是 INT,节省空间。

1
status TINYINT UNSIGNED DEFAULT 0 COMMENT '状态:0=未开始,1=进行中,2=已完成';

6. 作为计数或等级字段

1
level TINYINT UNSIGNED DEFAULT 1 COMMENT '等级:1~10';

适合范围小的整数类型,如用户级别、权限等级、重试次数等。

7. 注意事项

项目说明
(1)TINYINT(1) 中的数字 1 不是长度限制,而是“显示宽度”,几乎无效。从 MySQL 8.0.17 起被完全废弃。
(2)若定义为 UNSIGNED,再插入负数会报错或被截断为 0。
(3)TINYINTBOOLEAN 只是语法同义词,底层存储一样。
(4)若需要表示超过 ±128 的范围,建议改用 SMALLINTINT
(5)对布尔字段建立索引意义不大(因为区分度太低)。

8. 详细示例

1
2
3
4
5
6
CREATE TABLE user_status (
id INT PRIMARY KEY AUTO_INCREMENT COMMENT '用户ID',
is_active TINYINT(1) DEFAULT 1 COMMENT '是否启用:0=否,1=是',
status TINYINT UNSIGNED DEFAULT 0 COMMENT '状态:0=未激活,1=已激活,2=冻结',
level TINYINT UNSIGNED DEFAULT 1 COMMENT '等级:1~10'
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='用户状态信息表';

执行:

1
DESC user_status;

输出示例:

1
2
3
4
5
6
7
8
+-----------+--------------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+-----------+--------------------+------+-----+---------+-------+
| id | int(11) | NO | PRI | NULL | auto_increment |
| is_active | tinyint(1) | YES | | 1 | |
| status | tinyint unsigned | YES | | 0 | |
| level | tinyint unsigned | YES | | 1 | |
+-----------+--------------------+------+-----+---------+-------+

2. bit 类型

一个 TINYINT 存一个状态,而一个 BIT(8) 可以在一个字节中同时存 8 个二进制标志。

1. 作用

BIT 类型用于存储 二进制位数据(bit data),常用于表示 标志位、状态开关、权限标识、布尔组合等场景。它能更高效地存储多个开关量,相当于一组二进制“位标志位数组”。

2. 数据范围

1
BIT[(M)]
项目说明
M表示位数,取值范围为 1–64。如果省略,默认为 1。
存储空间每 8 位占用 1 字节(向上取整)。
值范围可存储从 02^M - 1 的整数值。
MySQL 类型位数存储空间C 语言类比取值范围(有符号)
BIT(1)1 位约 1 位(实际存储会按字节对齐,通常占 1 字节)unsigned char(低 1 位)0 ~ 1
BIT(4)4 位约 4 位(实际存储占 1 字节)unsigned char(低 4 位)0 ~ 15
BIT(8)8 位1 字节unsigned char0 ~ 255
BIT(16)16 位2 字节unsigned short0 ~ 65,535
BIT(32)32 位4 字节unsigned int0 ~ 4,294,967,295
BIT(64)64 位8 字节unsigned long long0 ~ 18,446,744,073,709,551,615

3. 基本使用

1
2
3
4
5
6
7
mysql> create table t3 (id int,num bit(8));		# 定义 num 为 8 位的 bit 类型

mysql> insert into t3 values (10,10); # 插入十进制 10,自动转为 8 位二进制 00001010

# 也可以用二进制格式插入等价数据:
insert into t3 values (11, b'00001010'); # 二进制 00001010 等价于十进制 10
insert into t3 values (12, 0b00001010); # 0b 前缀也表示二进制

直接查询 bit 类型字段时,MySQL 会默认以 十六进制字符串 显示,如果想查看十进制或二进制值,需用函数转换:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
# 示例查询结果(默认十六进制)
select * from t3;
+------+------------+
| id | num |
+------+------------+
| 10 | 0x0A | # 0x0A 是十六进制,对应二进制 00001010
+------+------------+

# 用 hex() 函数显式查看十六进制(和默认一致),语法:select id, hex(num) from 表名;
mysql> select id, hex(num) from t3; # 结果:10 | 0A
+------+----------+
| id | hex(num) |
+------+----------+
| 10 | A |
| 11 | A |
| 12 | A |
+------+----------+

# 用 bin() 函数查看二进制(补全位数),语法:select id, bin(num) from 表名;
mysql> select id, bin(num) from t3; # 结果:10 | 1010(注意:MySQL 会省略前导 0,实际存储是 800001010
+------+----------+
| id | bin(num) |
+------+----------+
| 10 | 1010 |
| 11 | 1010 |
| 12 | 1010 |
+------+----------+

# 用 cast() 转换为十进制整数查看,语法:select id, cast(num as unsigned) from 表名;
mysql> select id, cast(num as unsigned) from t3; # 结果:10 | 10(转回十进制)
+------+-----------------------+
| id | cast(num as unsigned) |
+------+-----------------------+
| 10 | 10 |
| 11 | 10 |
| 12 | 10 |
+------+-----------------------+

4. 存储布尔值或开关状态(节省空间)

bit(1) 可存储 1 位二进制(0 或 1),适合表示布尔值(如 是否有效开关状态 等),比 tinyint 更节省空间(1 位 vs 1 字节)。比如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
mysql> create table t4 (
-> id int,
-> gender bit(1) # 1 表示男,0 表示女
-> );

# 插入 id=1、性别为男(用 1 表示)的记录
mysql> insert into t4 values (1, 1);

# 插入 id=2、性别为女(用 0 表示)的记录
mysql> insert into t4 values (2, 0);

# 若插入非 0/1 的值(如 2),会报错(bit(1) 只能存 1 位二进制,范围 0~1
mysql> insert into t4 values (3, 2);
ERROR 1406 (22001): Data too long for column 'gender' at row 1

mysql> select * from t4;
+------+----------------+
| id | gender |
+------+----------------+
| 1 | 0x01 |
| 2 | 0x00 |
+------+----------------+

# 用 cast(gender as unsigned) 转为十进制,再给列起别名 (如 gender_desc),结果更易理解
mysql> select
-> id,
-> cast(gender as unsigned) as gender_code, # 转十进制 后显示 0/1(性别编码)
-> case cast(gender as unsigned) # 用 case 语句将 0/1 转为具体文字(可选,更友好)
-> when 1 then '男'
-> when 0 then '女'
-> end as gender_desc
-> from t4;
+------+-------------+-------------+
| id | gender_code | gender_desc |
+------+-------------+-------------+
| 1 | 1 ||
| 2 | 0 ||
+------+-------------+-------------+

3. 小数类型

1. float 类型

1. 作用

float 是用于存储单精度浮点数的数值类型,主要用于表示带小数部分的数值(如身高、体重、温度等),其核心特点是 占用空间小、计算效率高,但精度有限(适用于对精度要求不高的场景)。

2. 数据范围
  • 存储空间:4 字节(32 位)。
  • 取值范围: 绝对值最大能达到接近 10^38^ 数量级。
  • 精度限制:约 6~7 位有效数字(超过后可能产生精度丢失,这是浮点数的固有特性)。
1
float(M, D)
  • M总位数(整数部分 + 小数部分的总长度,范围 1~255)。
  • D小数位数(小数点后的位数,范围 0~30,且 D 必须小于 M)。

例如 float(5, 2) 表示:总长度 5 位,其中小数部分占 2 位,整数部分最多 3 位(可存储范围:-999.99 ~ 999.99)。如果省略 MD(直接写 float),MySQL 会根据插入的数据自动分配存储空间(默认单精度,占用 4 字节)。

3. 基本使用

float(5, 2) 为例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
# float(M,D) 中:M=5 表示总位数(整数+小数共5位),D=2 表示小数部分占2
# 因此整数部分最多 3 位(5-2=3),取值范围为 -999.99 ~ 999.99
create table t5(height float(5,2));

# 插入 99.99:整数部分2+ 小数2位,总4位(未超过5位),符合范围 → 成功
insert into t5 values(99.99);

# 插入 -99.99:负数同样遵循范围约束(-99.99-999.99 ~ 999.99 之间)→ 成功
insert into t5 values(-99.99);

# 插入 1000.1:整数部分4位(1000+ 小数1位,总位数超过5位 → 超出范围,报错
insert into t5 values(1000.1);
ERROR 1264 (22003): Out of range value for column 'height' at row 1

# 插入 999.99:整数部分3位(999+ 小数2位,总5位(刚好达到上限)→ 成功
insert into t5 values(999.99);

# 插入 999.994:小数部分实际3位(994),定义为2位 → 四舍五入后为999.99(未超上限)→ 成功
insert into t5 values(999.994);

# 插入 999.995:四舍五入后为1000.00,整数部分变为4位(1000),总位数超过5位 → 超出范围,报错
insert into t5 values(999.995);
ERROR 1264 (22003): Out of range value for column 'height' at row 1

# -999.995 需四舍五入到2位小数:小数点后第3位是5,向前进1 → 结果为 -1000.00 超过5位限制 → 报错
insert into t5 values(-999.995);
ERROR 1264 (22003): Out of range value for column 'height' at row 1

# -999.994 四舍五入到2位小数:小数点后第3位是4,舍去 → 结果为 -999.99 符合 float(5,2) 约束 → 成功
insert into t5 values(-999.994);

创建表 t6,定义 height 为 float(4,2) unsigned 进行演示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
# float(4,2) 表示总位数≤4,小数部分固定2位 → 整数部分最多2位(4-2=2
# unsigned 表示无符号 → 只能存储非负数(≥0
create table t6(height float(4,2) unsigned);

# 插入 -0.1:负数,违反 unsigned 无符号约束(只能存≥0的值)→ 报错
insert into t6 values (-0.1);
ERROR 1264 (22003): Out of range value for column 'height' at row 1

# 插入 00 是非负数,且总位数(0.00,整数0+小数2位)符合 float(4,2) → 成功
insert into t6 values(0);

# 插入 -0.01:负数,违反 unsigned 约束 → 报错
insert into t6 values(-0.01);
ERROR 1264 (22003): Out of range value for column 'height' at row 1

# 插入 99.94:整数部分2位(99+ 小数2位 → 总位数4,且非负数 → 符合约束 → 成功
insert into t6 values(99.94);

# 插入 99.95:整数部分2+ 小数2位 → 总位数4,非负数 → 成功
insert into t6 values(99.95);

# 插入 99.99:整数部分2+ 小数2位 → 总位数4,非负数 → 成功(达到理论最大值)
insert into t6 values(99.99);

# 插入 99.994:需四舍五入到2位小数 → 99.99(整数2+小数2位,总4位)→ 成功
insert into t6 values(99.994);

# 插入 99.995:四舍五入到2位小数 → 100.00(整数部分变为3位,总位数=3+2=54)→ 超出范围 → 报错
insert into t6 values(99.995);
ERROR 1264 (22003): Out of range value for column 'height' at row 1

2. decimal 类型

decimalfloat 类型的使用方式 一模一样,但 decimal 的精度比 float 更高。这里就不详细讲解,类比使用:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# 创建表 t7,定义两个字段:
# n1 为 float(10,8):单精度浮点数,总位数10位,小数部分8位(整数部分最多2位)
# n2 为 decimal(10,8):高精度小数(定点数),总位数10位,小数部分8位(整数部分最多2位)
# 对比两者在存储精度上的差异
create table t7(n1 float(10,8),n2 decimal(10,8));

# 插入相同的数值 3.1415926(小数部分7位)到两个字段
insert into t7 values(3.1415926,3.1415926);
Query OK, 1 row affected (0.00 sec)

# 查询结果,观察精度差异:
# n1(float类型):存储结果为 3.14159250,与原始值有细微偏差(精度丢失)
# 原因:float是浮点数,用二进制近似存储,无法精确表示某些十进制小数,超过6-7位有效数字后会失真
# n2(decimal类型):存储结果为 3.14159260,完全精确(补全8位小数为9260
# 原因:decimal是定点数,按十进制存储,能精确表示小数,适合对精度要求高的场景(如金额)
select * from t7;
+------------+------------+
| n1 | n2 |
+------------+------------+
| 3.14159250 | 3.14159260 |
+------------+------------+
1 row in set (0.00 sec)

3. 字符串类型

1. char 类型

1. 作用

char 是一种 固定长度 的字符串类型,适用于存储长度固定或变化很小的字符串(如手机号、身份证号、性别标识等)。其核心特点是 存储空间固定,无论实际存储的字符串长度如何,都会占用预设的固定字节数。

2. 数据范围

1
char(L)
  • L 表示字符串的长度(即最多能存储的字符数),范围是 0~255(单位:字符,不是字节)。
  • 例如 char(10) 表示该字段最多存储 10 个字符,无论实际插入的字符串是 1 个还是 10 个字符,都会占用 10 个字符对应的存储空间(具体字节数取决于字符集,如 UTF8 中一个字符可能占 1~3 字节)。

3. 基本使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
# 创建表 t8,定义 str 字段为 char(6)(固定存储6个字符)
create table t8(str char(6));

# 插入值时未用引号包裹"minbit",MySQL会误认为是列名,导致"未知列"错误,注意:字符串值必须用单引号('')或双引号("")包裹
insert into t8 values (minbit);
ERROR 1054 (42S22): Unknown column 'minbit' in 'field list'

# 用单引号包裹字符串"minbit"(长度6个字符,刚好符合char(6)的长度)→ 插入成功
insert into t8 values ('minbit');

# 用双引号包裹字符串"minbit"(效果同单引号,MySQL支持单/双引号表示字符串)→ 插入成功
insert into t8 values ("minbit");

# 插入字符串"minbit1"(长度7个字符,超过char(6)定义的最大长度6)→ 超长报错
insert into t8 values ("minbit1");
ERROR 1406 (22001): Data too long for column 'str' at row 1

# 插入中文字符串"小米里的大麦"(长度6个汉字,每个汉字按1个字符计数,符合char(6))→ 成功
# 说明:char(L)中的L是"字符数",不是字节数,中文也按1个字符算(具体字节数取决于字符集)
insert into t8 values ("小米里的大麦");

# 插入"小米里的大麦1"(6个汉字+1个字母,共7个字符,超过6)→ 超长报错
insert into t8 values ("小米里的大麦1");
ERROR 1406 (22001): Data too long for column 'str' at row 1

# 插入字符串"1"(长度1个字符,短于6)→ 成功(MySQL会自动补5个空格至6个字符长度)
insert into t8 values ("1");

# 插入空字符串""(长度0,符合char(6)允许的范围)→ 成功(存储6个空格)
insert into t8 values ("");

# 查询表中数据,注意:
# 1. 短于6个字符的字符串(如"1"),查询时自动去除末尾填充的空格,显示原始长度
# 2. 空字符串显示为空白(实际存储6个空格,查询时去除后为空)
# 3. 刚好6个字符的字符串(如"minbit"、"小米里的大麦")原样显示
select *from t8;
+--------------------+
| str |
+--------------------+
| minbit |
| minbit |
| 小米里的大麦 |
| 1 |
| |
+--------------------+

2. varchar 类型

1. 作用

varchar 是一种 可变长度的字符串类型,适用于存储长度不固定或变化较大的字符串(如用户名、地址、备注等)。其核心特点是 存储空间随实际字符串长度变化,避免了固定长度类型(如 char)的空间浪费。

2. 数据范围

1
varchar(L)
  • L 表示字符串的最大长度(最多能存储的字符数),范围根据 MySQL 版本和存储引擎有所不同(通常为 065535,实际受表中其他字段总长度限制),**注意:其中有 12 字节用来表示实际数据长度,还有 1 字节来存储其他控制信息,因此 varchar 类型的有效字节数最多是 65532 字节。**
  • 例如 varchar(50) 表示该字段最多存储 50 个字符,实际存储时会根据插入的字符串长度动态分配空间(不会像 char 那样固定占用 50 个字符的空间)。

注意: 在 MySQL 中,VARCHAR 类型的最大长度不是以“字符数”为上限,而是受 存储字节数 限制。理论上,单行数据总字节上限为 65535 字节(含记录开销),因此 VARCHAR(L) 的最大值要根据字符集计算:例如在 utf8 编码下,一个字符最多占 3 字节,理论上 65532 ÷ 3 ≈ 21844,而在 gbk 编码下为 65532 ÷ 2 ≈ 32766。但在实际中,InnoDB 存储引擎(尤其是默认 COMPACTREDUNDANT 行格式)会对单个 VARCHAR 字段额外限制其最大存储字节数为 16383 字节,超出即报错:ERROR 1074 (42000): Column length too big for column 'str' (max = 16383); use BLOB or TEXT instead。比如下面是我的配置:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
# 查看指定表(如t9)的状态信息,\G表示按行垂直显示结果(更易读)
mysql> SHOW TABLE STATUS LIKE 't9'\G
*************************** 1. row ***************************
Name: t9 # 表名
Engine: InnoDB # 存储引擎为InnoDB(MySQL默认的事务型引擎)
Version: 10 # 表结构版本
Row_format: Dynamic # 行格式为Dynamic(InnoDB动态行格式,支持变长字段高效存储)
Rows: 4 # 估算的行数(非精确值,InnoDB通过统计信息计算)
Avg_row_length: 4096 # 平均每行占用的字节数(总数据长度/行数)
Data_length: 16384 # 表数据占用的字节数(16384字节=16KB,InnoDB最小分配单位)
Max_data_length: 0 # 表可存储的最大数据量(InnoDB不限制,故为0
Index_length: 0 # 索引占用的字节数(该表无索引,故为0
Data_free: 0 # 已分配但未使用的空间(该表数据紧凑,故为0
Auto_increment: NULL # 自增字段的下一个值(该表无自增字段,故为NULL
Create_time: 2025-10-10 15:00:57 # 表的创建时间
Update_time: 2025-10-10 15:02:17 # 表数据最后更新时间
Check_time: NULL # 最后一次检查表的时间(未检查过,故为NULL
Collation: utf8mb4_general_ci # 表的默认字符集排序规则(utf8mb4编码对应的通用排序)
Checksum: NULL # 表的校验和(未启用,故为NULL
Create_options: # 创建表时的额外选项(无特殊选项,故为空)
Comment: # 表的注释(无注释,故为空)

# 查看数据库默认字符集(当前为utf8mb4,支持emoji和所有Unicode字符)
mysql> SHOW VARIABLES LIKE 'character_set_database';
+------------------------+---------+
| Variable_name | Value |
+------------------------+---------+
| character_set_database | utf8mb4 |
+------------------------+---------+

# 查看数据库默认排序规则(utf8mb4_general_ci为utf8mb4的通用不区分大小写排序)
mysql> SHOW VARIABLES LIKE 'collation_database';
+--------------------+--------------------+
| Variable_name | Value |
+--------------------+--------------------+
| collation_database | utf8mb4_general_ci |
+--------------------+--------------------+

# 查看InnoDB文件格式(MySQL 8.0已移除该参数,默认使用Barracuda格式,故为空)
mysql> SHOW VARIABLES LIKE 'innodb_file_format';
Empty set (0.00 sec)

# 查看InnoDB默认行格式(Dynamic为动态行格式,适合存储变长字段,节省空间)
mysql> SHOW VARIABLES LIKE 'innodb_default_row_format';
+---------------------------+---------+
| Variable_name | Value |
+---------------------------+---------+
| innodb_default_row_format | dynamic |
+---------------------------+---------+

# 查看是否启用独立表空间(ON表示每张表单独存储为.ibd文件,便于管理)
mysql> SHOW VARIABLES LIKE 'innodb_file_per_table';
+-----------------------+-------+
| Variable_name | Value |
+-----------------------+-------+
| innodb_file_per_table | ON |
+-----------------------+-------+

# 查看MySQL服务器版本(当前为8.0.43,基于Ubuntu 22.04系统)
mysql> SELECT VERSION();
+-------------------------+
| VERSION() |
+-------------------------+
| 8.0.43-0ubuntu0.22.04.2 |
+-------------------------+

# 查看服务器默认字符集(与数据库一致,均为utf8mb4)
mysql> SHOW VARIABLES LIKE 'character_set_server';
+----------------------+---------+
| Variable_name | Value |
+----------------------+---------+
| character_set_server | utf8mb4 |
+----------------------+---------+

# 查看服务器默认排序规则(与数据库一致,确保字符处理规则统一)
mysql> SHOW VARIABLES LIKE 'collation_server';
+------------------+--------------------+
| Variable_name | Value |
+------------------+--------------------+
| collation_server | utf8mb4_general_ci |
+------------------+--------------------+
配置一览当前配置说明
MySQL 版本8.0.43新版 InnoDB,默认行格式已为 Dynamic
存储引擎InnoDB支持大字段分离存储
行格式Dynamic允许单行超出 8KB,字段可溢出页外存储
字符集utf8mb4每字符最多 4 字节
校验规则utf8mb4_general_ci默认排序规则
每表独立文件ON每张表独立存储,支持更大数据量

我的配置只能配置 varchar(16383)

3. 基本使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
# 创建表 t9,定义 str 字段为 varchar(6)(可变长度字符串,最大6个字符)
create table t9(str varchar(6));

# 插入数值1(未加引号,MySQL会自动转为字符串"1",长度16)→ 成功,说明:varchar 是字符串类型,插入数值时会隐式转换为字符串存储
insert into t9 values(1);

# 插入单引号包裹的字符串"minbit"(长度6个字符,刚好达到 varchar(6) 的最大限制)→ 成功
insert into t9 values('minbit');

# 插入双引号包裹的中文字符串"小米里的大麦"(长度6个汉字,每个汉字按1个字符计数,符合最大长度6)→ 成功
# 注意:varchar(L) 中的 L 是字符数,中文、字母、数字均按1个字符计算
insert into t9 values("小米里的大麦");

# 插入"小米里的大麦1"(6个汉字+1个数字,共7个字符,超过 varchar(6) 的最大长度)→ 超长报错
insert into t9 values("小米里的大麦1");
ERROR 1406 (22001): Data too long for column 'str' at row 1

# 插入空值(未指定具体值,等价于插入 NULL 或空字符串,符合长度要求)→ 成功
# varchar 允许存储空值,此时不占用额外空间(仅存储长度标识)
insert into t9 values();

# 查询 t9 表中的所有数据,观察 varchar(6) 字段的存储结果
select *from t9;
+--------------------+
| str |
+--------------------+
| 1 | # 对应插入的数值1,自动转为字符串"1"(长度1,按实际长度存储)
| minbit | # 插入的6字符字符串,刚好达到varchar(6)的最大长度,完整存储
| 小米里的大麦 | # 插入的6个汉字(6字符),符合长度限制,完整存储(varchar对中文按字符计数)
| NULL | # 对应插入的空值(values()),varchar允许存储NULL,显示为NULL(区别于空字符串"")
+--------------------+

3. char VS varchar

1. 功能与定义差异

特性CHAR(L)VARCHAR(L)
存储方式定长(固定长度)变长(按实际长度存储)
最大存储上限255 个字符与字符集和表结构相关(utf8mb4 下约 16383 字符)
空间分配无论实际存储多少字符,都占 L 个字符空间仅占实际字符所需空间 + 1~3 字节长度信息
超出定义长度报错报错
是否需要额外字节是(1~3 字节记录长度)

2. 性能与效率差异

对比项CHARVARCHAR
空间利用率较低(浪费空间)较高(按需分配)
访问效率高(定长数据定位快)稍低(需先解析长度字节)
更新效率高(不易产生碎片)低(长度变化可能导致页分裂)
适用场景数据长度固定数据长度不固定

4. 日期和时间类型

1. 常用的三种时间日期类型

类型格式占用空间说明
DATEYYYY-MM-DD3 字节只保存日期,不含时间。适合存储生日、入职日期等。
DATETIMEYYYY-MM-DD HH:MM:SS8 字节保存完整的日期与时间。与时区无关,适合通用业务时间记录。
TIMESTAMPYYYY-MM-DD HH:MM:SS4 字节自动记录插入或更新时间,会受时区影响。常用于“创建时间”、“修改时间”等字段。

2. 基本使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
# 创建表 t10,包含三种日期时间类型:
# t1: date(仅存储日期,格式YYYY-MM-DD)
# t2: datetime(存储日期+时间,格式YYYY-MM-DD HH:MM:SS)
# t3: timestamp(存储日期+时间,受时区影响,默认可自动更新)
create table t10( t1 date, t2 datetime, t3 timestamp);
Query OK, 0 rows affected (0.03 sec)

# 查看表结构:
# t3 字段类型为 timestamp,但 Extra 列无特殊标识(默认不自动更新,需显式配置)
desc t10;
+-------+-----------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+-------+-----------+------+-----+---------+-------+
| t1 | date | YES | | NULL | |
| t2 | datetime | YES | | NULL | |
| t3 | timestamp | YES | | NULL | | # 无 ON UPDATE CURRENT_TIMESTAMP,故不自动更新
+-------+-----------+------+-----+---------+-------+

# 插入时未用引号包裹日期时间值,MySQL 会解析为表达式(2025-01-01 被当作数学计算),导致语法错误
insert into t10 (t1,t2) values(2025-01-01,2025-01-01 00:00:00);
ERROR 1064 (42000): You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '00:00:00)' at line 1

# 插入数据时,为 t1、t2 赋值(t3 未指定,默认为 NULL
# 日期时间字符串需用单引号包裹,格式符合要求('YYYY-MM-DD''YYYY-MM-DD HH:MM:SS'
mysql> insert into t10 (t1,t2) values('2025-01-01','2025-01-01 00:00:00'); # 完整语句补全后

# 用双引号包裹日期时间字符串(效果同单引号),符合语法规则,插入成功
insert into t10 (t1,t2) values("2025-01-01","2025-01-01 00:00:00");

# 查询结果:t3 均为 NULL(因插入时未赋值,且未配置自动更新)
select *from t10;
+------------+---------------------+------+
| t1 | t2 | t3 |
+------------+---------------------+------+
| 2025-01-01 | 2025-01-01 00:00:00 | NULL | # t3 未赋值,保持 NULL
| 2025-01-01 | 2025-01-01 00:00:00 | NULL | # t3 未赋值,保持 NULL
+------------+---------------------+------+

注意:default current_timestamp on update current_timestamp 这类规则只能在 创建表时定义字段修改表结构时 配置,不能在 insert 语句中直接使用!必须在 create tablealter table 时声明,属于表结构的一部分!创建表时配置规则(推荐):

1
2
3
4
5
6
7
create table t11(
t1 date,
t2 datetime,
t3 timestamp default current_timestamp on update current_timestamp # 定义时配置规则
);

insert into t11 (t3) values (default); # 使用 t3 字段定义的默认值(当前时间)

对于已存在的表,用 alter table 补充规则:

1
2
3
alter table t11
modify column t3 timestamp
default current_timestamp on update current_timestamp;

5. enum 和 set

1. enum 类型(枚举)

enum 用于存储 “多选一” 的值,即字段值只能是预定义选项中的 一个

1. 数据范围
1
enum('值1', '值2', ..., '值n')

选项列表最多支持 65535 个值(实际使用中通常远少于此),选项区分大小写(取决于表的字符集排序规则,如 utf8mb4_general_ci 不区分大小写,utf8mb4_bin 区分)。

2. 基本使用
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
# 创建表 t12,定义 gender 为 enum 类型(枚举,只能从预定义选项中选一个)
# enum('男',"女",'未知') 表示可选值为 '男''女''未知'(单/双引号均可)
create table t12(
id int,
gender enum('男',"女",'未知')
);

# 插入符合 enum 选项的值(单/双引号包裹均可),均成功
insert into t12 values
(1,'男'), # 选 '男'
(2,"女"), # 选 '女'(双引号)
(3,'未知'); # 选 '未知

# 插入未在 enum 选项中的值 '保密' → 不符合预定义范围,报错,错误提示:字段值被截断(实际因值不在选项中,无法存储)
insert into t12 values (4,"保密");
ERROR 1265 (01000): Data truncated for column 'gender' at row 1

# 插入数字 1 → enum 支持用数字索引(从1开始)表示选项,1 对应第一个选项 '' → 成功
insert into t12 values (4,1);

# 插入数字 3 → 3 对应第三个选项 '未知' → 成功
insert into t12 values (4,3);

# 插入数字 4 → enum 选项只有3个(索引1~3),4 超出范围 → 报错
insert into t12 values (4,4);
ERROR 1265 (01000): Data truncated for column 'gender' at row 1

# 查询结果:
# 数字索引会自动转换为对应的选项值(1→'',3→'未知')
select * from t12;
+------+--------+
| id | gender |
+------+--------+
| 1 | 男 | # 直接插入 ''
| 2 | 女 | # 直接插入 ''
| 3 | 未知 | # 直接插入 '未知'
| 4 | 男 | # 插入 1 → 对应 ''
| 4 | 未知 | # 插入 3 → 对应 '未知'
+------+--------+

2. set 类型(集合)

set 用于存储 “多选多” 的值,即字段值可以是预定义选项中的 多个(0 个或多个),值之间用逗号分隔。

1. 数据范围
1
set('值1', '值2', ..., '值n')

选项列表最多支持 64 个值(因存储方式限制),选项同样区分大小写(规则同 enum)。

2. 基本使用
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
# 创建表 t13,定义 s 为 set 类型(集合,可从预定义选项中选多个值)
# set('吃饭',"睡觉",'打豆豆',"编码") 表示可选值为这4个,支持多选(用逗号分隔)
create table t13 (
id int,
s set('吃饭',"睡觉",'打豆豆',"编码")
);

# 插入符合 set 规则的值(单/双引号均可):
# 1.1个值;2. 空值(选0个);3. 选多个值(用逗号分隔);4.1个值
insert into t13 values
(1,'吃饭'),
(2,""), # 空字符串表示不选任何值
(3,"吃饭,睡觉,编码"), # 多选值用逗号分隔
(4,'编码');

# 插入重复值 '吃饭,吃饭'set 会自动去重,存储为 '吃饭' → 成功
insert into t13 values (5,'吃饭,吃饭');

# 插入未在 set 选项中的 '打游戏' → 不在预定义范围,报错
insert into t13 values (5,"打游戏");
ERROR 1265 (01000): Data truncated for column 's' at row 1

# 插入数字 11set 支持用二进制位对应的数字表示选项(每个选项对应一个位,从左到右为 1,2,4,8...)
# 选项对应关系:'吃饭'=12^0)、'睡觉'=22^1)、'打豆豆'=42^2)、'编码'=82^3
# 11 = 8 + 2 + 1 → 对应 '吃饭,睡觉,编码' → 成功
insert into t13 values (5,11);

# 插入数字 1010 = 8 + 2 → 对应 '睡觉,编码' → 成功
insert into t13 values (5,10);

# 插入数字 000(即 0)→ 0 表示不选任何值(等价于空字符串)→ 成功
insert into t13 values (5,000);

# 插入数字 0001(即 1)→ 1 对应 '吃饭' → 成功
insert into t13 values (5,0001);

# 插入数字 1111 → 该 set 只有4个选项(最大对应数字 1+2+4+8=15),1111 超出范围(1111是十进制4369)→ 报错
insert into t13 values (5,1111);
ERROR 1265 (01000): Data truncated for column 's' at row 1

# 查询结果:
mysql> select *from t13;
+------+----------------------+
| id | s |
+------+----------------------+
| 1 | 吃饭 |
| 2 | |
| 3 | 吃饭,睡觉,编码 |
| 4 | 编码 |
| 5 | 吃饭 |
| 5 | 吃饭,睡觉,编码 |
| 5 | 睡觉,编码 |
| 5 | |
| 5 | 吃饭 |
+------+----------------------+