MySQL 系列:第3篇 数据类型全解
2026/6/16 10:19:03 网站建设 项目流程

IT策士 10余年一线大厂经验,专注 IT 思维、架构、职场进阶。我会在各个平台持续发布最新文章,助你少走弯路。


建好了库和表,接下来最让人纠结的就是:这一列该用什么数据类型?选错了轻则浪费空间、重则查询缓慢甚至数据丢失。今天我们就来一场 MySQL 数据类型的“选品大会”,配合 Python 实打实地看看每种类型怎么用、什么时候用、有什么坑。

1. 数值类型:不只是 INT 和 DECIMAL

MySQL 的数值类型分为整数、浮点数、定点数。选型核心在于范围够不够精度准不准空间省不省

1.1 整数类型

选型原则:能用小的就别用大的。状态标识(0/1)用 TINYINT,年龄用 TINYINT UNSIGNED,主键量级上亿才考虑 BIGINT。

用 Python 建表验证一下整数范围:

importmysql.connector conn=mysql.connector.connect(host="127.0.0.1",port=3306,user="root",password="MyNewPass123!",database="shop")cursor=conn.cursor()# 建一张测试表cursor.execute(""" CREATE TABLE IF NOT EXISTS int_test(idINT AUTO_INCREMENT PRIMARY KEY, age TINYINT UNSIGNED, status TINYINT DEFAULT0, big_id BIGINT)ENGINE=InnoDB""")print("✅ 整数测试表创建成功")# 插入正常数据cursor.execute("INSERT INTO int_test (age, status, big_id) VALUES (25, 1, 9000000000)")conn.commit()print("✅ 插入 25 岁、状态1、big_id=90亿 成功")# 尝试插入超出 TINYINT UNSIGNED 范围的年龄(255以上)try: cursor.execute("INSERT INTO int_test (age) VALUES (256)")conn.commit()except mysql.connector.DataError as e: print(f"❌ 年龄 256 插入失败: {e}")

预期输出

✅ 整数测试表创建成功 ✅ 插入25岁、状态1、big_id=90亿 成功 ❌ 年龄256插入失败:1264(22003): Out of range valueforcolumn'age'at row1

1.2 浮点数与定点数

  • FLOAT/DOUBLE:近似值,存在精度丢失,绝不用于金额。

  • DECIMAL(M,D):精确值,M 为总位数,D 为小数位数。如DECIMAL(10,2)表示最大99999999.99

cursor.execute(""" CREATE TABLE IF NOT EXISTS money_test(price_float FLOAT, price_double DOUBLE, price_decimal DECIMAL(10,2))""")cursor.execute("INSERT INTO money_test VALUES (0.1+0.2, 0.1+0.2, 0.1+0.2)")cursor.execute("SELECT * FROM money_test")row=cursor.fetchone()print(f"FLOAT: {row[0]}")print(f"DOUBLE: {row[1]}")print(f"DECIMAL:{row[2]}")

预期输出(可能略有差异):

FLOAT:0.30000001192092896DOUBLE:0.30000000000000004DECIMAL:0.30

结论:涉及金钱、库存扣减一律用 DECIMAL,永不踩坑。

2. 字符串类型:VARCHAR 与 CHAR 的真相

2.1 VARCHAR(n):可变长

  • 存储实际字符 + 1~2 字节长度前缀。

  • 最大 65535 字节,但实际受行大小和字符集影响。

  • VARCHAR(255)曾为经典,但现在 8.0 更推荐按需定义,避免临时表被撑大。

2.2 CHAR(n):定长

  • 始终占用 n 个字符的空间,末尾空格会被截断。

  • 适合存储固定长度的值:手机号、MD5 哈希、身份证号(18位)。

  • 读写效率稍高,因为长度固定,无需计算变长头部。

2.3 字符集占用字节的影响

VARCHAR(10)在 utf8mb4 下最大可能占用 40 字节(一个字符最多 4 字节)。定义时n指的是字符数,不是字节数。

Python 演示插入与长度截断:

cursor.execute(""" CREATE TABLE IF NOT EXISTS str_test(name VARCHAR(5), code CHAR(5))""")# CHAR 自动补空格cursor.execute("INSERT INTO str_test VALUES ('Tom', 'A')")cursor.execute("SELECT name, code, CHAR_LENGTH(code), LENGTH(code) FROM str_test")row=cursor.fetchone()print(f"name='{row[0]}', code='{row[1]}', 字符数={row[2]}, 字节数={row[3]}")# VARCHAR 超长报错try: cursor.execute("INSERT INTO str_test (name) VALUES ('TooLongName')")except mysql.connector.DataError as e: print(f"❌ VARCHAR 超长: {e}")

预期输出

name='Tom',code='A ', 字符数=5, 字节数=5❌ VARCHAR 超长:1406(22001): Data too longforcolumn'name'at row1

常见误区
VARCHAR(10)VARCHAR(1000)在存储短字符串时空间开销几乎一样(只有额外长度前缀差异),但后者可能导致排序、临时表使用更多的内存。切勿为了“保险”随意加大长度,要根据业务合理定义。

2.4 TEXT 系列

用于存储大段文本(文章正文、JSON 字符串):

  • TINYTEXT (255B)、TEXT (64KB)、MEDIUMTEXT (16MB)、LONGTEXT (4GB)

  • 缺点:不能有默认值、全量读取影响性能、通常单独存或使用外部对象存储。

3. 日期时间类型:TIMESTAMP 还是 DATETIME?

推荐:绝大多数场景用DATETIME,因为它范围大且与业务时区解耦;只有需要自动时区转换的场景(如全球化系统记录用户操作)才用 TIMESTAMP。

Python 验证时区转换:

cursor.execute("SET time_zone = '+00:00'")# 设为 UTCcursor.execute(""" CREATE TABLE IF NOT EXISTS dt_test(ts TIMESTAMP DEFAULT CURRENT_TIMESTAMP, dt DATETIME DEFAULT CURRENT_TIMESTAMP)""")cursor.execute("INSERT INTO dt_test VALUES ()")cursor.execute("SELECT * FROM dt_test")row=cursor.fetchone()print(f"UTC 下: TIMESTAMP={row[0]}, DATETIME={row[1]}")# 切回东八区再查cursor.execute("SET time_zone = '+08:00'")cursor.execute("SELECT * FROM dt_test")row=cursor.fetchone()print(f"+08:00: TIMESTAMP={row[0]}, DATETIME={row[1]}")

预期输出

UTC 下:TIMESTAMP=2025-07-21 08:30:00,DATETIME=2025-07-21 08:30:00 +08:00:TIMESTAMP=2025-07-2116:30:00,DATETIME=2025-07-21 08:30:00

可见 TIMESTAMP 会随着时区自动偏移,而 DATETIME 始终不变。

4. 半结构化类型:ENUM、SET 与 JSON

4.1 ENUM:单选枚举

内部存储为整数(1、2、3…),可读性好但扩展麻烦。

gender ENUM('male','female','other')

优点:存储紧凑,查询时可写字符串。
缺点:新增枚举值需 ALTER TABLE,且顺序调整危险。适合不变的值(如星期)。

4.2 SET:多选集合

hobbies SET('reading','sports','music')

内部用位存储,最多 64 个选项。查询可用FIND_IN_SET

4.3 JSON:灵活的文档存储

MySQL 8.0 对 JSON 的支持已经相当成熟:能创建虚拟列并加索引、丰富的 JSON 函数。

cursor.execute(""" CREATE TABLE IF NOT EXISTS user_profile(idINT PRIMARY KEY AUTO_INCREMENT, profile JSON)""")# 插入 JSONcursor.execute(""" INSERT INTO user_profile(profile)VALUES('{"name":"Alice","tags":["tech","ai"],"age":28}')""")conn.commit()# 查询 JSON 字段cursor.execute("SELECT profile->>'$.name' AS name FROM user_profile")print("姓名:", cursor.fetchone()[0])# 用 JSON_CONTAINS 检查数组cursor.execute("SELECT * FROM user_profile WHERE JSON_CONTAINS(profile->'$.tags', '\"ai\"')")print("包含 ai 标签的用户:", cursor.fetchone())

预期输出

姓名: Alice 包含 ai 标签的用户:(1,'{"name": "Alice", "tags": ["tech", "ai"], "age": 28}')

最佳实践
JSON 列不适合频繁按内部字段查询的场景,性能远不如普通列加索引。一般用于存储动态扩展的元数据,核心业务字段仍应独立建列。

5. 其他常用类型速览

  • BINARY / VARBINARY:二进制字符串,存哈希、UUID 字节等。

  • BLOB:存图片、文件,但通常只存路径,文件本身放对象存储。

  • BOOLEAN:TINYINT(1) 的别名,0 为 false,非 0 为 true。

6. 动手试试:设计一张完整的员工表

现在请你为一家公司的 HR 系统设计employees表,并录入几条数据。要求:

  1. 自增主键 id,使用 INT。

  2. 姓名 name (VARCHAR(50),不为空),工号 code (CHAR(6),唯一),性别 gender (ENUM(‘男’,‘女’))。

  3. 薪资 salary 使用 DECIMAL(10,2),保证精度。

  4. 入职日期 hire_date 使用 DATE。

  5. 扩展信息 extra 使用 JSON,可存紧急联系人、证书等动态字段。

  6. 创建时间 created_at 使用 DATETIME DEFAULT CURRENT_TIMESTAMP。

用 Python 建表并插入一条你自己的模拟数据,最后用SELECT打印整行。

参考代码

conn=mysql.connector.connect(host="127.0.0.1",port=3306,user="root",password="MyNewPass123!",database="shop")cursor=conn.cursor()cursor.execute(""" CREATE TABLE IF NOT EXISTS employees(idINT AUTO_INCREMENT PRIMARY KEY, name VARCHAR(50)NOT NULL, code CHAR(6)NOT NULL UNIQUE, gender ENUM('男','女'), salary DECIMAL(10,2), hire_date DATE, extra JSON, created_at DATETIME DEFAULT CURRENT_TIMESTAMP)ENGINE=InnoDB""")print("✅ 表 employees 创建成功")cursor.execute(""" INSERT INTO employees(name, code, gender, salary, hire_date, extra)VALUES('张三','EMP001','男',15000.00,'2025-07-01','{"contact":"13900001111","cert":["PMP"]}')""")conn.commit()cursor.execute("SELECT * FROM employees")forcolincursor.description: print(col[0],end="\t")print()forrowincursor.fetchall(): print(row)cursor.close()conn.close()

预期输出

✅ 表 employees 创建成功idname code gender salary hire_date extra created_at1张三 EMP001 男15000.002025-07-01{"contact":"13900001111","cert":["PMP"]}2025-07-2116:45:00

7. 总结

数据类型的选择直接影响数据的正确性、存储效率和查询性能。记住三条铁律:

  • 金额永远用 DECIMAL

  • 定长文本用 CHAR,变长用 VARCHAR,大文本用 TEXT

  • 日期不涉及时区用 DATETIME,否则用 TIMESTAMP(注意 2038 问题)

每当我们设计新表时,花 5 分钟仔细斟酌每列的类型,往往能避免未来数倍的迁移痛苦。

下篇我们将进入DML 实战,用 Python 实现高效的 INSERT/UPDATE/DELETE,同时揭秘批量插入的性能秘籍。下次见!

想了解更多还可以去各个平台搜索「IT策士」,一起升级 IT 思维 !

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询