【深入浅出MySQL】之数据类型介绍
【深入浅出MySQL】之数据类型介绍
- MySQL中常见的数据类型一览
- 为什么需要如此多的数据类型
- 数值类型
- BIT(M)类型
- INT类型
- TINYINT类型
- BIGINT类型
- 浮点数类型
- float类型
- DECIMAL(M,D)类型
- 区别总结
- 字符串类型
- CHAR类型
- VARCHAR(M)类型
- 日期和时间类型
- enum和set类型
前言:MySQL是一种广泛使用的关系型数据库管理系统,它提供了多种数据类型供开发者去选择,以此来满足不同的场景。
MySQL中常见的数据类型一览
类别 | 数据类型 | 描述 | 存储范围/长度 | 常见用途 |
---|---|---|---|---|
数值类型 | TINYINT | 非常小的整数 | -128 到 127(有符号);0 到 255(无符号) | 存储小的整数,如状态码(0/1)、年龄等 |
BITM | 位类型,M 指定位数,默认值1 | M 的范围(1~64) | 适用于需要存储二进制信息的情况,例如标志位、布尔值数组等。通过使用 BIT 类型,可以更有效地存储和操作位级别的数据。 | |
SMALLINT | 小整数 | -32,768 到 32,767(有符号);0 到 65,535(无符号) | 存储中等范围的整数,如计数器、小范围的ID | |
MEDIUMINT | 中等大小的整数 | -8,388,608 到 8,388,607(有符号);0 到 16,777,215(无符号) | 存储中等范围的整数,较少使用 | |
INT / INTEGER | 标准整数 | -2,147,483,648 到 2,147,483,647(有符号);0 到 4,294,967,295(无符号) | 存储常用整数,如用户ID、订单号等 | |
BIGINT | 大整数 | -9,223,372,036,854,775,808 到 9,223,372,036,854,775,807(有符号) | 存储大范围整数,如大型系统的ID、计数器 | |
FLOAT | 单精度浮点数 | 约 ±3.4E+38(7位有效数字) | 存储近似数值,如价格、科学计算数据 | |
DOUBLE | 双精度浮点数 | 约 ±1.79E+308(15位有效数字) | 存储更高精度的近似数值,如金融数据、科学计算 | |
DECIMAL(M,D) | 定点数(精确小数) | M 是总位数,D 是小数位数,例如 DECIMAL(10,2) 存储 8位整数+2位小数 | 存储精确小数,如货币金额(避免浮点误差) | |
字符串类型 | CHAR(M) | 固定长度字符串 | 0 到 255 个字符 | 存储固定长度的字符串,如状态码、性别(‘M’/‘F’) |
VARCHAR(M) | 可变长度字符串 | 0 到 65,535 个字符(取决于字符集和存储引擎) | 存储可变长度的字符串,如用户名、地址 | |
TINYTEXT | 短文本字符串 | 最大 255 个字符 | 存储短文本,如备注、小段描述 | |
TEXT | 标准文本字符串 | 最大 65,535 个字符 | 存储较长的文本,如文章内容、评论 | |
MEDIUMTEXT | 中等长度文本字符串 | 最大 16,777,215 个字符 | 存储中等长度的文本,如长篇文章 | |
LONGTEXT | 超长文本字符串 | 最大 4,294,967,295 个字符 | 存储超长文本,如日志文件、JSON 数据 | |
ENUM('value1', 'value2', ...) | 枚举类型,只能从预定义值中选择一个值 | 最多 65,535 个不同值 | 存储固定选项,如状态(‘active’/‘inactive’)、性别(‘male’/‘female’) | |
SET('value1', 'value2', ...) | 集合类型,可以选择多个预定义值(以逗号分隔) | 最多 64 个不同值 | 存储多选选项,如兴趣(‘reading,swimming’) | |
日期和时间类型 | DATE | 日期 | 1000-01-01 到 9999-12-31 | 存储日期,如生日、注册日期 |
TIME | 时间 | -838:59:59 到 838:59:59 | 存储时间,如一天中的时间点、持续时间 | |
DATETIME | 日期和时间组合 | 1000-01-01 00:00:00 到 9999-12-31 23:59:59 | 存储完整的日期和时间,如创建时间、更新时间 | |
TIMESTAMP | 时间戳(从 1970-01-01 00:00:00 UTC 开始) | 1970-01-01 00:00:00 到 2038-01-19 03:14:07(UTC) | 存储时间戳,常用于记录数据更新时间(自动更新) | |
YEAR | 年份 | 1901 到 2155(4位格式);70 到 69(2位格式,1970-2069) | 存储年份,如出生年份、发布年份 | |
其他类型 | BINARY(M) | 固定长度的二进制字符串 | 0 到 255 个字节 | 存储固定长度的二进制数据,如校验码 |
VARBINARY(M) | 可变长度的二进制字符串 | 0 到 65,535 个字节 | 存储可变长度的二进制数据,如小型文件 | |
BLOB | 二进制大对象 | 最大 65,535 个字节 | 存储二进制数据,如图片、文件 | |
MEDIUMBLOB | 中等长度的二进制大对象 | 最大 16,777,215 个字节 | 存储中等大小的二进制数据,如较大的文件 | |
LONGBLOB | 超长二进制大对象 | 最大 4,294,967,295 个字节 | 存储超大二进制数据,如视频文件 | |
JSON | JSON 格式数据(MySQL 5.7+ 支持) | 最大 1GB(受限于存储引擎) | 存储 JSON 数据,如配置信息、动态字段 |
为什么需要如此多的数据类型
似乎单一数据类型如字符类型可以存储所有的数据,但事实上,我们需要如此多的数据类型的原因有以下几种:
- 数据的准确性与合法性需求:有时候我们描述某一个列时,如果这个列只能是整数,而不能出现小数点(如
id
),此时如果只有字符类型就满足不了需求,而整数类型和浮点数类型的存在就可以有效防止非法的数据进入数据库中。 - 性能上:
- 空间上:如果只有单一的
INT
类型的整数,有时候某些列的大小永远不会超过某一个整数值,这个时候使用INT
就很浪费空间,所以使用 TINYINT 来存储 0 到 255 范围内的整数比使用 VARCHAR 更节省空间。 - 时间上:对于数值运算,直接使用数值类型而不是字符串类型进行计算会更加高效。这是因为数值类型可以直接参与算术运算,而字符串则需要先转换成数值形式才能进行相应的操作,这增加了额外的处理开销。
- 空间上:如果只有单一的
- 功能上:
- 日期时间处理:专门的日期时间类型(如
DATE
,TIME
,DATETIME
)提供了丰富的函数支持,方便进行日期计算、格式化输出等操作。如果用字符串表示日期时间,则需要手动编写代码来进行这些操作,不仅复杂而且容易出错。
- 日期时间处理:专门的日期时间类型(如
- 查询优化:
- 索引利用:某些数据类型允许创建特定类型的索引(如全文索引适用于 TEXT 类型),从而提高查询能力。如果所有数据都以字符串形式存储,则可能无法充分利用这些高级索引功能。
数值类型
BIT(M)类型
BIT是位类型,其中
M
是位数,如果你想精确控制该列的位数,可以使用这个类型。
-
创建一个表
tt1
,表中有一个num
列,它的类型是BIT(1)
: -
表创建成功了:
-
向表中插入数据:
- 超过
1
就插入失败,因为bit
位的位数位1,只能表示0
、1
。 - 但是发现一个很奇怪的现象,就算查表,1没有显示出来。
- 超过
-
这是因为
bit
类型是默认是按照ASCII
码,1
对应的符号是不可显示的特殊符号。 -
测试一下
bit
类型位数的边界情况:create table tt3(num bit(65)); create table tt4(num bit(0));
[!tip]
如果我们有一些列,只需要
0
或者1
就可以使用bit(1)
,这样非常节省空间。
INT类型
INT类型的范围:-2,147,483,648 到 2,147,483,647(有符号);0 到 4,294,967,295(无符号)
这和我们C语言中的是相符的。
但C语言中溢出后会截断,数据库是否会这样呢?
-
首先我们创建表
tt4
:create table tt4(num int);
-
查看表
tt4
:int
后面的数字的含义:这是表示的默认宽度,通常配合ZEROFILL
属性使用,这个后面我们再谈,和C语言中的printf
的格式控制符很像。
-
插入一个数字,我们测试
int
的边界,插入2147483647
、2,147,483,648
,-2,147,483,648
、-2,147,483,649
:
TINYINT类型
TINYINT
也是整数类型,但是它只能表示-128 到 127(有符号),0 到 255(无符号)当你的列需要一个整数类型来表示,但是不会超过255
时就可以使用这个类型。
-
创建表
tt5
:create table tt5(num tinyint);
-
越界测试:
insert into tt5 values(128);
BIGINT类型
大整数类型,它能表示-9,223,372,036,854,775,808 到 9,223,372,036,854,775,807(有符号),适合做大型系统的
id
列的类型、计数器等。
-
创建表
tt6
:create table tt6(num bigint);
-
数据类型边界测试:
insert into tt6 values(-9223372036854775808);
浮点数类型
float类型
语法(syntax):
float[(m, d)] [unsigned] : M指定显示长度,d指定小数位数,占用空间4个
-
创建表
tt7
,它有一个num
列是float(5,2)
类型,长度为5,小数位数为2,它可以表示999.99 ~ -999.99
的值:create table tt7(float(5,2));
-
插入一些值观察现象:
mysql> insert into tt7 values(999.99999999); ERROR 1264 (22003): Out of range value for column 'num' at row 1 mysql> insert into tt7 values(999.99); Query OK, 1 row affected (0.00 sec)mysql> insert into tt7 values(999.93); Query OK, 1 row affected (0.00 sec)mysql> insert into tt7 values(-999.999); ERROR 1264 (22003): Out of range value for column 'num' at row 1 mysql> insert into tt7 values(-999.99); Query OK, 1 row affected (0.00 sec)mysql> insert into tt7 values(-9999); ERROR 1264 (22003): Out of range value for column 'num' at row 1 mysql> insert into tt7 values(-99);Query OK, 1 row affected (0.00 sec) mysql> select * from tt7; +---------+ | num | +---------+ | 999.99 | | 999.93 | | -999.99 | | -99.00 | +---------+
- 可以看到依旧会发生越界的情况。
-
如果我们插入的值长度超过
M
,但是范围又在此时[M,d]所限定的范围内,就会发生四舍五入的情况:
DECIMAL(M,D)类型
M 是总位数,D 是小数位数,例如 DECIMAL(10,2) 存储 8位整数+2位小数
听上去好像和float
没什么区别呀,我们来建个表插入数据看看:
-
创建表
tt8
,它有一列num,数据类型为decimal(5,2)
,存储3位整数+2位小数:create table tt8(num decimal(5,2));
-
插入一些值,然后观察现象:
insert into tt8 values(999.976) ...
- 它似乎也和
float
一样也会进行越界判断,也会四舍五入,那它们有什么区别呢?
区别总结
我们从实际的现象来观察并思考为什么会这样。
-
创建表
tt9
:create table tt9(num1 float(10,1),num2 decimal(10,1));
-
插入下面的值:
insert into tt9(999999998,999999998);
-
表中最终的结果:
mysql> select * from tt9; +--------------+-------------+ | num1 | num2 | +--------------+-------------+ | 1000000000.0 | 999999998.0 | +--------------+-------------+
- 居然!,
num1
被近似成了1000000000.0
,而且总长度是11位,我们不是规定了总长度吗?这个num2
是正常显示的。
- 居然!,
-
我们手动插入一下一行让
num1
为1000000000.0
,看能否成功:mysql> insert into tt9 values(1000000000.0,999999998); ERROR 1264 (22003): Out of range value for column 'num1' at row 1
- 很明显失败了,
MySQL
报错了。
- 很明显失败了,
总结:
FLOAT
是近似类型:它不保证精确存储和显示,可能会对大数值进行近似处理。DECIMAL
是精确类型:它严格按照定义的范围和精度存储和显示数据。- 显示宽度不受严格限制:FLOAT(M,D) 的定义主要用于限制存储范围(也就是限制用户的),但实际显示的宽可能会因为近似超出定义的范围。
所以如果我们的列对数据的精确性要求很高,且是浮点数,就需要使用DECIMAL
类型。
字符串类型
CHAR类型
语法:char(L):固定长度字符串,不管用户输入的字符串的长度为多少,
MySQL
都会拿出L
的长度给该列,L
的最大值是255
。
[!caution]
MySQL
里面的一个长度就对应一个字符,不管你是中文、英文字符、还是特殊字符都只占一个长度单位,也就是说MySQL
对于字符长度有自己的标准。在 MySQL 中,字符长度单位指的是字符的数量,而不是字节数。但是,实际占用的存储空间取决于字符集。
下面我们创建表,插入一些值来验证一下:
-
创建表
tt10
:create table tt10(s char(5));
-
插入一些字符:
insert into tt10 values('你好世界呀'); insert into tt10 values('abcde'); insert into tt10 values('abcdef');
- 我们都知道实际上中文字符的存储字节和英文字符的存储字节一般是不同的,要看具体的存储编码,所以在 MySQL 中,字符长度单位指的是字符的数量,而不是字节数。
[!caution]
如果你插入的字符的长度比
L
小,MySQL
会自动填充空格。
VARCHAR(M)类型
varchar(L): 可变长度字符串,L表示字符长度,最大长度65535个字节
-
创建表
tt11
:create table tt11(s varchar(10));
-
插入一些字符串:
insert into tt11 values("111"); insert into tt11 values("1111111111");
[!caution]
变长并不是这个类型存储的字符的长度可以超过用户指定的长度
L
,而是当用户实际存储的字符长度没有L
时,MySQL
不会使用空格填充,这样节省了空间。
如何选择char
和varchar
类型:
- 如果指定列的字符串长度是固定的,就使用
char
类型。 - 当存储大量可变长度的字符串时,可以使用
varchar
来节省存储空间。 CHAR
因为是定长的,存储和检索效率更高,尤其是在频繁访问和更新的场景中。VARCHAR
因为需要额外的长度信息,存储和检索效率略低,但在现代数据库系统中,这种差异通常不明显。
日期和时间类型
MySQL中日期类型(如
DATE
、DATETIME
、TIMESTAMP
等)是非常重要的数据类型。它们的存在是为了更高效地存储、查询和操作与时间相关的数据。
日期类型可以表示广泛的日期范围,远超过普通字符串或数字能表达的范围。例如:
DATE
类型支持从 ‘1000-01-01’ 到 ‘9999-12-31’ 的日期。DATETIME
和TIMESTAMP
支持精确到秒甚至微秒的时间点。
下面我们创建一个表,使用一下日期类型:
-
创建表
tt12
:create table tt12(d1 date,d2 datetime,t timestamp);
-
插入一些值:
insert into tt12 values('1922-01-22','1922-01-22 00:00:00',FROM_UNIXTIME(1));
-
TIMESTAMP
不支持直接插入原始的 Unix 时间戳(如1742947200
),需要通过FROM_UNIXTIME()
函数进行转换。插入当前时间:可以使用NOW()
或CURRENT_TIMESTAMP
来插入当前时间。 -
DATETIME
存储日期和时间。 -
DATE
只存储日期。
-
enum和set类型
ENUM
(枚举)类型是一种字符串对象,其值范围必须来自一个预定义的列表。这些值是按定义顺序排列的,并且只能选择列表中的值之一。SET 类型是一种字符串对象,它可以包含零个或多个由逗号分隔的值,这些值来自于一个预定义的列表。与 ENUM 不同的是,SET 允许一个字段包含多个值。
使用介绍:
-
创建表
tt13
:create table tt13(identity enum('学生','老师','工人'), set permissions('write','read','exec'));
-
插入一些值:
mysql> insert into tt13 values('学生','write,res'); ERROR 1265 (01000): Data truncated for column 'permissions' at row 1 mysql> insert into tt13 values('学生','write,read'); Query OK, 1 row affected (0.00 sec)mysql> insert into tt13 values('学生','write'); Query OK, 1 row affected (0.00 sec)mysql> insert into tt13 values('学生,老师','write'); ERROR 1265 (01000): Data truncated for column 'identity' at row 1 mysql> insert into tt13 values('学生1','write'); ERROR 1265 (01000): Data truncated for column 'identity' at row 1
应用场景:
- 使用
ENUM
:- 当字段的值是单一选项时。
- 需要确保数据一致性且选项数量较少。
- 场景示例:性别、状态、分类等。
- 使用
SET
:- 当字段的值是多个选项的组合时。
- 需要灵活的多选功能且选项数量较少。
- 场景示例:权限、兴趣爱好、标签等。