MySQL 数据类型详解(含实战案例)
本文系统讲解 MySQL 中常用的数据类型,包括数值类型、字符串类型、日期时间类型、枚举与集合类型,附带可直接运行的 SQL 示例。
一、数据类型分类
MySQL 中的数据类型主要分为以下几类:
- 数值类型 :
tinyint、int、float、decimal、bit等 - 字符串类型 :
char、varchar、text、blob等 - 日期和时间类型 :
date、datetime、timestamp等 - 枚举和集合类型 :
enum、set
二、数值类型
2.1 tinyint 类型
tinyint 占 1 个字节,默认是有符号的,范围为 -128 ~ 127。
数值越界测试:
sql
-- 创建表
mysql> create table tt1(num tinyint);
Query OK, 0 rows affected (0.02 sec)
-- 正常插入
mysql> insert into tt1 values(1);
Query OK, 1 row affected (0.00 sec)
-- 越界插入,报错
mysql> insert into tt1 values(128);
ERROR 1264 (22003): Out of range value for column 'num' at row 1
说明: 在 MySQL 中,整型默认是有符号的。可以通过 UNSIGNED 来声明无符号字段。
无符号案例:
sql
-- 创建无符号 tinyint 表
mysql> create table tt2(num tinyint unsigned);
-- 无符号范围是 0 ~ 255,插入 -1 报错
mysql> insert into tt2 values(-1);
ERROR 1264 (22003): Out of range value for column 'num' at row 1
-- 正常插入 255
mysql> insert into tt2 values(255);
Query OK, 1 row affected (0.02 sec)
建议: 尽量不使用
unsigned。对于int类型可能存放不下的数据,int unsigned同样可能存放不下,不如直接将int提升为bigint。
2.2 bit 类型
基本语法:
bit[(M)] : 位字段类型。M 表示每个值的位数,范围从 1 到 64。如果 M 被忽略,默认为 1。
举例:
sql
mysql> create table tt4 (id int, a bit(8));
Query OK, 0 rows affected (0.01 sec)
mysql> insert into tt4 values(10, 10);
Query OK, 1 row affected (0.01 sec)
-- bit 字段显示时按 ASCII 码对应值显示,10 没有对应可显示字符
mysql> select * from tt4;
+------+------+
| id | a |
+------+------+
| 10 | |
+------+------+
mysql> insert into tt4 values(65, 65);
mysql> select * from tt4;
+------+------+
| id | a |
+------+------+
| 10 | |
| 65 | A | -- 65 对应 ASCII 字符 'A'
+------+------+
使用场景: 如果字段只存放 0 或 1,可以定义 bit(1) 来节省空间。
sql
mysql> create table tt5(gender bit(1));
mysql> insert into tt5 values(0);
mysql> insert into tt5 values(1);
mysql> insert into tt5 values(2); -- 越界
ERROR 1406 (22001): Data too long for column 'gender' at row 1
2.3 小数类型
2.3.1 float
语法:
float[(m, d)] [unsigned] : M 指定显示长度,d 指定小数位数,占用空间 4 个字节
float(4,2) 表示的范围是 -99.99 ~ 99.99,MySQL 在保存值时会进行四舍五入。
sql
mysql> create table tt6(id int, salary float(4,2));
Query OK, 0 rows affected (0.01 sec)
mysql> insert into tt6 values(100, -99.99);
Query OK, 1 row affected (0.00 sec)
-- 多出的小数位被四舍五入截断
mysql> insert into tt6 values(101, -99.991);
Query OK, 1 row affected (0.00 sec)
mysql> select * from tt6;
+------+--------+
| id | salary |
+------+--------+
| 100 | -99.99 |
| 101 | -99.99 |
+------+--------+
无符号 float:
如果定义 float(4,2) unsigned,则范围变为 0 ~ 99.99。
sql
mysql> create table tt7(id int, salary float(4,2) unsigned);
mysql> insert into tt7 values(100, -0.1); -- 贊界,会产生 warning
Query OK, 1 row affected, 1 warning (0.00 sec)
mysql> insert into tt7 values(100, 99.99);
Query OK, 1 row affected (0.00 sec)
2.3.2 decimal
语法:
decimal(m, d) [unsigned] : 定点数,m 指定长度,d 表示小数点的位数
decimal(5,2) 表示的范围是 -999.99 ~ 999.99。decimal 整数最大位数 m 为 65,支持小数最大位数 d 是 30。
float 与 decimal 精度对比:
sql
mysql> create table tt8(id int, salary float(10,8), salary2 decimal(10,8));
mysql> insert into tt8 values(100, 23.12345612, 23.12345612);
Query OK, 1 row affected (0.00 sec)
mysql> select * from tt8;
+------+-------------+-------------+
| id | salary | salary2 |
+------+-------------+-------------+
| 100 | 23.12345695 | 23.12345612 | -- decimal 精度更准确
+------+-------------+-------------+
建议: 如果希望小数的精度高,推荐使用
decimal。
三、字符串类型
3.1 char
语法:
char(L): 固定长度字符串,L 是可以存储的长度,单位为字符,最大长度值可以为 255
sql
mysql> create table tt9(id int, name char(2));
mysql> insert into tt9 values(100, 'ab');
Query OK, 1 row affected (0.00 sec)
mysql> insert into tt9 values(101, '中国');
Query OK, 1 row affected (0.00 sec)
mysql> select * from tt9;
+------+--------+
| id | name |
+------+--------+
| 100 | ab |
| 101 | 中国 |
+------+--------+
-- 超过 2 个字符报错
mysql> create table tt10(id int, name char(256));
ERROR 1074 (42000): Column length too big for column 'name' (max = 255); use BLOB or TEXT instead
3.2 varchar
语法:
varchar(L): 可变长度字符串,L 表示字符长度,最大长度 65535 个字节
关于 varchar(L) 中 L 的实际最大值,与表的编码密切相关:
| 编码 | 每字符字节数 | varchar 最大字符数 |
|---|---|---|
| utf8 | 3 | 65532 / 3 = 21844 |
| gbk | 2 | 65532 / 2 = 32766 |
有 1~3 个字节用于记录数据大小,所以有效字节数是 65532。
sql
mysql> create table tt10(id int, name varchar(6)); -- 可存放 6 个字符
mysql> insert into tt10 values(100, 'hello');
mysql> insert into tt10 values(100, '我爱你,中国');
mysql> select * from tt10;
+------+--------------------+
| id | name |
+------+--------------------+
| 100 | hello |
| 100 | 我爱你,中国 |
+------+--------------------+
3.3 char 和 varchar 的选择
| 场景 | 推荐类型 |
|---|---|
| 长度固定的数据(身份证、手机号、md5) | char |
| 长度可变的数据(名字、地址) | varchar |
核心区别:
- 定长(char):磁盘空间比较浪费,但效率高(直接开辟好对应空间)
- 变长(varchar):磁盘空间比较节省,但效率低(用多少,开辟多少)
四、日期和时间类型
| 类型 | 格式 | 范围 | 字节数 |
|---|---|---|---|
date |
yyyy-MM-dd |
--- | 3 字节 |
datetime |
yyyy-MM-dd HH:mm:ss |
1000 ~ 9999 | 8 字节 |
timestamp |
yyyy-MM-dd HH:mm:ss |
1970 年起 | 4 字节 |
案例:
sql
-- 创建表
mysql> create table birthday (t1 date, t2 datetime, t3 timestamp);
Query OK, 0 rows affected (0.01 sec)
-- 插入数据,timestamp 会自动填充当前时间
mysql> insert into birthday(t1,t2) values('1997-7-1','2008-8-8 12:1:1');
Query OK, 1 row affected (0.00 sec)
mysql> select * from birthday;
+------------+---------------------+---------------------+
| t1 | t2 | t3 |
+------------+---------------------+---------------------+
| 1997-07-01 | 2008-08-08 12:01:01 | 2017-11-12 18:28:55 | -- timestamp 自动填充
+------------+---------------------+---------------------+
-- 更新数据时,timestamp 会自动更新为当前时间
mysql> update birthday set t1='2000-1-1';
Query OK, 1 row affected (0.00 sec)
mysql> select * from birthday;
+------------+---------------------+---------------------+
| t1 | t2 | t3 |
+------------+---------------------+---------------------+
| 2000-01-01 | 2008-08-08 12:01:01 | 2017-11-12 18:32:09 | -- 自动更新
+------------+---------------------+---------------------+
五、enum 和 set 类型
5.1 enum(枚举 --- 单选)
语法:
sql
enum('选项1','选项2','选项3',...);
最终一个单元格中只存储其中一个值。出于效率考虑,这些值实际存储的是数字(1, 2, 3, ...),最多支持 65535 个选项。
5.2 set(集合 --- 多选)
语法:
sql
set('选项值1','选项值2','选项值3', ...);
一个单元格中可存储其中任意多个值。存储时使用位图(1, 2, 4, 8, 16, 32, ...),最多支持 64 个选项。
不建议在添加枚举值和集合值时使用数字方式,因为不利于阅读。
5.3 综合案例
创建调查表:
sql
mysql> create table votes(
-> username varchar(30),
-> hobby set('登山','游泳','篮球','武术'), -- 多选
-> gender enum('男','女') -- 单选
-> );
Query OK, 0 rows affected (0.02 sec)
插入数据:
sql
insert into votes values('雷锋', '登山,武术', '男');
insert into votes values('Juse', '登山,武术', 2); -- 2 对应 '女'
insert into votes values('LiLei', '登山', '男');
insert into votes values('LiLei', '篮球', '男');
insert into votes values('HanMeiMei', '游泳', '女');
查询所有数据:
sql
mysql> select * from votes;
+-----------+---------------+--------+
| username | hobby | gender |
+-----------+---------------+--------+
| 雷锋 | 登山,武术 | 男 |
| Juse | 登山,武术 | 女 |
| LiLei | 登山 | 男 |
| LiLei | 篮球 | 男 |
| HanMeiMei | 游泳 | 女 |
+-----------+---------------+--------+
注意: 使用 = 查询集合类型时,只能精确匹配:
sql
mysql> select * from votes where hobby='登山';
+----------+--------+--------+
| username | hobby | gender |
+----------+--------+--------+
| LiLei | 登山 | 男 | -- 只查到恰好只有"登山"的记录
+----------+--------+--------+
正确做法 --- 使用 find_in_set 函数:
sql
find_in_set(sub, str_list) : 如果 sub 在 str_list 中,则返回下标;不在则返回 0
str_list 是用逗号分隔的字符串
sql
-- 查找所有爱好包含"登山"的人
mysql> select * from votes where find_in_set('登山', hobby);
+----------+---------------+--------+
| username | hobby | gender |
+----------+---------------+--------+
| 雷锋 | 登山,武术 | 男 |
| Juse | 登山,武术 | 女 |
| LiLei | 登山 | 男 |
+----------+---------------+--------+
find_in_set 示例:
sql
mysql> select find_in_set('a', 'a,b,c');
+---------------------------+
| find_in_set('a', 'a,b,c') |
+---------------------------+
| 1 |
+---------------------------+
mysql> select find_in_set('d', 'a,b,c');
+---------------------------+
| find_in_set('d', 'a,b,c') |
+---------------------------+
| 0 |
+---------------------------+
六、总结
| 数据类型 | 常用类型 | 说明 |
|---|---|---|
| 整数 | tinyint、int、bigint |
根据数据范围选择 |
| 小数 | float、decimal |
精度要求高用 decimal |
| 位 | bit |
节省空间存储布尔值 |
| 字符串 | char、varchar |
定长用 char,变长用 varchar |
| 日期时间 | date、datetime、timestamp |
timestamp 自动维护 |
| 枚举集合 | enum、set |
单选用 enum,多选用 set |
选择原则:
- 选择最小够用的类型,节省存储空间
- 金额等精度敏感数据用
decimal,不用float - 主键推荐
bigint,避免int溢出风险 - 定长数据用
char,变长数据用varchar - 查询集合类型时用
find_in_set(),不要用=