MySQL笔记---数据类型
MySQL 提供了丰富的数据类型,用于定义表中列的存储格式和取值范围。选择合适的数据类型能提高存储效率、保证数据完整性。
相比于C语言中的数据类型,SQL语言中的数据类型更像是一种约束条件,数据类型既不会发生类型转换,也不会发生数据截断(除了浮点型小数部分会四舍五入),只能插入完全符合某种数据类型要求的数据。
以下是常用数据类型的分类及详解:
1. 数值型
用于存储整数、小数等数值,分为整数型和小数型。
定义列:
列名 类型 [UNSIGNED] [约束条件]
1.1 整数型(精确整数)
类型 | 存储空间 | 有符号范围(默认) | 无符号范围(UNSIGNED) | 说明 |
TINYINT | 1 字节 | -128 ~ 127 | 0 ~ 255 | 微小型整数(如状态标记) |
SMALLINT | 2 字节 | -32768 ~ 32767 | 0 ~ 65535 | 小型整数(如数量) |
MEDIUMINT | 3 字节 | -8388608 ~ 8388607 | 0 ~ 16777215 | 中型整数 |
INT | 4 字节 | -2147483648 ~ 2147483647 | 0 ~ 4294967295 | 常用整数(如 ID、年龄) |
BIGINT | 8 字节 | -9223372036854775808 ~ 9223372036854775807 | 0 ~ 18446744073709551615 | 大型整数(如订单号、时间戳) |
示例:
CREATE TABLE test(age SMALLINT UNSIGNED, -- 年龄(0~120,用无符号SMALLINT更节省空间)order_id BIGINT -- 订单号(数值较大,用BIGINT)
);
1.2 小数型(浮点数 / 定点数)
用于存储带小数的数值,分浮点数(精度有限,可能有误差)和定点数(精确,无误差)。
类型 | 存储空间 | 说明 |
FLOAT(M, D) | 4 字节 | 单精度浮点数。M总位数,D小数位数(M最大 25,D最大 17) |
DOUBLE(M, D) | 8 字节 | 双精度浮点数。M最大 53,D最大 30(精度比 FLOAT 高,误差更小) |
DECIMAL(M, D) | 可变 | 定点数(精确计算)。M总位数(最大 65),D小数位数(最大 30) |
区别与选择:
- FLOAT/DOUBLE:适合非精确计算(如科学数据),存储效率高但可能有四舍五入误差。
- DECIMAL:适合精确计算(如金额、价格),无误差但存储效率较低。
1.3 bit类型
BIT 类型是一种二进制数值类型,用于存储位值(0 或 1 组成的二进制数据),适合需要紧凑存储二进制状态的场景(如开关状态、标志位等)。
BIT(M)
M
表示存储的位数,范围是1 ~ 64
(默认1
)。- 空间效率高:BIT(M) 占用的存储空间为 CEIL(M/8) 字节(向上取整)。例如:
- BIT(1) 占用 1 字节(实际仅用 1 位,其余 7 位空闲);
- BIT(9) 占用 2 字节(8 位用 1 字节,第 9 位需额外 1 字节)。
- 存储二进制值:本质是存储二进制数,而非字符串。插入时可传入十进制数、二进制字面量(b'101')或十六进制字面量(0x5)。
- 直接查询 BIT 类型列会返回二进制原始值(可能显示为乱码),需用函数转换为可读形式:
- BIN(N):转换为二进制字符串(带 b'...' 前缀);
- HEX(N):转换为十六进制字符串;
- CAST(N AS UNSIGNED):转换为十进制整数。
2. 字符串型
用于存储文本、字符等数据,分短字符串、长文本、二进制数据等。
列名 类型 [约束条件]
2.1 短字符串(固定 / 可变长度)
类型 | 存储特点 | 长度限制 | 适用场景 |
CHAR(M) | 固定长度(不足补空格,查询时自动去除) | M最大 255 字符 | 长度固定的字符串(如手机号、身份证号) |
VARCHAR(M) | 可变长度(只占实际内容 + 1~2 字节长度标记) | M最大 65535 字符 | 长度不固定的字符串(如姓名、地址) |
区别与选择:
- CHAR:查询速度快,但浪费空间(如CHAR(10)存 "abc" 占 10 字节)。
- VARCHAR:节省空间,但查询时需计算长度,速度略慢。
注意:一个中文字符占三个字节(三个英文字符长度),所以VARCHAR类型最多能存储的中文字符串的长度为
2.2 长文本
用于存储超过VARCHAR长度的大段文本(如文章、描述)。
类型 | 最大长度(约) | 说明 |
TINYTEXT | 255 字节 | 微型文本(如短备注) |
TEXT | 65535 字节(64KB) | 普通文本(如评论) |
MEDIUMTEXT | 16777215 字节(16MB) | 中型文本(如文章内容) |
LONGTEXT | 4GB | 大型文本(如日志、长文档) |
注意:实际开发中,通常不直接存储大文件到数据库,而是存储文件路径,文件本身存在服务器磁盘。
3. 日期时间型
用于存储日期、时间或日期 + 时间,精度到秒。
类型 | 格式 | 范围 | 特点与场景 |
DATE | YYYY-MM-DD | 1000-01-01 ~ 9999-12-31 | 仅日期(如生日、注册日期) |
TIME | HH:MM:SS | -838:59:59 ~ 838:59:59 | 仅时间(如时长、打卡时间) |
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 ~ 2038-01-19 03:14:07 | 日期 + 时间(受时区影响,自动更新) |
YEAR | YYYY | 1901 ~ 2155 | 仅年份(如毕业年份) |
关键区别:
- DATETIME:存储时不依赖时区,插入时是什么时间,查询时就是什么时间。
- TIMESTAMP:存储的是 UTC 时间戳,查询时会根据数据库时区转换,适合跨时区场景;且可设置自动更新(如ON UPDATE CURRENT_TIMESTAMP)。
示例:
CREATE TABLE testTime(-- 生日(仅日期)birth_day DATE,-- 订单创建时间(日期+时间,不受时区影响)create_time DATETIME DEFAULT CURRENT_TIMESTAMP,-- 最后更新时间(自动更新)update_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
);
Query OK, 0 rows affected (0.02 sec)mysql> INSERT INTO testTime (birth_day) VALUES ('2004-12-18');
Query OK, 1 row affected (0.00 sec)mysql> SELECT * FROM testTime;
+------------+---------------------+---------------------+
| birth_day | create_time | update_time |
+------------+---------------------+---------------------+
| 2004-12-18 | 2025-09-26 15:12:04 | 2025-09-26 15:12:04 |
+------------+---------------------+---------------------+
1 row in set (0.00 sec)
4. 特殊类型
4.1 ENUM(枚举)
只能从预定义的列表中选择一个值,适合状态、类型等固定选项的场景。
ENUM('值1', '值2', ..., '值N')
在插入时,可以直接插入列表中的值,也可以用列表中值的下标代替(从1开始)。
示例:
mysql> CREATE TABLE testEnum( gender ENUM('男', '女', '未知') DEFAULT '未知' );
Query OK, 0 rows affected (0.03 sec)mysql> INSERT INTO testEnum VALUES (1);
Query OK, 1 row affected (0.00 sec)mysql> INSERT INTO testEnum VALUES ('女');
Query OK, 1 row affected (0.00 sec)mysql> INSERT INTO testEnum VALUES (3);
Query OK, 1 row affected (0.01 sec)mysql> SELECT * FROM testEnum;
+--------+
| gender |
+--------+
| 男 |
| 女 |
| 未知 |
+--------+
3 rows in set (0.00 sec)
4.2 SET(集合)
可以从预定义的列表中选择零个或多个值(最多 64 个选项)。
SET('值1', '值2', ..., '值N')
插入多个值时,有两种方法:
- 多个值之间用逗号隔开;
- 二进制数值的形式表示要插入哪些值(最低位表示第一个值)。
示例:
mysql> CREATE TABLE testSet( name VARCHAR(20), hobby SET('唱', '跳', 'rap', '篮球') );
Query OK, 0 rows affected (0.02 sec)mysql> INSERT INTO testSet VALUES ('张三', '唱'); -- 插入单个值
Query OK, 1 row affected (0.00 sec)mysql> INSERT INTO testSet VALUES ('李四', 2); -- 用二进制'10'表示插入第二个值'跳'
Query OK, 1 row affected (0.01 sec)mysql> INSERT INTO testSet VALUES ('王五', 'rap,篮球'); -- 插入多个值
Query OK, 1 row affected (0.00 sec)mysql> INSERT INTO testSet VALUES ('蔡徐坤', '15'); -- 用二进制'1111'表示四个值都要插入
Query OK, 1 row affected (0.00 sec)mysql> SELECT * FROM testSet;
+-----------+--------------------+
| name | hobby |
+-----------+--------------------+
| 张三 | 唱 |
| 李四 | 跳 |
| 王五 | rap,篮球 |
| 蔡徐坤 | 唱,跳,rap,篮球 |
+-----------+--------------------+
4 rows in set (0.00 sec)
判断一个集合是否包含某个值:find_in_set函数。
用C++来描述,大概是如下逻辑:
bool find_in_set(string& str, vector<string>& str_list)
{if(str_list.find(str) != str_list.end())return true;return false;
}
示例,查询喜欢打篮球的人:
mysql> SELECT * FROM testSet WHERE find_in_set('篮球', hobby);
+-----------+--------------------+
| name | hobby |
+-----------+--------------------+
| 王五 | rap,篮球 |
| 蔡徐坤 | 唱,跳,rap,篮球 |
+-----------+--------------------+
2 rows in set (0.00 sec)