MySQL 数据类型
本篇笔记系统梳理了 MySQL(SQL)中常用的数据类型,覆盖数值类型、字符串类型、二进制类型、日期时间类型 四大类,逐类给出存储大小、取值范围、精度说明、0 值与典型选型,并补充了 CHAR 与 VARCHAR、VARCHAR 与 TEXT 的对比与选型建议。适合入门到进阶复习,所有要点均可逐条对照课件。
目录
- [1. 本节目标](#1. 本节目标)
- [2. 常用数据类型分类](#2. 常用数据类型分类)
- [3. 数值类型(数据值类型)](#3. 数值类型(数据值类型))
- [3.1 类型列表](#3.1 类型列表)
- [3.2 数据类型取值范围](#3.2 数据类型取值范围)
- [4. 字符串类型](#4. 字符串类型)
- [4.1 类型列表](#4.1 类型列表)
- [4.2 关于排序](#4.2 关于排序)
- [4.3 CHAR 与 VARCHAR 的区别](#4.3 CHAR 与 VARCHAR 的区别)
- [4.4 如何选择 CHAR 与 VARCHAR](#4.4 如何选择 CHAR 与 VARCHAR)
- [4.5 VARCHAR 与 TEXT 的区别](#4.5 VARCHAR 与 TEXT 的区别)
- [5. 二进制类型](#5. 二进制类型)
- [6. 日期与时间类型](#6. 日期与时间类型)
- [6.1 类型列表](#6.1 类型列表)
- [6.2 其他](#6.2 其他)
- [7. 选型总结](#7. 选型总结)
1. 本节目标
- 掌握数据类型分类:理解 SQL 中数据类型的整体划分(数值、字符串、二进制、日期),以及它们各自对应的应用场景。
- 掌握常用数据类型:熟悉每一种常用类型的存储大小、取值范围与精度,能在建表时为字段选择合适的类型。
2. 常用数据类型分类
在面向对象软件开发的过程中,通常会先进行需求分析 从而得到类 和属性。这套概念可以一一映射到数据库的世界中:
| 面向对象 | 关系数据库 |
|---|---|
| 类(Class) | 实体(Entity,通常以表的形式存在) |
| 属性(Attribute) | 实体中的属性(即表中的字段 / 列) |
| ------ | 每个实体对应一张表 |
| 对象实例 | 表中的一条记录(数据行),即实体的一个实例 |
一条记录包含若干字段(列),每个字段代表实体的一个属性。
如果要定义实体的属性,就需要为属性命名 并指定合适的数据类型。与其他编程语言类似,SQL 规定了用于描述属性的数据类型。常用的数据类型可分为以下几大类:
- 数值类型(数据值类型):整数、浮点数、精确小数。
- 字符串类型:定长 / 变长字符串、文本类型、枚举与集合。
- 二进制类型:用于存储二进制字节(图片、文件等)。
- 日期类型:日期、时间、日期时间、时间戳、年份。
3. 数值类型(数据值类型)
3.1 类型列表
| 类型 | 大小 | 说明 |
|---|---|---|
BIT[(M)] |
默认 1 bit | 位值类型 。M 表示每个值的位数,取值范围 1 ~ 64 。若省略 M,默认为 1。 |
TINYINT[(M)] |
1 byte | 取值范围 -2⁷ ~ 2⁷−1 (即 -128 ~ 127);无符号取值范围 0 ~ 2⁸−1(即 0 ~ 255)。 |
BOOL / BOOLEAN |
1 byte | TINYINT(1) 的同义词。值为 0 被视为假(false) ,非 0 值被视为真(true)。 |
SMALLINT[(M)] |
2 byte | 取值范围 -2¹⁵ ~ 2¹⁵−1 ;无符号取值范围 0 ~ 2¹⁶−1。 |
MEDIUMINT[(M)] |
3 byte | 取值范围 -2²³ ~ 2²³−1 ;无符号取值范围 0 ~ 2²⁴−1。 |
INT[(M)] |
4 byte | 取值范围 -2³¹ ~ 2³¹−1 ;无符号取值范围 0 ~ 2³²−1。 |
INTEGER[(M)] |
4 byte | INT[(M)] 的同义词。 |
BIGINT[(M)] |
8 byte | 取值范围 -2⁶³ ~ 2⁶³−1 ;无符号取值范围 0 ~ 2⁶⁴−1。 |
FLOAT[(M,D)] |
4 byte | 单精度浮点型 。M 是总位数,D 是小数点后面的位数,大约可精确到小数点后 7 位。 |
DOUBLE[(M,D)] |
8 byte | 双精度浮点型 。M 是总位数,D 是小数点后面的位数,大约可精确到小数点后 15 位。 |
DECIMAL[(M[,D])] |
动态 | 不存在精度损失 (以字符串形式保存的精确数值)。M 是总位数,D 是小数点后的位数。最大位数(M)为 65 ,最大小数位数(D)为 30 。若省略 M 默认为 10 ;若省略 D 默认为 0 。M 中不计算小数点和负号 - ;若 D 为 0,则值没有小数点和小数部分。 |
💡 补充说明(关于
INT(M)中的M) :在 MySQL 8.0 之后,整数类型括号里的M(如INT(11))只表示显示宽度 ,不再影响取值范围 ,且对SELECT输出的实际取值没有任何限制作用,仅为历史遗留写法。真正决定存储与取值范围的是类型本身(TINYINT / INT / BIGINT......)以及是否UNSIGNED。
💡 选型经验 :表示状态、布尔值优先TINYINT;普通整数用INT;金额、需要精确计算的小数(如财务)务必使用DECIMAL,不要用 FLOAT / DOUBLE,因为浮点数存在精度损失。
3.2 数据类型取值范围
| 类型 | 大小 | 有符号最小值 | 有符号最大值 | 无符号最小值 | 无符号最大值 |
|---|---|---|---|---|---|
TINYINT |
1 Byte | -128 | 127 | 0 | 255 |
SMALLINT |
2 Byte | -32768 | 32767 | 0 | 65535 |
MEDIUMINT |
3 Byte | -8388608 | 8388607 | 0 | 16777215 |
INT |
4 Byte | -2147483648 | 2147483647 | 0 | 4294967295 |
BIGINT |
8 Byte | -2⁶³ | 2⁶³−1 | 0 | 2⁶⁴−1 |
规律:有符号 类型占用
n字节时,取值范围为 -2^(8n-1) ~ 2^(8n-1)−1 ;加上UNSIGNED后范围为 0 ~ 2^(8n)−1 。可见UNSIGNED把"负数那半边"的空间全部让给了正数,使正数上限翻倍。
4. 字符串类型
4.1 类型列表
字符串(以字符为单位)
| 类型 | 说明 |
|---|---|
CHAR[(M)] |
固定长度 字符串。M 表示长度,以字符为单位 ,取值范围 0 ~ 255 ;M 省略则长度为 1。 |
VARCHAR(M) |
可变长度 字符串。M 表示字符最大长度,取值范围 0 ~ 65535 ;有效字符个数取决于实际字符数 和使用的字符集。 |
TINYTEXT |
小文本类型,最大长度为 255(2⁸−1)个字节,有效字符个数取决于字符集。 |
TEXT[(M)] |
文本类型,最大长度为 65535(2¹⁶−1)个字节,有效字符个数取决于字符集。 |
MEDIUMTEXT |
中文本类型,最大长度为 16,777,215(2²⁴−1)个字节,有效字符个数取决于字符集。 |
LONGTEXT |
大文本类型,最大长度为 4,294,967,295(即 4GB,2³²−1)个字节,有效字符个数取决于字符集。 |
枚举与集合
| 类型 | 说明 |
|---|---|
ENUM('value1','value2',...) |
枚举 。从值列表 'value1','value2' 或 ''(空字符串)以及 NULL 中选一个值 ;最多可有 65,535 个不同元素;单个元素最大长度受 M ≤ 255 或 (M × w) ≤ 1020 限制(M 是元素字符长度,w 是字符集中字符所需的最大字节数);ENUM 的值在内部表示为整数。 |
SET('value1','value2',...) |
集合 。从值列表 'value1','value2' 中选零个或多个值 ;最多 64 个元素;单个元素最大长度同样受 M ≤ 255 或 (M × w) ≤ 1020 限制;SET 值在内部表示为整数。 |
💡
ENUMvsSET:ENUM是"多选一"(如性别:男 / 女 / 未知);SET是"多选多 / 子集"(如标签集合:可同时拥有多个标签)。两者内部都以整数存储,因此占空间小、比较与排序快 ,但取值只能来自预先定义的列表,灵活性较低。
💡CHAR/VARCHARvsTEXT:前者是"短字符串",后者是"长文本"。当TEXT实际长度需求超过阈值时,MySQL 会自动升级为更大的文本类型(详见 4.5)。
4.2 关于排序
- 字符串类型的列以字符为单位 ,并且可以为列单独指定字符集和排序规则 。例如:字符集为
utf8mb4,排序规则为utf8_0900_ai_ci(其中ci表示 case-insensitive,大小写不敏感)。 - 二进制的列以字节为单位 ,可以指定以
_bin结尾的排序规则,例如utf8mb4_bin。此时比较和排序基于数字字符的代码值(逐字节比较) ,因此是大小写敏感的。
4.3 CHAR 与 VARCHAR 的区别
-
CHAR固定长度字符串M表示以字符为单位 的列长度,取值范围 0 ~ 255,省略则长度为 1。- 存储时总是用空格向右填充 到指定长度;获取列的值时会从尾部删除空格。
- 允许定义
CHAR(0),此时列的值只能为NULL或空字符串,主要目的是兼容旧系统------例如某个类中必须有这个属性,但又不使用它的值(值没有实际意义,但列不能不存在)。
-
VARCHAR可变长度字符串M表示以字符为单位 的最大列长度,取值范围 0 ~ 65,535(在所有列中共享)。- 有效长度取决于实际字符数 和使用的字符集。
- 用额外的一或两个字节 记录实际使用的字节数:当实际字节数不超过 255 个字节 时用一个字节记录长度;超过 255 个字节时使用两个字节记录长度。
- 获取列的值时不会从尾部删除空格 ;插入数据时会删除超出长度的尾部空格。
示例:观察尾部空格的差异
sql
mysql> CREATE TABLE vc (v VARCHAR(4), c CHAR(4));
Query OK, 0 rows affected (0.01 sec)
mysql> INSERT INTO vc VALUES ('ab ', 'ab ');
Query OK, 1 row affected (0.00 sec)
mysql> SELECT CONCAT('(', v, ')'), CONCAT('(', c, ')') FROM vc;
+---------------------+---------------------+
| CONCAT('(', v, ')') | CONCAT('(', c, ')') |
+---------------------+---------------------+
| (ab ) | (ab) |
+---------------------+---------------------+
1 row in set (0.06 sec)
mysql> INSERT INTO vc VALUES ('ab ', 'ab ');
Query OK, 1 row affected, 1 warning (0.01 sec)
mysql> SHOW WARNINGS;
+-------+------+----------------------------------------+
| Level | Code | Message |
+-------+------+----------------------------------------+
| Note | 1265 | Data truncated for column 'v' at row 1 |
+-------+------+----------------------------------------+
1 row in set (0.00 sec)
解读:
- 第一行插入
'ab '(ab后两个空格)时,CHAR(4)列c在存储时被空格右填充到 4 位,但查询时尾部空格被删除,所以CONCAT('(', c, ')')输出(ab);而VARCHAR(4)列v会保留 尾部空格,输出(ab )。 - 第二行插入
'ab '(超出VARCHAR(4)的有效长度)时,会触发 Data truncated 警告(Note 1265),超出的空格被截断。
💡 一句话记忆:
CHAR存时补空格、取时去空格;VARCHAR存时按实际长度、取时不去空格。
4.4 如何选择 CHAR 与 VARCHAR
- 如果数据长度基本都一样 ,使用定长
CHAR,例如:身份证号、MD5 值、学号、邮编。 - 如果数据长度有变化 ,使用变长
VARCHAR,例如:名字、地址 ,但要规划好长度,保证最长的字符串能存进去。 - 定长
CHAR比较浪费磁盘空间,但效率高 ------它会直接开辟好对应的存储空间,无需额外记录实际长度。 - 变长
VARCHAR比较节省磁盘空间,但效率略低 ------它在不超过定义长度范围的情况下用多少开辟多少存储空间。
💡 经验补充:定长字段往往更适合做索引与比较(无变长带来的指针与长度计算开销),因此短、等长、高频检索 的字段倾向
CHAR;长、不等长、存储优先 的字段倾向VARCHAR。
4.5 VARCHAR 与 TEXT 的区别
- 容量大小 :
VARCHAR最大支持 65535 个字节 ;TEXT最大支持 65535 个字节 。在指定TEXT长度时,当超过 65535 时自动转换为MEDIUMTEXT,当超过 16,777,215 时自动转换为LONGTEXT。 - 存储位置 :
VARCHAR类型的列,实际内容小于 768 字节时存在当前行 ,大于 768 时存在溢出页 ,当前行保存溢出页的地址;TEXT类型的列整体保存在溢出页,当前行只保存溢出页地址。 - 查询性能 :对于频繁查询的
VARCHAR列可以创建索引 ,提升查询性能;TEXT类型的列无法直接创建普通索引 ,但可以使用FULLTEXT索引。由于索引的支持与存储位置不同,VARCHAR列的性能高于TEXT列。 - 适用场景 :如果存储的数据长度较小且需要创建索引进行检索 ,可选择
VARCHAR,如姓名、用户名、邮箱 等;如果存储的数据长度较大且不需要频繁以该列为条件检索 ,可选择TEXT,如文章内容等。
5. 二进制类型
二进制类型以字节为单位 存储二进制字节序列,常用于存储图片、文件、加密后的密文等二进制数据。它与字符串类型的差别在于:存储的是二进制字节而不是字符 ,不做字符集与大小写排序处理,比较排序基于字节值。
| 类型 | 说明 |
|---|---|
BINARY[(M)] |
固定长度二进制字节 ,与 CHAR 类似,但存储的是二进制字节 而不是字符串。M 表示长度,以字节为单位 ,取值范围 0 ~ 255 ;M 省略则长度为 1。 |
VARBINARY(M) |
可变长度二进制字节 ,与 VARCHAR 类似,但存储的是二进制字节 而不是字符串。M 表示长度,以字节为单位。 |
TINYBLOB |
小二进制字节类型,最大长度为 255(2⁸−1)个字节。 |
BLOB[(M)] |
二进制字节类型,最大长度为 65535(2¹⁶−1)个字节。 |
MEDIUMBLOB |
中二进制字节类型,最大长度为 16,777,215(2²⁴−1)个字节。 |
LONGBLOB |
大二进制字节类型,最大长度为 4,294,967,295(即 4GB,2³²−1)个字节。 |
💡
BLOB系列与TEXT系列一一对应 :TINYBLOB↔TINYTEXT、BLOB↔TEXT、MEDIUMBLOB↔MEDIUMTEXT、LONGBLOB↔LONGTEXT,最大长度完全相同;唯一的区别是 BLOB 存二进制字节、TEXT 存字符。
6. 日期与时间类型
6.1 类型列表
| 类型 | 大小 | 说明 | 0 值 |
|---|---|---|---|
TIMESTAMP[(fsp)] |
4 bytes | 时间戳类型 。支持范围 1970-01-01 00:00:01.000000 ~ 2038-01-19 03:14:07.499999 。0 值为 0000-00-00 00:00:00。 |
0000-00-00 00:00:00 |
DATETIME[(fsp)] |
8 bytes | 日期类型和时间类型的组合 。支持范围 1000-01-01 00:00:00.000000 ~ 9999-12-31 23:59:59.499999 。显示格式为 YYYY-MM-DD hh:mm:ss[.fraction]。0 值为 0000-00-00 00:00:00。 |
0000-00-00 00:00:00 |
DATE |
3 bytes | 日期类型 。支持范围 1000-01-01 ~ 9999-12-31 。显示格式为 YYYY-MM-DD。0 值为 0000-00-00。 |
0000-00-00 |
TIME[(fsp)] |
3 bytes | 时间类型 。支持范围 -838:59:59.000000 ~ 838:59:59.000000 。显示格式为 hh:mm:ss[.fraction]。0 值为 00:00:00。 |
00:00:00 |
YEAR[(4)] |
1 byte | 4 位格式的年份 。支持范围 1901 ~ 2155 。显示格式为 YYYY。0 值为 0。 |
0 |
⚠️ 勘误提示 :课件中
TIMESTAMP上限曾误写为 "20388-01-19",正确应为 2038-01-19 03:14:07 ;LONGBLOB的 4GB 对应的是 2³²−1(课件中误写为 2⁸−1),此处已更正。
💡TIMESTAMPvsDATETIME(高频考点):
TIMESTAMP只占 4 字节 、范围窄(受 2038 年问题 限制),并且会与时区绑定:写入时按当前时区转成 UTC 存储、读出时再转回会话时区。DATETIME占 8 字节 、范围宽(1000 ~ 9999 年),与时区无关,存什么读什么。- 选型:需要跨时区、自动记录"创建/修改时间"用
TIMESTAMP;需要大范围、稳定的字面日期时间用DATETIME。
6.2 其他
fsp为可选设置 ,用来指定小数秒精度 ,范围 0 ~ 6;值为 0 表示没有小数部分;若省略,默认精度为 0。CURRENT_DATE和CURRENT_DATE()是CURDATE()的同义词,用于获取当前日期。CURRENT_TIME和CURRENT_TIME([fsp])是CURTIME()的同义词,用于获取当前时间。CURRENT_TIMESTAMP和CURRENT_TIMESTAMP([fsp])是NOW()的同义词,用于获取当前日期和时间。
sql
-- 一个常用的小例子:建表时自动维护创建/更新时间
CREATE TABLE article (
id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
title VARCHAR(120) NOT NULL, -- 标题:变长字符串
content LONGTEXT, -- 正文:长文本,用 LONGTEXT
author CHAR(18) , -- 身份证:定长,用 CHAR
status ENUM('draft','published','archived') DEFAULT 'draft', -- 状态:多选一
tags SET('java','mysql','python'), -- 标签:多选多
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, -- 创建时间
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP -- 更新时间
);
7. 选型总结
| 要存什么 | 推荐类型 | 理由 |
|---|---|---|
| 状态 / 布尔 | TINYINT / BOOL |
仅 1 字节,0/非 0 表真假 |
| 普通整数 | INT |
4 字节,范围足够 |
| 超大整数(如雪花 ID) | BIGINT |
8 字节,范围最大 |
| 金额 / 精确小数 | DECIMAL |
无精度损失,避免浮点误差 |
| 等长短字符串(身份证、MD5、邮编) | CHAR |
定长,效率更高 |
| 不定长短字符串(姓名、地址、邮箱) | VARCHAR |
变长,节省空间 |
| 大段文本(文章内容) | TEXT / LONGTEXT |
容量大,存于溢出页 |
| 二进制(图片 / 文件) | BLOB / LONGBLOB |
以字节存二进制内容 |
| 多选一状态 | ENUM |
内部整数,省空间、快 |
| 多选多标签 | SET |
内部整数,可取子集 |
| 跨时区 / 自动时间戳 | TIMESTAMP |
4 字节,绑定时区 |
| 大范围稳定日期时间 | DATETIME |
范围宽,与时区无关 |
核心心法 :选型时同时权衡三件事------取值范围够不够 、是否要建索引检索 、是存字符还是二进制 。在满足范围的前提下,用空间更小的类型、能定长则定长、能建索引则建索引,就能兼顾准确与性能。
至此,MySQL 四大类常用数据类型(数值、字符串、二进制、日期时间 )已全部梳理完毕。建议结合建表语句反复对照每种类型的大小、范围、0 值与适用场景,做到"看到一个字段就能条件反射地选出最合适的类型"。