【MySQL】数据类型

目录

数据类型一览

五大分类总览

概论

数值类型

整数类型

bit 类型

小数类型

字符类型

char 类型

varchar 类型

varchar 和 char 的区别

如何选择定长或变长字符串

日期与时间类型

enum 和 set 类型


数据类型一览

五大分类总览

分类代表类型核心用途
数值类型TINYINT, INT, BIGINT, DECIMAL存储整数、小数、金额
字符串类型CHAR, VARCHAR, TEXT, BLOB存储文本、二进制数据
日期时间类型DATE, DATETIME, TIMESTAMP存储时间点、时段
空间类型POINT, LINESTRING, POLYGON地理坐标、GIS应用
JSON类型JSON存储半结构化文档(MySQL 5.7+)

概论

整数类型

类型字节有符号范围无符号范围典型场景
TINYINT1-128 ~ 1270 ~ 255状态码、性别、开关
SMALLINT2-32,768 ~ 32,7670 ~ 65,535小型枚举、评分
MEDIUMINT3-8,388,608 ~ 8,388,6070 ~ 16,777,215中等范围ID(如日志)
INT4-21亿 ~ 21亿0 ~ 42亿常规主键ID
BIGINT8-9.22×10¹⁸ ~ 9.22×10¹⁸0 ~ 1.84×10¹⁹雪花ID、大表主键

关键点

  • INT(11) 中的数字不表示存储大小,只表示显示宽度(配合ZEROFILL使用),不影响实际存储。

  • 建议主键优先用 BIGINT(考虑未来数据量)或 INT(数据量可控)。

浮点与定点数

类型字节特点适用场景
FLOAT4单精度,约7位有效数字科学计算,精度要求不高
DOUBLE8双精度,约15位有效数字科学计算,稍高精度
DECIMAL(M,D)变长精确存储,每9位数字占4字节金融、金额、税率(必须用)

核心原则

  • 金额永远用 DECIMAL,避免浮点精度丢失(如 0.1+0.2 ≠ 0.3)。

  • DECIMAL(10,2) 表示总共 10 位数字,其中小数2位,整数部分最多8位。

  • 精度过高(如 DECIMAL(30,10))会消耗更多存储和 CPU,按需设置。

字符串类型

CHAR vs VARCHAR(核心区别)

类型长度定义存储方式尾部空格适用场景
CHAR(N)固定 N 个字符总是分配 N 个字符空间默认移除定长数据:MD5、手机号、身份证
VARCHAR(N)可变,最多 N 个字符实际长度 + 1~2字节(存储长度)保留用户名、标题、描述

性能与空间权衡

  • CHAR 适合长度固定的数据(如UUID、MD5),更新时不会产生行迁移(性能更好)。

  • VARCHAR 适合长度波动大的数据,节省空间,但更新变长时可能引发页分裂。

重要VARCHAR(N) 中的 N 是字符数,不是字节数。在 utf8mb4 下,一个汉字占3~4字节,所以 VARCHAR(255) 最多可存 255 个字符(但实际受行大小限制)。

 TEXT 系列(大文本)

类型最大长度存储额外开销
TINYTEXT255 字节1字节
TEXT65,535 字节(约64KB)2字节
MEDIUMTEXT16,777,215 字节(约16MB)3字节
LONGTEXT4,294,967,295 字节(约4GB)4字节

关键注意

  • TEXT 数据在 InnoDB 中可能存储在外部页(off-page),访问时需额外IO,影响性能。

  • 若经常查询大文本字段(如文章内容),考虑拆分为独立表或使用缓存。

  • 不能在 TEXT 上直接建索引(除非指定前缀长度)。

 BLOB(二进制大对象)

  • 与 TEXT 对应,但存储的是二进制数据(图片、音频、视频、序列化对象)。

  • 排序和比较基于字节数值,而非字符集。

  • 强烈不建议在数据库直接存大文件,推荐存 OSS/S3 路径。

日期时间类型(最容易混淆

类型字节范围精度时区影响场景
DATE3'1000-01-01' ~ '9999-12-31'生日、财报日
TIME3'-838:59:59' ~ '838:59:59'时长、时间段
DATETIME5~8'1000-01-01 00:00:00' ~ '9999-12-31 23:59:59'微秒(MySQL 5.6+)订单创建时间(业务时间)
TIMESTAMP4'1970-01-01 00:00:01' UTC ~ '2038-01-19 03:14:07' UTC(自动转UTC)系统时间戳、日志(含时区转换)
YEAR11901 ~ 2155年份单独存储

核心原则(高频面试点)

  1. TIMESTAMP 受时区影响:存入时转UTC,取出时转当前时区;DATETIME 不做任何转换。

  2. TIMESTAMP 有 2038 年问题(Unix时间溢出),如果系统存远期时间,必须用 DATETIME。

  3. MySQL 5.6 之后 DATETIME 支持微秒(如 DATETIME(3) 精确到毫秒)。

  4. 建表规范:推荐用 DATETIME(0) 或 DATETIME(3) 存储业务时间,避免时区歧义;系统字段(如 update_time)可考虑 TIMESTAMP 自动更新。

  5. 永远不要用 字符串 或 INT 存日期(无法使用日期函数,无法优化索引)。

JSON 类型(现代应用必备)

核心特性(MySQL 5.7+)

  • 自动校验:插入非法JSON会报错。

  • 高效访问:通过 -> 和 ->> 操作符直接提取字段,无需解析整个文档。

  • 索引支持:可对JSON字段的特定路径创建虚拟列+索引,极大提升查询性能。

-- 创建表
CREATE TABLE users (
    id INT PRIMARY KEY,
    info JSON
);

-- 插入数据
INSERT INTO users VALUES (1, '{"name":"张三","age":30,"tags":["vip","gold"]}');

-- 查询(返回带引号)
SELECT info->'$.name' FROM users;  -- "张三"

-- 查询(返回纯文本)
SELECT info->>'$.name' FROM users;  -- 张三

-- 为age字段建虚拟索引(加速范围查询)
ALTER TABLE users ADD COLUMN age INT GENERATED ALWAYS AS (info->>'$.age') VIRTUAL, ADD INDEX idx_age(age);

适合场景

  • 属性不固定的扩展字段(如商品自定义属性)。

  • 存储前端传来的复杂嵌套对象(避免拆多张表)。

  • 不适合频繁更新JSON内部某个字段(会整块重写,性能差)。

数值类型

整数类型

类型字节有符号范围无符号范围典型场景
TINYINT1-128 ~ 1270 ~ 255状态码、性别、开关
SMALLINT2-32,768 ~ 32,7670 ~ 65,535小型枚举、评分
MEDIUMINT3-8,388,608 ~ 8,388,6070 ~ 16,777,215中等范围ID(如日志)
INT4-21亿 ~ 21亿0 ~ 42亿常规主键ID
BIGINT8-9.22×10¹⁸ ~ 9.22×10¹⁸0 ~ 1.84×10¹⁹雪花ID、大表主键

以 tinyint 为例介绍以上的数据类型,其他的数据类型可以以此类推。

在 MySQL中,整型可以指定是有符号的和无符号的,默认是有符号的。 可以通过 UNSIGNED 来说明某个字段是无符号的

数值越界测试:

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> select * from tt1;
+------+
| num  |
+------+
|    1 |
+------+
1 row in set (0.00 sec)

如果要插入的数据超过了数据类型的范围,在 c/c++ 可能发生截断,在 MySQL 这里直接拦截不让插入,即: MySQL 如果插入数据成功,数据一定是合法的,所以MySQL的数据类型本身就是一种约束,它使得程序员思考插入的数据是否合法

bit 类型

基本语法

bit[(M)] : 位字段类型。M表示每个值的位数,范围从1到64。如果M被忽略,默认为1。

说明:

  • bit类型同样也有约束,当定义的 bit 位数大于大于 64 或插入的数据的 bit 位大于定义时规定的 bit 时,MySQL 同样会拦截
  • bit 类型在显示的时候,是按照 ASCII 码表对应的字符显示的,bit 类型在插入时,插入的数据也可以写成 ASCII 码对应的字符

小数类型

小数类型有 float 类型、double 类型、decimal 类型,它们的精度依次增加

类型字节特点适用场景
FLOAT4单精度,约7位有效数字科学计算,精度要求不高
DOUBLE8双精度,约15位有效数字科学计算,稍高精度
DECIMAL(M,D)变长精确存储,每9位数字占4字节金融、金额、税率(必须用)

语法

float[(m, d)] [unsigned] : M指定最大位数(整数部分 + 小数部分),d指定小数位数,占用空间4个字节
double[(m, d)] [unsigned] : M指定最大位数(整数部分 + 小数部分),d指定小数位数,占用空间4个字节
decimal[(m, d)] [unsigned] : M指定最大位数(整数部分 + 小数部分),d指定小数位数,占用空间4个字节

说明:

  • 如果插入的数据的小数位数大于定义时规定的小数位数,此时 MySQL 不会拦截,而是会进行四舍五入,但是如果四舍五入后整数和小数位数之和大于定义时规定的位数,MySQL 仍然会进行拦截
  • unsigned float 类型的数据的取值范围就是 float 类型的取值范围去掉负数部分
  • 不指定 (M, D) 时,FLOAT 就是标准的 4 字节单精度,DOUBLE 就是标准的 8 字节双精度,完全交给硬件处理(等价于 c 语言的 float 类型),不额外限制小数位数。
  • double 类型的使用与 float 完全一样,只是精度更高
  • decimal整数最大位数 m 为 65。支持小数最大位数 d 是30。如果 d 被省略,默认为 0.如果 m 被省略, 默认是 10。

字符类型

char 类型

语法:

char(L): 固定长度字符串,L是可以存储的长度,单位为字符,最大长度值可以为255

注意:

  • 在 MySQL 中,L 表示可以存储的长度,但并不是像 c/c++ 语言那样,比如 "中国" 在 utf8 中总共占 6 字节,在 c/c++ 语言的长度是 6,但是在 MySQL 中 “中国” 的长度是 2,即 MySQL 是真的有几个字符,长度就是几。
  • 可以插入长度小于 L 的字符,但是如果长度大于 L,MySQL 会拦截
mysql> create table tt9(id int, name char(2));
Query OK, 0 rows affected (0.00 sec)
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 | 中国   |
+------+--------+

varchar 类型

语法:

varchar(L): 可变长度字符串,L表示字符长度,最大长度65535个字节

说明:

关于varchar(len),len的最大值,和表的编码密切相关。varchar长度可以指定为0到65535之间的值,但是有1 - 3 个字节用于记录数据大小,所以说有效字节数是65532。

  • 如果编码是utf8,varchar(n)的参数n最大值是65532/3=21844(因为utf中,一个字符占用3字节)
  • 如果编码是gbk,varchar(n)的参数n最大是65532/2=32766(因为gbk中,一个字符占用2字节)
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 | 我爱你,中国         |
+------+--------------------+ 

从上面的使用中,我们没有看出 varchar 和 char 的区别,那么它们的区别是什么?

varchar 和 char 的区别

char 类型是先开辟 L 长度的空间,但你可能只使用了其中的一部分,而 varchar 类型是使用多少就开辟多少,但不要超过最大限度。

如何选择定长或变长字符串

  • 如果数据确定长度都一样,就使用定长(char),比如:身份证,手机号,md5
  • 如果数据长度有变化,就使用变长(varchar), 比如:名字,地址,但是你要保证最长的能存的进去。
  • 定长的磁盘空间比较浪费,但是效率高。
  • 变长的磁盘空间比较节省,但是效率低。

日期与时间类型

类型字节范围精度时区影响场景
DATE3'1000-01-01' ~ '9999-12-31'生日、财报日
TIME3'-838:59:59' ~ '838:59:59'时长、时间段
DATETIME5~8'1000-01-01 00:00:00' ~ '9999-12-31 23:59:59'微秒(MySQL 5.6+)订单创建时间(业务时间)
TIMESTAMP4'1970-01-01 00:00:01' UTC ~ '2038-01-19 03:14:07' UTC(自动转UTC)系统时间戳、日志(含时区转换)
YEAR11901 ~ 2155年份单独存储

常用的日期有三个: date、datetime、timestamp

注意:

如果某个表包含 timestamp 这一列,那么往后不需要对 timestamp 做任何插入,当创建 timestamp 这一列时,timestamp 自动初始化为当前时间,往后对包含 timestamp 的表的其他列做任何修改时,timestamp 都会自动更新为当前时间,而其他类型并不会更新

//创建表
mysql> create table birthday (t1 date, t2 datetime, t3 timestamp);
Query OK, 0 rows affected (0.01 sec)

//插入数据:
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 | 
+------------+---------------------+---------------------+
                                    t3:添加数据时,时间戳初始化为当前时间

//更新数据:
mysql> update birthday set t1='2000-1-1';
Query OK, 1 row affected (0.00 sec)
Rows matched: 1  Changed: 1  Warnings: 0
mysql> select * from birthday;
+------------+---------------------+---------------------+
| t1         | t2                  | t3                  |
+------------+---------------------+---------------------+
| 2000-01-01 | 2008-08-08 12:01:01 | 2017-11-12 18:32:09 | 
+------------+---------------------+---------------------+
                                    t3:更新数据,时间戳会更新成当前时间

enum 和 set 类型

enum:枚举,“单选”类型;

语法:

enum('选项1','选项2','选项3',...);

该设定只是提供了若干个选项的值,最终一个单元格中,实际只存储了其中一个值;而且出于效率考 虑,这些值实际存储的是“数字”,因为这些选项的每个选项值依次对应如下数字:1,2,3,....最多65535 个;当我们添加枚举值时,也可以添加对应的数字编号。

set:集合,“多选”类型;

语法:

set('选项1','选项2','选项3', ...);

该设定只是提供了若干个选项的值,最终一个单元格中,设计可存储了其中任意多个值;而且出于效率 考虑,这些值实际存储的是“数字”,因为这些选项的每个选项值依次对应如下数字:1,2,4,8,16,32,.... 最多64个。

注意:

  • 不管是枚举类型还是集合类型,插入的数据如果是字符那么必须是定义时出现的选项,如果不是,MySQL会拦截
  • 对于枚举类型,插入时可以输入定义时字符的下标(从 1 开始)
  • 对于集合类型,插入时也可以输入数字,但这个数字就不是下标了,而是位图,它的每一位从低位到高位与定义集合类型时选项从左到右一一对应,0 表示不插入,1 表示插入。特殊情况:如果输入 0,可以插入成功,但不是 NULL,而是 ‘’。

案例

 有一个调查表votes,需要调查人的喜好, 比如(登山,游泳,篮球,武术)中去选择(可以多选), (男,女)[单选]

mysql-> create table votes(
     -> username varchar(30),
     -> gender enum('男','女')); 
     -> hobby set('登山','游泳','篮球','武术'),     
Query OK, 0 rows affected (0.02 sec)

insert into votes values('张三','男','登山,武术');
insert into votes values('Juse',2,'登山,武术');
insert into votes values('李四','男',10); // 10 -> 1010(二进制),表示插入:'登山','篮球
select * from votes where gender=2;
+----------+---------------+--------+
| username | hobby         | gender |
+----------+---------------+--------+
| Juse     | 登山,武术      |女       |
+----------+---------------+--------+

对 enum 和 set 类型的查询

mysql> select * from votes where hobby='登山';

注意:

对于 set 类型的查询,上面的方式是严格查询,即只会查询到爱好只有“登山”的,不会查询到爱好有“登山”和“武术”的。如果不想严格匹配,就要用 find_in_set 函数:

mysql> select * from votes where hobby=find_in_set('登山',hobby);

匹配条件如果不止一个:

mysql> select * from votes where hobby=find_in_set('登山',hobby) and find_in_set('代码',hobby);

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值