【MySQL】数据类型

文章目录

一、数据类型分类


二、数值类型

下表就是关于MySQL提供的所有整数类型。单是整数类型就提供了五种方案,这主要是为了满足不同场景的需求 。例如年龄字段,使用tinyint类型就可以覆盖绝大多数情况了,也就是说1 Byte就可以解决;但如果使用smallint或者mediumint,就会造成空间浪费。虽然看着只有一两个字节的差距,但一个项目的数据库的数据往往是以万为单位的,累加起来就是一个非常庞大的数字。因此,提供如此多的种类,也是为了避免空间浪费,节省空间

类型 字节 最小值(有符号/无符号) 最大值(有符号/无符号)
tinyint 1 -2^7 / 0 2^7 - 1 / 2^8 - 1
smallint 2 -2^15 / 0 2^15 - 1 / 2^16 - 1
mediumint 3 -2^23 / 0 2^23 - 1 / 2^24 - 1
int 4 -2^31 / 0 2^31 - 1 / 2^32 - 1
bigint 8 -2^63 / 0 2^63 - 1 / 2^64 - 1

2.1、tinyint

虽然MySQL提供了五种整数类型,但是它们的区别仅仅在于可表示数值范围上,因此,我们仅需要了解一个,便可无痛地理解其他四个类型。我们以tinyint为例。

sql 复制代码
mysql> create database test2;
Query OK, 1 row affected (0.01 sec)

mysql> use test2;
Database changed
mysql> create table t1(num tinyint);
Query OK, 0 rows affected (0.08 sec)

mysql> insert into t1 values(0);
Query OK, 1 row affected (0.00 sec)

mysql> insert into t1 values(127);		# 上限
Query OK, 1 row affected (0.01 sec)

mysql> insert into t1 values(-128);		# 下限
Query OK, 1 row affected (0.01 sec)

mysql> insert into t1 values(128);		# 超出范围边界
ERROR 1264 (22003): Out of range value for column 'num' at row 1
mysql> insert into t1 values(-129);		# 超出范围边界
ERROR 1264 (22003): Out of range value for column 'num' at row 1
mysql> select * from t1;
+------+
| num  |
+------+
|    0 |
|  127 |
| -128 |
+------+
3 rows in set (0.00 sec)

在MySQL中,整型可以指定是有符号还是无符号的,默认是有符号的 。在建表的时候,我们可以通过unsigned来说明某一个字段是无符号的。关于使用usigned的时机,取决于场景。

sql 复制代码
mysql> create table t2(num tinyint unsigned);		# 指定无符号tinyint
Query OK, 0 rows affected (0.04 sec)

mysql> insert into t2 values(0);		# 下限
Query OK, 1 row affected (0.01 sec)

mysql> insert into t2 values(255);		# 上限
Query OK, 1 row affected (0.01 sec)

mysql> insert into t2 values(123);
Query OK, 1 row affected (0.00 sec)

mysql> insert into t2 values(256);		# 超出范围边界
ERROR 1264 (22003): Out of range value for column 'num' at row 1
mysql> insert into t2 values(-1);		# 超出范围边界
ERROR 1264 (22003): Out of range value for column 'num' at row 1
mysql> select * from t2;
+------+
| num  |
+------+
|    0 |
|  255 |
|  123 |
+------+
3 rows in set (0.00 sec)

以上两个案例中,展示了合法数据的插入,以及插入不合法数据的后果。可以观察到,如果我们向mysql指定的类型插入不合法的数据,MySQL一般都是直接拦截我们的,不让我们操作。 反过来说,如果我们已经有数据被成功插入MySQL中,那么插入的时候一定是合法数据

因此,对MySQL来说,数据类型本身就是一种"约束"。对于初学者而言,"约束"这个概念相对而言是比较陌生的。在平时的代码编写中,编译器是否也会提供一种"约束"呢??当然,例如一个for循环,如果我们错误地使用它的语法格式,编译器便会警告,倒逼着程序员修改为正确的格式。MySQL也同样如此,通过"约束",倒逼程序员正确地插入数据,由此可以看出是用来约束使用者的,如果你不是一个很好的使用者,MySQL也能保证数据插入的合法性

C/C++中,我们将一个int类型的变量赋值给char类型的变量,编译器会报错吗?由经验来看,显然是不会的,它会发生数据截断。但是MySQL不能,它严格约束各字段的范围,在整个项目中,数据库往往是最后一道关卡,他必须保证存入的数据是正确的、合法的,而不能产生严重偏差。

关于约束更加详细的内容,会在后续章节详细阐述。


2.2、bit

🐬语法:

sql 复制代码
bit[(M)]:位字段类型。M表示每个值的的位数,范围1~64。默认为1。

bit类型很好理解,就是以bit为单位定义字段长度。

sql 复制代码
mysql> create table t3(id int, a bit(8));
Query OK, 0 rows affected (0.04 sec)

mysql> insert into t3 values(10, 10);
Query OK, 1 row affected (0.02 sec)

mysql> insert into t3 values(10, 1);
Query OK, 1 row affected (0.01 sec)

mysql> select * from t3;
+------+------------+
| id   | a          |
+------+------------+
|   10 | 0x0A       |
|   10 | 0x01       |
+------+------------+
2 rows in set (0.01 sec)

可以看到,a字段最终是以十六进制回显的。

补充:在一些版本较低的MySQL中,此处可能什么也观察不到,因为它们是以ASCII码 回显的!!

MySQL 5.6.37版本引入了--binary-as-hex 选项,但在8.0.19之前默认是关闭的,需要手动启用。

MySQL 8.0.19之前的版本中,MySQL客户端默认不启用--binary-as-hex选项。此时,对于bit类型的二进制数据,客户端便会尝试将其作为ASCII码来解释并显示;MySQL 8.0.19及之后版本开始,MySQL客户端默认启用了--binary-as-hex选项。启用后,客户端会将所有二进制数据统一以0x开头的十六进制格式显示。

在实际的业务场景中,如果我们有这样的值,只存放0或者1,这时就可以将字段类型定义为bit(1)。这样可以节省空间。

sql 复制代码
mysql> create table t4(gender bit(1));
Query OK, 0 rows affected (0.03 sec)

mysql> insert into t4 values(0);
Query OK, 1 row affected (0.01 sec)

mysql> insert into t4 values(1);
Query OK, 1 row affected (0.01 sec)

mysql> insert into t4 values(2);		# 越界
ERROR 1406 (22001): Data too long for column 'gender' at row 1
mysql> select * from t4;
+----------------+
| gender         |
+----------------+
| 0x00           |
| 0x01           |
+----------------+
2 rows in set (0.01 sec)

2.3、浮点数类型

2.3.1、float

🐬语法:

sql 复制代码
float[(m, d)] [unsigned] : m指定显示长度,d指定小数位数,占用空间4个字节。

例如,float(4,2)可表示的范围为-99.99~99.99,MySQL在保存数据的时候会采用四舍五入,如果四舍五入后的数据超出了数据表示范围,就会报错。

sql 复制代码
mysql> create table t5(id tinyint, salary float(4, 2));
Query OK, 0 rows affected, 1 warning (0.06 sec)

mysql> desc t5;
+--------+------------+------+-----+---------+-------+
| Field  | Type       | Null | Key | Default | Extra |
+--------+------------+------+-----+---------+-------+
| id     | tinyint    | YES  |     | NULL    |       |
| salary | float(4,2) | YES  |     | NULL    |       |
+--------+------------+------+-----+---------+-------+
2 rows in set (0.01 sec)

mysql> insert into t5 values(1, -99.99);
Query OK, 1 row affected (0.00 sec)

mysql> insert into t5 values(1, 99.99);
Query OK, 1 row affected (0.02 sec)

mysql> insert into t5 values(1, 100);		# 超出数据范围
ERROR 1264 (22003): Out of range value for column 'salary' at row 1
mysql> insert into t5 values(1, -100);		# 超出数据范围
ERROR 1264 (22003): Out of range value for column 'salary' at row 1
mysql> insert into t5 values(1, 99.991);		# 四舍五入后,没有超出数据范围
Query OK, 1 row affected (0.00 sec)

mysql> insert into t5 values(1, 99.995);		# 四舍五入后,超出数据范围
ERROR 1264 (22003): Out of range value for column 'salary' at row 1
mysql> select * from t5;
+------+--------+
| id   | salary |
+------+--------+
|    1 | -99.99 |
|    1 |  99.99 |
|    1 |  99.99 |
+------+--------+
3 rows in set (0.02 sec)

如果使用unsigned关键字修饰float类型,此时会将对应字段指定为无符号数,此时可表示的数据范围也许与我们的经验有所出入。

例如float(4,2) unsigned可表示的数据范围为0~99.99。直接在原基础上,清除了负数部分。

sql 复制代码
mysql> create table t6(salary float(4, 2) unsigned);
Query OK, 0 rows affected, 2 warnings (0.04 sec)

mysql> desc t6;
+--------+---------------------+------+-----+---------+-------+
| Field  | Type                | Null | Key | Default | Extra |
+--------+---------------------+------+-----+---------+-------+
| salary | float(4,2) unsigned | YES  |     | NULL    |       |
+--------+---------------------+------+-----+---------+-------+
1 row in set (0.00 sec)

mysql> insert into t6 values(-1);
ERROR 1264 (22003): Out of range value for column 'salary' at row 1
mysql> insert into t6 values(0);
Query OK, 1 row affected (0.01 sec)

mysql> insert into t6 values(99.99);
Query OK, 1 row affected (0.01 sec)

double类型同样也是用于表示浮点数类型,相比于float类型,仅存在数据范围上的差异,这里不做阐述。


2.3.2、decimal

🐬语法:

sql 复制代码
decimal[(m, d)] [unsigned] : m指定显示长度,d指定小数位数。

MySQL还提供了decimal类型来表示浮点数类型。在使用上,decimalfloat很像,但是也是存在区别的:decimalfloat可表示的精度不一样

sql 复制代码
mysql> create table t7(salary_1 float(10,8), salary_2 decimal(10, 8));
Query OK, 0 rows affected, 1 warning (0.14 sec)

mysql> insert into t7 values(10.12345678, 10.12345678);
Query OK, 1 row affected (0.01 sec)

mysql> select * from t7;
+-------------+-------------+
| salary_1    | salary_2    |
+-------------+-------------+
| 10.12345695 | 10.12345678 |
+-------------+-------------+
1 row in set (0.00 sec)

可以发现,decimal的精度更准确,因此,如果我们希望某个数据表示高精度,就可以选择decimal。像银行这种需要高精度的业务,一般推荐使用decimal

float 表示的精度大约是7位。decimal整数最大位数m65,支持小数最大位数d30。如果d被忽略,则默认为0m被忽略,则默认为10


三、字符类型

3.1、char

🐬语法:

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

**注意:**此处的单位为 "字符" ,与C/C++中的char有所区别。utf8中的汉字一般占三个字节,但是在MySQL中同样也是作为一个字符!!

sql 复制代码
mysql> create table t8(id int, name char(2));
Query OK, 0 rows affected (0.04 sec)

mysql> insert into t8 values(1, '张三');
Query OK, 1 row affected (0.00 sec)

mysql> insert into t8 values(2, 'ab');
Query OK, 1 row affected (0.01 sec)

mysql> insert into t8 values(3, 'abc');
ERROR 1406 (22001): Data too long for column 'name' at row 1
mysql> select * from t8;
+------+--------+
| id   | name   |
+------+--------+
|    1 | 张三   |
|    2 | ab     |
+------+--------+
2 rows in set (0.00 sec)

char(2)表示可以存放两个字符,可以是字符或者汉字,但是不可以超过两个,最大只能255。

sql 复制代码
mysql> create table t9(name char(256));
ERROR 1074 (42000): Column length too big for column 'name' (max = 255); use BLOB or TEXT instead

3.2、varchar

🐬语法:

sql 复制代码
varchar(L): 可变长度字符串,L表示字符长度,最大长度65535个字节。
sql 复制代码
mysql> create table t9(name varchar(20));
Query OK, 0 rows affected (0.03 sec)

mysql> insert into t9 values('hello world!');
Query OK, 1 row affected (0.01 sec)

mysql> insert into t9 values('你好,世界');
Query OK, 1 row affected (0.00 sec)

mysql> select * from t9;
+-----------------+
| name            |
+-----------------+
| hello world!    |
| 你好,世界      |
+-----------------+
2 rows in set (0.00 sec)

在语法介绍中,varchar类型的字段的最大长度为65535Byte,那么,参数L的最大值应该是多少呢??

varchar长度可以指定为0~65535之间的值,但是又1~3个字节用于记录数据大小,所以说有效的字节数为65532。当我们的表的编码是utf8时,varchar(L)的参数L的最大值是65532/3=21844;如果编码是gbkvarchar(L)的参数L最大是65532/2=32766(因为gbk中,一个字符占用2字节)。得出结论,在不同的编码下,参数L的最大值不同

sql 复制代码
mysql> create table t10(name varchar(21854));
ERROR 1074 (42000): Column length too big for column 'name' (max = 21845); use BLOB or TEXT instead
mysql> create table t10(name varchar(21845));
ERROR 1118 (42000): Row size too large. The maximum row size for the used table type, not counting BLOBs, is 65535. This includes storage overhead, check the manual. You have to change some columns to TEXT or BLOBs
mysql> create table t10(name varchar(21844));
Query OK, 0 rows affected (0.03 sec)

由上例可以看出,一行只有varchar时,最长为21844,但是MySQL中一行数据的大小是有上限的,恰好就是65535Byte,因此,如果一行中还有其他字段,varchar的参数只会比21844更小。`更小。


3.3、char与varchar的对比

在使用上,charvarchar给人的感觉几乎没有什么区别,事实上,它们之间的关系就类似于C++中arrayvector之间的关系。

char也称固定长度字符串,L参数为多少,底层就在磁盘中开辟多少空间;varchar称为变长字符串,L参数可以类比于vector中的capacity参数,它会根据用户实际使用的空间大小,动态地开辟磁盘空间,但是不会超过L。

在实际项目中,我们如何选择定长或变长字符串呢??

  1. 如果数据确定长度都一样,就使用定长(char),比如:身份证,手机号,md5。
  2. 如果数据长度有变化,就使用变长(varchar),比如:名字、地址,但是你要保证最长的能存的进去。
  3. 定长的磁盘空间比较浪费,但是效率高。
  4. 变长的磁盘空间比较节省,但是效率低。
  5. 定长的意义是,直接开辟好对应的空间
  6. 变长的意义是,在不超过自定义范围的情况下,用多少,开辟多少。

四、日期和时间类型

常用的日期类型有如下三个:

  1. date:日期 yyyy-mm-dd ,占用三字节。
  2. datetime:时间日期格式yyyy-mm-dd HH:ii:ss表示范围从10009999,占用八字节。
  3. timestamp:时间戳,从1970年开始的,格式和 datetime 完全一致,占用四字节。

datetimetimestamp格式完全相同,它们又分别运用于什么场景下呢??

当需要自动变化的时候或者与时间变化强相关的字段时,我们便可以采用timestamp。例如,电商/订单系统,追踪订单状态流转(待支付→已发货→已完成),记录支付时间、发货时间、状态更新时间; 全球化的跨国业务等。

sql 复制代码
mysql> create table t12(
    -> time1 date,
    -> time2 datetime,
    -> time3 timestamp default current_timestamp on update current_timestamp
    -> );
Query OK, 0 rows affected (0.07 sec)

mysql> insert into t12(time1, time2) values('2026-8-1','2026-8-1 12:0:0');
Query OK, 1 row affected (0.01 sec)

// time3给自动补齐了
mysql> select * from t12;
+------------+---------------------+---------------------+
| time1      | time2               | time3               |
+------------+---------------------+---------------------+
| 2026-08-01 | 2026-08-01 12:00:00 | 2026-08-18 17:05:02 |
+------------+---------------------+---------------------+
1 row in set (0.00 sec)

mysql> update t12 set time1='2006-6-18';
Query OK, 1 row affected (0.05 sec)
Rows matched: 1  Changed: 1  Warnings: 0

// 修改time1的同时,time3也随着一起更新
mysql> select * from t12;
+------------+---------------------+---------------------+
| time1      | time2               | time3               |
+------------+---------------------+---------------------+
| 2006-06-18 | 2026-08-01 12:00:00 | 2026-08-18 17:05:59 |
+------------+---------------------+---------------------+
1 row in set (0.00 sec)

注意:在很老的 MySQL 版本,表中第一个timestamp列会被"偷偷"加上自动更新 属性。但这个特性早就被废弃了,现代版本(5.7/8.0)要求必须显式声明,否则就是普通的可为空的列。在建表时显式指定default current_timestamp,再插入数据的时候,就会自动填充timestamp列;显示指定on update current_timestamp,更新该行其他数据时,timestamp列也会自动刷新


五、enum和set

5.1、enum

🐬语法:

sql 复制代码
enum:枚举,"单选"类型;
enum('选项1','选项2','选项3',...);

该设定只是提供了若干个选项的值,最终一个单元格中,实际只存储了其中一个值;而且出于效率考

虑,这些值实际存储的是"数字",因为这些选项的每个选项值依次对应如下数字:1、2、3... ...最多65535个;当我们添加枚举值时,也可以直接添加对应的数字编号。

sql 复制代码
mysql> create table t13(
    -> gender enum('男','女'),
    -> name varchar(10)
    -> );
Query OK, 0 rows affected (0.06 sec)

mysql> insert into t13 values('男', '张三');
Query OK, 1 row affected (0.02 sec)

mysql> insert into t13 values(2, '李四');		# 使用数字标识的时候,就是正常的元素下标
Query OK, 1 row affected (0.01 sec)

mysql> select * from t13;
+--------+--------+
| gender | name   |
+--------+--------+
| 男     | 张三   |
| 女     | 李四   |
+--------+--------+
2 rows in set (0.00 sec)

5.2、set

🐬语法:

sql 复制代码
set:集合,"多选"类型;
set('选项值1','选项值2','选项值3', ...);

该设定只是提供了若干个选项的值,最终一个单元格中,设计可存储了其中任意多个值;而且出于效率

考虑,这些值实际存储的是"数字",因为这些选项的每个选项值依次对应如下数字:1、2、4、8、16、32、... ...最多64个。不难发现,这些数字都是2的幂次,事实上,这里底层就是使用了位图的思想,与文件操作中open()系统调用中的flags参数如出一辙。

sql 复制代码
mysql> create table t14(
    -> name varchar(10),
    -> gender enum('男','女'),
    -> hobby set('登山','滑雪','游泳','乒乓球','篮球')
    -> );
Query OK, 0 rows affected (0.07 sec)

mysql> insert into t14 values('张三', '男', '游泳,登山');
Query OK, 1 row affected (0.02 sec)

mysql> insert into t14 values('李四', '女', '游泳,滑雪,篮球');
Query OK, 1 row affected (0.01 sec)

mysql> insert into t14 values('王五', '男', '乒乓球');
Query OK, 1 row affected (0.01 sec)

mysql> insert into t14 values('赵六', 2, '游泳');
Query OK, 1 row affected (0.01 sec)

mysql> insert into t14 values('田七', 1, 3);		# 采用数字
Query OK, 1 row affected (0.00 sec)

mysql> select * from t14;
+--------+--------+----------------------+
| name   | gender | hobby                |
+--------+--------+----------------------+
| 张三   | 男     | 登山,游泳            |
| 李四   | 女     | 滑雪,游泳,篮球       |
| 王五   | 男     | 乒乓球               |
| 赵六   | 女     | 游泳                 |
| 田七   | 男     | 登山,滑雪            |
+--------+--------+----------------------+
5 rows in set (0.00 sec)

5.3、find_ in_ set()

在前一小节的例子当中,我想利用查询语句找出所有爱好游泳的用户,当我们使用如下语句查找时,结果是不尽人意的。

sql 复制代码
mysql> select * from t14 where hobby='游泳';
+--------+--------+--------+
| name   | gender | hobby  |
+--------+--------+--------+
| 赵六   | 女     | 游泳   |
+--------+--------+--------+
1 row in set (0.00 sec)

该查询语句只能够严格地按照给出的hobby字段一一比较,并不能在集合内部进行对比。

对此,MySQL提供了专门用于集合查询的函数find_in_set()

🐬语法:

sql 复制代码
find_in_set(sub,str_list):如果sub在str_list中,则返回下标;如果不在,返回0。
sql 复制代码
mysql> select * from t14 where find_in_set('游泳', hobby);
+--------+--------+----------------------+
| name   | gender | hobby                |
+--------+--------+----------------------+
| 张三   | 男     | 登山,游泳            |
| 李四   | 女     | 滑雪,游泳,篮球       |
| 赵六   | 女     | 游泳                 |
+--------+--------+----------------------+
3 rows in set (0.00 sec)

当我们想要同时查找既喜欢游泳有喜欢篮球的人时应该如何做呢??

sql 复制代码
mysql> select * from t14 where find_in_set('游泳,篮球', hobby);		# 错误案例
Empty set (0.00 sec)

// 此处的and关键字就相当于C/C++中的&&
mysql> select * from t14 where find_in_set('游泳', hobby) and find_in_set('篮球', hobby);
+--------+--------+----------------------+
| name   | gender | hobby                |
+--------+--------+----------------------+
| 李四   | 女     | 滑雪,游泳,篮球       |
+--------+--------+----------------------+
1 row in set (0.00 sec)

完🥳🥳🥳

相关推荐
某不知名網友1 小时前
MySQL数据库库级操作|创建、字符集、修改删除、备份恢复
数据库·mysql
恋猫de小郭1 小时前
社区版 Dart macros?一个可以解决 JSON 序列化的Fluter “宏编程”第三方包
android·前端·flutter
mmsx1 小时前
Android Design 项目集合
android
黄毛火烧雪下2 小时前
智能眼镜无线adb调试
adb
某不知名網友3 小时前
MySQL数据表详解|创建、查看、修改、删除实战教程
数据库·mysql
游戏开发爱好者812 小时前
带签名时间戳接口的重放与压力测试实战,用动态值在发送前现算签名
网络协议·计算机网络·网络安全·ios·adb·https·压力测试
启雀AI16 小时前
培训平台移动端离线学习方案设计与实现:视频缓存、断点续传与进度同步的工程实践
android·学习·缓存·音视频·企业lms
01_ice16 小时前
MySQL库和表的操作
数据库·mysql
我命由我1234517 小时前
RxJava - 冷数据流与热数据流
android·java·java-ee·android studio·rxjava·android-studio·android runtime